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-return/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.