> Either you end up with 50 messages in the model update or you end up with
50 files for each record that can be updated.

There is no need to split or combine messages because of update function.
The other solution is to create a `mapX` function for each nested structure
and use it for each update:

-- /Feature/Announcements/AnnouncementItemEdit.md

    BodyEdit body ->
        mapAnnouncementItem model -- this is top level model
            (\item -> { item | body = body })
            ! []

    DurationEdit duration ->
        mapAnnouncementItem model -- this is top level model
            (\item -> { item | duration = duration })
            ! []

-- /Model.elm

type Model
    = Blank
    | AnnouncementListPage (WebData (List Announcement))
    | AnnouncementItemPage (WebData AnnouncementForm)

The top `Msg` has just one tag for entire announcement item edit form, the
view/update functions always return top level models and messages.

Regards,
Witold Szczerba


On Mon, Mar 6, 2017 at 12:41 AM, Martin Bailey <[email protected]> wrote:

> Hi Francesco, thanks for sharing the example.
>
> I agree that's a good structure for different sections of an app. I
> oversimplified the first example to show the current necessity of updater
> functions. The more painful part is deeply nested records such as in my
> last Geocode example. I feel it would be cumbersome creating layers of
> messages and OOP-style updaters to mimic the data structure. Either you end
> up with 50 messages in the model update or you end up with 50 files for
> each record that can be updated.
>
> I'd love to see examples of how to achieve the following without lots of
> boilerplate.
>
>     RcvGeocode geocode ->
>         ( { model | user.profile.contact.address = extractAddress geocode
> }, Cmd.none )
>
> On Sunday, March 5, 2017 at 4:09:08 PM UTC-5, Francesco Orsenigo wrote:
>>
>>
>> Hi Martin, I wonder if for your case a different way to organise your
>> code would be viable:
>>
>> -- Family.elm
>> updateFamily msg family =
>>   case msg of
>>     ChangeName newName -> ( { family | name = newName }, Cmd.none )
>>     Save -> ( model, saveFamily family )
>>
>>
>> selectedView family =
>>     UI.form
>>         [ UI.class "fluid"
>>         , UI.onSubmit <| SaveFamily model.family
>>         ]
>>         [ UI.input
>>             [ UI.label "Name"
>>             , UI.value model.family.name
>>             , UI.onInput ChangeName
>>             ]
>>         ]
>>
>>
>> -- Update.elm
>> update msg model =
>>   case msg of
>>     FamilyMsg familyMsg ->
>>       let
>>         (newFamily, familyCmd) = Family.update familyMsg model.family
>>       in
>>         ( { model | family = newFamily }, Cmd.map FamilyMsg familyCmd )
>>
>>
>>
>> This is how we organise the code in production and it scales really well;
>> it also has the advantage of removing as much logic as possible from the
>> view and keeping it in the update.
>> The last block's boilerplate can be further reduced using an helper like
>> `mapBoth` from http://package.elm-lang.org/packages/Fresheyeball/elm-r
>> eturn/latest (or implementing it yourself, it's a couple of lines).
>>
>>
>>
>>
>>
>>
>> On Monday, March 6, 2017 at 5:56:42 AM UTC+11, Martin Bailey wrote:
>>>
>>> Here are some real world examples of record updates that could be
>>> streamlined at the language level, including attempts to simplify UI code
>>> defining record updates.
>>>
>>> -- Update.elm
>>> update : Msg -> Model -> ( Model, Cmd Msg )
>>> update msg model =
>>>     case msg of
>>>         Update updater ->
>>>             ( updater model, Cmd.none )
>>>         SaveFamily family ->
>>>             ( model, Cmd.saveFamily family )
>>>         _ ->
>>>             ( model, Cmd.none )
>>>
>>> -- View.elm
>>> selected_family : Model -> UI.Node Msg
>>> selected_family model =
>>>     UI.form
>>>         [ UI.class "fluid"
>>>         , UI.onSubmit <| SaveFamily model.family
>>>         ]
>>>         [ UI.input
>>>             [ UI.label "Name"
>>>             , UI.value model.family.name
>>>             , UI.onInput <| \val -> Update <| familyUpdater (\family ->
>>> { family | name = val })
>>>             ]
>>>         ]
>>>
>>> familyUpdater : (Family -> Family) -> Model -> Model
>>> familyUpdater updater model =
>>>     { model | family = updater model.family }
>>>
>>> -- familyUpdater looks simple enough, but at production scale with
>>> multiple embedded records, a few of these functions must be chained
>>> together to complete what could be a simple update such as
>>> model.family.contact.address.street = "Main Street"
>>> -- Multiple specific Msg types such as UpdateFamily can be created to
>>> "clean" the view, but then it becomes really hard to achieve separation of
>>> concern as the Update function grows with out-of-context logic. Those
>>> updater functions are then moved to the Update file or you have to use many
>>> layers of let bindings. There are two concerns here, the update behavior
>>> being defined in the context of the form or becoming overly complex due to
>>> embedded records. The latter is more important.
>>> -- Here's an example from another real world app :
>>>
>>>     RcvGeocode geocode ->
>>>         let
>>>             address =
>>>                 extractAddress geocode
>>>
>>>             updated =
>>>                 address |> updateAddress |> updateContact |>
>>> updateProfile |> updateUser model
>>>         in
>>>             ( { updated | geocode = geocode }, Cmd.none )
>>>
>>> -- This would look much nicer in my opinion :
>>>
>>>     RcvGeocode geocode ->
>>>         ( { model | user.profile.contact.address = extractAddress
>>> geocode }, Cmd.none )
>>>
>> --
> 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