I don't know what I was thinking when I wrote that. The code I just
provided can't actually cycle. We can go from updateView to updateData to
updateViewForData, but then we're done.

Mark

On Mon, Oct 24, 2016 at 12:36 PM, Mark Hamburg <[email protected]>
wrote:

> So, your "data model" is a list of users but your "view model" is a
> boolean state for each user? I'm working on thinking about how that would
> come up but I guess the checkboxes could be for selection and the selection
> could be view specific.
>
> Anyway, here is my quick effort at breaking this out. I'm going to assume
> that we can represent users with strings. If they need more data, then
> there is presumably a way to convert a user into a user-id for internal use
> and a string for display. So, our data model is:
>
> type alias User = String
>
> type alias DataModel = List User
>
>
> -- In model:
>
> dataModel : DataModel
>
>
> The view model consists of the information for each checkbox which
> assuming it's a checkbox really is just a boolean state. We maintain this
> as a dictionary indexed by user:
>
> type alias ViewModel = Dict User Bool
>
>
> -- In model:
>
> viewModel : ViewModel
>
> The view function can now map over the data model (list of users) and
> extract the extra view model information by consulting the dictionary.
>
> Changes to the checkbox state, just write values back into the view model
> dictionary.
>
> Changes to the user list need to update the data model but then they also
> need to update the view model to remove any users no longer on the list and
> add entries for any users newly on the list. (Alternatively, one could use
> the initial state for users as a default and use Maybe.withDefault to
> handle new users. Then one just needs to remove the view model entries for
> users no longer in the data model.)
>
> The key thing in structuring this is that you can build all of the logic
> that works on the data model independent of the checkbox states needed by
> the view model. For example, if these checkboxes represent selected users,
> you could have the changes to the data model be driven by a set of users.
> Events would flow into the view model collecting up this selected set and
> then turn into a message or operation for the data model. That change to
> the data model would then result in an update to the view model.
>
> Potential type signatures could be:
>
> view : DataModel -> ViewModel -> Html ViewMsg
>
> updateView : ViewMsg -> ViewModel -> (ViewModel, Cmd ViewMsg, Maybe
> DataMsg)
>
> updateData: DataMsg -> DataModel -> (DataModel, Cmd DataMsg)
>
> updateViewForData: DataModel -> ViewModel -> (ViewModel, Cmd ViewMsg)
>
> Then we get something like the following at the model level:
>
> type alias Model = { dataModel : DataModel, viewModel : ViewModel }
>
> type Msg = ToData DataMsg | ToView ViewMsg
>
> update : Msg -> Model -> (Model, Cmd Msg)
> update msg model =
>
> case msg of
>
> ToData dataMsg ->
>
> let
>
> (newData, dataCmd ) = updateData dataMsg model.dataModel
>
> (newView, viewCmd ) = updateViewForData newData model.viewModel
>
> in
>
> ( { model | dataModel = newData, viewModel = newView }
>
> , Cmd.batch [ Cmd.map ToData dataCmd, Cmd.map ToView viewCmd ]
>
> )
>
> ToView viewMsg ->
>
> updateView viewMsg model.viewModel
>
> |> OutMessage.mapComponent (\newView -> { model | viewModel = newView })
>
> |> OutMessage.mapCmd ToView
>
> |> OutMessage.evaluateMaybe (\dataMsg m -> update (ToData dataMsg) m)
>
>
> (You can find OutMessage here: http://package.elm-lang.
> org/packages/folkertdev/outmessage/1.0.2/OutMessage)
>
> This does have the potential for pinging back and forth between data model
> updates and view updates but that's a hazard in any two component system.
> The key thing is that the model can be built and in operation can evolve
> independent of the view or views. The potentially more ugly problem comes
> if it is difficult to factor the response to a view message into logic that
> can just update the current view model and then generic logic that can
> respond to a data model change.
>
> Mark
>
> On Mon, Oct 24, 2016 at 4:29 AM, Oliver Searle-Barnes <[email protected]>
> wrote:
>
>> @Mark this is a pattern I've been exploring recently. An area that I
>> haven't found a solution to is keeping the view model in sync with the
>> shared models. For instance, let's say I have a view with a list of
>> checkboxes next to each user. The state for the checkboxes would be kept in
>> the view model, but then I need to manage adding/removing checkboxes as the
>> list of users changes. Currently I handle this by having a default state
>> for the checkbox which is used until a user actually clicks on a checkbox
>> at which point the state is added to the view model. This works but is
>> semantically awkward and doesn't take care of removing the checkbox state
>> if a user is removed from the shared list. https://github.com/elm-lang/ht
>> ml/issues/19 provides a potential avenue for cleanup, but even then the
>> lifecycle seems a little awkward.
>>
>> I wonder if you have a different approach for this problem?
>>
>>
>> On Sunday, 23 October 2016 21:29:23 UTC+2, Mark Hamburg wrote:
>>>
>>> We're just getting into having an app where this is likely to matter,
>>> but if you have multiple views onto the same data, then the pattern that
>>> would seem to apply is to have a single data model and multiple view
>>> models. The data model handles the shared part. The view models handle
>>> things specific to the particular view — e.g., is a spin down open, what's
>>> the scroll position, etc. The view functions would take the data model and
>>> their particular view model as parameters and would generate appropriately
>>> tagged messages back. This avoids significant nesting while still providing
>>> some structure to keep pieces that can be separated separate.
>>>
>>> There are then a couple ways to structure the multiple view models. One
>>> is to have a view selector but keep all of the view models alive all the
>>> time. The benefit of this is that when you switch back to a view, it will
>>> remember where you were. The downside is that one is carrying around — and
>>> potentially updating if the view model depends on the data model — an
>>> increasing number of view models if you have an increasing number of views.
>>> The alternative is to put the view models into a union type and have one
>>> model entry that serves as both view selector and view model storage. This
>>> keeps things lighter weight but means that you have nowhere to maintain
>>> context when switching back and forth.
>>>
>>> Above all of this, the other key subdivision we are making in our SPA
>>> and that I would recommend having seen where other programmers often first
>>> head, is having a top layer that handles overall app state — e.g., logged
>>> in v not logged in — as a type union. This can also often be where the
>>> routing logic plugs in. People who haven't spent a lot of time thinking in
>>> terms of type unions tend to load their model up with a lot of fields that
>>> only have meaning in certain states. This then leads to more invariants
>>> about the state that can't be enforced by the compiler. Breaking things up
>>> into different type cases for different states allows one to make more bad
>>> states simply impossible. For example, our logging in state has a
>>> RemoteData.WebData User reflecting whether we've gotten valid user data
>>> back from the server but our logged in state simply has a User. The top
>>> level event dispatcher works with the following type signature on the log
>>> in state update:
>>>
>>> update : Login.Msg -> Login.Model -> ( Login.Model, Cmd Login.Msg, Maybe
>>> User )
>>>
>>>
>>> After each call to update, it can check to see whether we now have a
>>> valid user and if so switch to the logged in state, initializing it with
>>> the supplied user.
>>>
>>> This isn't as flat as some people go, but it doesn't spread things out
>>> into lots of components and the top layer(s) are pretty simple.
>>>
>>> Mark
>>>
>>>
>>> On Sun, Oct 23, 2016 at 11:39 AM, Francois <[email protected]>
>>> wrote:
>>>
>>>> Hi,
>>>>
>>>> I'm really new to Elm. I'm not good enough to propose a guideline about
>>>> Elm app structure but as a novice a guideline can help lots of people.
>>>>
>>>> I worked / works on a large angular application and John Papa
>>>> Styleguide has been really helpful at the beginning.
>>>> So writing a community guideline can be a great resource. (there is
>>>> another thread inter component that seems related)
>>>>
>>>> For example, on the server side, structuring domain core by features is
>>>> good thing (for me). Help define some boundaries and help maintability.
>>>> Grouping all Java classes by technical aspect at the project root : all
>>>> services in one package, all models in one package can be tedious.
>>>>
>>>> On the front, if I correctly understood this thread :
>>>> - a unique Msg / Model inside Main.elm can be a good starting point and
>>>> easy to refactor later.
>>>> - separating the view per feature (only view functions) : advice taken
>>>> from Dave
>>>> - break Model / Msg when too big (definition of "too big" is perhaps
>>>> not easy, or someone can give some advices to detect "a too big Msg"). At
>>>> this point, break Msg / Model / Update by feature. It is not all or
>>>> nothing, just extract at first one feature and put them inside a file. Then
>>>> the Main module wraps the feature (using map inside the Update).
>>>>
>>>> Can we imagine starting a community guideline somewhere ? adding
>>>> examples later.
>>>> Really easy for me to say that.
>>>>
>>>>
>>>> --
>>>> You received this message because you are subscribed to the Google
>>>> Groups "Elm Discuss" group.
>>>> To unsubscribe from this group and stop receiving emails from it, send
>>>> an email to [email protected].
>>>> For more options, visit https://groups.google.com/d/optout.
>>>>
>>>
>>> --
>> You received this message because you are subscribed to the Google Groups
>> "Elm Discuss" group.
>> To unsubscribe from this group and stop receiving emails from it, send an
>> email to [email protected].
>> For more options, visit https://groups.google.com/d/optout.
>>
>
>

-- 
You received this message because you are subscribed to the Google Groups "Elm 
Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to