Update 2016-05-14
I'm sorry that I may have failed to explain my problem clearly because of my bad English. This time I've abstracted the question and demonstrated it in a counter example.
There are two sub-modules(CounterPair
and CounterTriplet
), and each of them uses Counter
to display num
filed of SharedModel
and displays something else (other Counter
s in this example). I highlighted the Counter
displaying SharedModel
's num
with red color.
The red Counter
of CounterPair
and CounterTriple
should synchronized because they're representing same SharedModel
. Also, shared
field of Model
also need to be updated when any of the red Counter
s updates. The code shown below have only managed to display the things as I want but the update chain is missing. How should I implement the update chain ?
You can try this code on Try Elm
.
import Html exposing (div, button, text)
import Html.App as App exposing (beginnerProgram)
import Html.Events exposing (onClick)
import Html.Attributes exposing (style)
main =
beginnerProgram { model = init, view = view, update = update }
-------------------------------------
type alias SharedModel =
{ num : Int
, foo : String
}
sharedInit =
SharedModel 0 "Foo"
--------------------------------------
type alias Model =
{ shared : SharedModel
, pair : CounterPair
, triplet : CounterTriplet
}
init =
let
shared = sharedInit
in
Model shared (pairInit shared) (tripletInit shared)
view model =
div []
[ App.map Pair (pairView model.pair)
, App.map Triplet (tripletView model.triplet)
]
type Msg
= Pair PairMsg
| Triplet TripletMsg
update msg model =
case msg of
Pair sub ->
let
pair = pairUpdate sub model.pair
in
{ model | pair = pair }
Triplet sub ->
let
triplet = tripletUpdate sub model.triplet
in
{ model | triplet = triplet }
----------------------------------------
type alias CounterTriplet =
{ localFst : CounterModel
, localSnd : CounterModel
, global : CounterModel
}
tripletInit shared =
CounterTriplet (counterInit 0) (counterInit 0) (counterInit shared.num)
tripletView model =
div [ style [("background-color","lightgray"), ("margin-bottom", "1rem")] ]
[ App.map TriLocalSnd (counterView "green" model.localFst)
, App.map TriLocalSnd (counterView "green" model.localSnd)
, App.map TriGlobal (counterView "red" model.global)
]
type TripletMsg
= TriLocalFst CounterMsg
| TriLocalSnd CounterMsg
| TriGlobal CounterMsg
tripletUpdate msg model =
case msg of
TriLocalFst sub ->
let
localFst = counterUpdate sub model.localFst
in
{ model | localFst = localFst }
TriLocalSnd sub ->
let
localSnd = counterUpdate sub model.localSnd
in
{ model | localSnd = localSnd }
TriGlobal sub ->
let
global = counterUpdate sub model.global
in
{ model | global = global }
----------------------------------------------
type alias CounterPair =
{ local : CounterModel
, global : CounterModel
}
pairInit shared =
CounterPair (counterInit 0) (counterInit shared.num)
pairView model =
div [ style [("background-color","lightgray"), ("margin-bottom", "1rem")] ]
[ App.map PairLocal (counterView "green" model.local)
, App.map PairGlobal (counterView "red" model.global)
]
type PairMsg
= PairLocal CounterMsg
| PairGlobal CounterMsg
pairUpdate msg model =
case msg of
PairLocal sub ->
let
local = counterUpdate sub model.local
in
{ model | local = local }
PairGlobal sub ->
let
global = counterUpdate sub model.global
in
{ model | global = global }
---------------------------------------
type alias CounterModel =
{ num : Int
, btnClicks : Int
}
counterInit num =
CounterModel num 0
counterView color model =
div [ style [("display","inline-block"), ("margin-right", "1rem")] ]
[ button [ onClick Decrement ] [ text "-" ]
, div [ style [("color", color)]] [ text (toString model.num) ]
, button [ onClick Increment ] [ text "+" ]
, div [ ] [ text ("btn click: " ++ (toString model.btnClicks)) ]
]
type CounterMsg = Increment | Decrement
counterUpdate msg model =
case msg of
Increment ->
{ model | num = model.num + 1, btnClicks = model.btnClicks + 1 }
Decrement ->
{ model | num = model.num - 1, btnClicks = model.btnClicks + 1 }
Original Question
I'm using The Elm Architecture
and the view hierarchy is like
,--------------------------------------------------------------------.
| View |
| ,------------------. ,------------------------------------------. |
| | SubView1 | | SubView2 | |
| | ,--------------. | | ,---------------. ,--------------------. | |
| | | Label(title) | | | | Label(title) | | Label(description) | | |
| | `--------------' | | `---------------' `--------------------' | |
| `------------------' `------------------------------------------' |
`--------------------------------------------------------------------'
Label
is a customized <label>
which can turn into <input>
field when double clicked. So it is editable.
The Model
s is like below. The problem is I want the content
of ViewModel
is updated when the sub-sub label's value is edited. When I update the title label of subview1, that of subview2 should also be updated because they should "share" same Content
But the value
of LabelModel
is somehow stand-alone so I need to transfer the change of value
all way up to ViewModel
. I don't think this is a practical way of implementing what I want, because the view hierarchy may become more complicated. Besides, almost all Model
s have something specific (Foo
, Bar
, BarBar
, etc. used to store states), so they need to be initialized. So Making ViewModel
neat, for example type alias ViewModel = Content
, is impractical, because I don't know where to get Bar
and BarBar
for SubViewModel1
and SubViewModel2
type alias ViewModel =
{ content : Content
, subView1 : SubViewModel1
, subView2 : SubViewModel2
}
type alias SubViewModel1 =
{ content : Content
, label : Label
, bar : Bar
}
type alias SubViewModel2 =
{ content : Content
, titleLabel : Label
, descLabel : Label
, barbar : BarBar
}
type alias Content =
{ title: String
, description : String
}
type alias LabelModle =
{ value : String
, state : Foo
}
I'm very new to Elm so this question may seems stupid, sorry for that.
Thanks for reading (and answering :) )