htmldrop-down-menuelm

`Html.select` displaying wrong value when changing the options


I have a Model with options and a selected item.

type alias Model =
    { options : List String
    , selected : String
    }

The Model's initial value is given by Model ["aaa","bbb"] ""
To generate the options I use the following helper: [This ended up being the problem, see correct version below]

selectHelper : (String -> msg) -> Bool -> List String -> String -> Html msg
selectHelper oninput enabled options current =
    select [ onInput oninput, disabled <| not enabled ]
        (Utils.distinct identity options
            |> List.map
                (\opt -> option [ value opt, selected <| opt == current ] [ text opt ])
        )

and my view and update are simply:

view : Model -> Html Msg
view model =
    div []
        [ selectHelper Select True (model.selected :: model.options) model.selected
        , text model.selected
        ]

update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        Select newval ->
            ( { model | selected = newval }, Cmd.none )

When loading the page, I see the select control with "" selected as the default options. Now I open the dropdown and click on the "aaa" option.
In the Model, I see that selected is now "aaa". However, visually, on the html page, I see that "bbb" is selected.

Does anybody know about this? What is the correct way to add the current value as an ad-hoc option which can disappear if deselected?

EDIT (2023.06.30): This is the implementation of my Utils.distinct function which the selectHelper uses:

distinct : (a -> comparable) -> List a -> List a
distinct extractKey set =
    set
        |> List.map (\a -> ( extractKey a, a ))
        |> Dict.fromList
        |> Dict.values

EDIT (2023.07.09) The correct version of selectHelper which works is:

selectHelper : (String -> msg) -> Bool -> List String -> String -> Html msg
selectHelper oninput enabled options current =
    Keyed.node "select"
        [ onInput oninput, disabled <| not enabled, value current ]
        (distinct identity options
            |> List.map
                (\opt -> ( opt, option [ value opt, selected <| opt == current ] [ text opt ] ))
        )

Solution

  • What you are seeing are artefacts of the elm virtual dom handler getting out of sync with the Dom itself.

    If you want dynamically to add/remove items to a select element while it is being used, you will need to use https://package.elm-lang.org/packages/elm/html/latest/Html-Keyed - give each option a unique key.