Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use select (dropdown) tag in Elm lang

I want to render simple dropdown within my elm app with the following code but it's not working as expected. I want to keep union type Role in place and avoid using strings if it's possible.

What's the best way to work with dropdown in Elm? I didn't find any example yet.

import Html exposing (..)
import Html.App exposing (beginnerProgram)
import Html.Events exposing (..)
import Html.Attributes exposing (..)
import Json.Decode

main =
  beginnerProgram
    { model = initialModel
    , view = view
    , update = update
    }

initialModel =
  { role = None
  }

type Role
  = None
  | Admin
  | User

type alias Model = 
  { role: Role
  }

type Msg
  = SelectRole Role


update msg model =
  case msg of
    SelectRole role ->
      { model | role = role }


view : Model -> Html Msg
view model =
  div
    []
    [ select
        [ ]
        [ viewOption None
        , viewOption Admin
        , viewOption User
        ]
    , pre [] [ text <| toString model ]
    ]



viewOption : Role -> Html Msg
viewOption role =
  option
    [ onClick (SelectRole role) ]
    [ text <| toString role ]
like image 958
ondrej Avatar asked Sep 07 '16 13:09

ondrej


1 Answers

The onClick handler doesn't really work with option elements. You'll instead want to capture the change event and look at the JSON targetValue to see what was selected.

I would first remove the onClick handler and instead set the option tag's value attribute:

viewOption : Role -> Html Msg
viewOption role =
  option
    [ value <| toString role ]
    [ text <| toString role ]

Now you'll need a JSON Decoder that can take the event's targetValue string and convert it to a Role:

targetValueRoleDecoder : Json.Decode.Decoder Role
targetValueRoleDecoder =
  targetValue `Json.Decode.andThen` \val ->
    case val of
      "Admin" -> Json.Decode.succeed Admin
      "User" -> Json.Decode.succeed User
      "None" -> Json.Decode.succeed None
      _ -> Json.Decode.fail ("Invalid Role: " ++ val)

Now you just need to add that to the select change event handler:

    [ select
        [ on "change" (Json.Decode.map SelectRole targetValueRoleDecoder)
        ]
        [ viewOption None
        , viewOption Admin
        , viewOption User
        ]

The fact that we have to resort to strings is merely an artifact of having to translate values from DOM events into valid union types. You'll still want to keep Role as a union type; you just have to create JSON decoders targeting each union type for now, since Elm does not (yet) support any kind of automatic String to Union Type function.

like image 85
Chad Gilbert Avatar answered Nov 16 '22 11:11

Chad Gilbert