如何在榆树中的不同模型之间共享一些值

时间:2016-05-13 03:13:26

标签: functional-programming frontend elm

更新5-14-2016

很抱歉,由于英语不好,我可能无法清楚地解释我的问题。这次我抽象了这个问题并在一个反例中进行了演示。

有两个子模块(CounterPairCounterTriplet),每个子模块都使用Counter来显示num的{​​{1}}个字段并显示某些内容else(本例中的其他SharedModel)。我突出显示Counter显示Counter的{​​{1}}红色。

SharedModelnum的红色Counter应该同步,因为它们代表相同的CounterPair。此外,CounterTriple的{​​{1}}字段也需要在任何红色SharedModel更新时进行更新。下面显示的代码只是设法显示我想要的东西,但缺少更新链。我该如何实施更新链?

您可以在Try Elm上试用此代码。

shared

原始问题

我正在使用Model,视图层次结构就像

Counter

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 } 是自定义The Elm Architecture,双击时可以变为,--------------------------------------------------------------------. | View | | ,------------------. ,------------------------------------------. | | | SubView1 | | SubView2 | | | | ,--------------. | | ,---------------. ,--------------------. | | | | | Label(title) | | | | Label(title) | | Label(description) | | | | | `--------------' | | `---------------' `--------------------' | | | `------------------' `------------------------------------------' | `--------------------------------------------------------------------' 字段。所以它是可编辑的。

Label如下所示。问题是我想在编辑子子标签的值时更新<label>的{​​{1}}。当我更新subview1的标题标签时,也应该更新subview2的标题标签,因为它们应该“共享”相同的<input>

Model的{​​{1}}在某种程度上是独立的,因此我需要将content的更改一直转移到ViewModel。我不认为这是实现我想要的实用方法,因为视图层次结构可能变得更加复杂。此外,几乎所有Content都有特定的内容(valueLabelModelvalue等用于存储状态),因此需要对其进行初始化。因此,使ViewModel整洁,例如Model是不切实际的,因为我不知道FooBarBarBar的归档位置ViewModeltype alias ViewModel = Content }}

Bar

我对榆树很新,所以这个问题可能看起来很愚蠢,对不起。

感谢您阅读(并回答:))

1 个答案:

答案 0 :(得分:0)

当单个数据仅在一个位置表示时,Elm组件层次结构最有效。如果需要多个组件中的数据,则将其放在这些组件的最近共同祖先处。

在具体案例中,如果SubViewModel1.label.valueSubViewModel2.titleLabel.value实际上是同一条数据,那么它应该位于ViewModel

一旦数据存在于该位置,您就可以通过update组件的ViewModel函数与Msg SubViewModel1...2的{​​{1}}作出反应来对其中的更新作出反应,或者module View import SubView1 import SubView2 type alias ViewModel = { content : Content , subView1 : SubView1.Model , subView2 : SubView2.Model , label : String -- New! } type Msg = SubView1Msg SubView1.Msg | SubView2Msg SubView2.Msg | ... update action model = case action of ... (SubView1.UpdateLabel str) as action' -> let (subView1', fx) = SubView1.update action' model.subView1 in ( { model | subView1 = subView1' , label = str } , Cmd.map SubView1Action fx ) 。像这样:

datarow