elmelm-architecture

How can I use Cmd.map within a update-function using multiple arguments?


In Elm 0.19.1, I have the following Msg (among others):

type Msg
    = Yay Multiselect.Msg

update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        Yay sub ->
            let
                ( subModel, subCmd, _ ) =
                    Multiselect.update sub model
            in
            ( { model | multiselectC = subModel }, Cmd.map Yay subCmd )

I'm using an external library here (Multiselect: https://github.com/inkuzmin/elm-multiselect). For this Msg, I'm correctly following the examples of the library. Goal here is handling events from this library to update the Model in this module. This correctly works.

Here comes the problem. I want to add multiple arguments to this Msg. For example, let's add the Multiselect.Model to it, creating the following type:

type Msg
    = Yay Multiselect.Msg Multiselect.Model

This gives an error in the Cmd.map (makes sense since it is missing de Model). I've tried updating the Cmd.map-function in the following function:

Yay sub msModel ->
            let
                ( subModel, subCmd, _ ) =
                    Multiselect.update sub msModel
            in
            ( { model | multiselectC = subModel }, Cmd.map (\cmd -> Yay cmd subModel) subCmd )

This will remove the compile errors and will work for most of the events. However, some some events (for example, removing items), the library will behave unexpectedly and different from the code before.

I've also added some logging, creating the following command line:

enter image description here

It looks like it is correctly updating the Model when removing an selected item. However, with the next triggered event, it looks like the function is using the old model, removing the update. This basically means that nothing happens.

How can I correctly write the Cmd.map with multiple arguments which will behave the same as the first update-function with only 1 argument?


Solution

  • The way you pass multiple arguments to your Msgs is correct.

    But you ran into a special case, because you passed state from the view.
    Most times, it is better to only pass information what is supposed to change and not both the supposed change and an old state as part of your message.

    My take at an explanation: This behavior is due to the Elm runtime and optimizations for rendering: The DOM is only rendered between 30 and 60 times a second, and the event that is fired from the view is using an old state. So the state of your model was updated (because it is executed as fast as possible), but then later overwritten with the old state that is still being displayed by the browser.

    When looking at this image from the official documentation, you can see that not every update triggers an immediate re-rendering of the DOM, but the work is done out-of-sync: enter image description here