kayne
kayne

Reputation: 349

Elm - Updating Fields Dynamically in Elm

I copied this code so anyone can try it out http://elm-lang.org/try

import Html exposing (..)
import Html.App as App
import Html.Attributes exposing (..)
import Html.Events exposing (..)


main =
  App.program
  { init = init
  , view = view
  , update = update
  , subscriptions = \_ -> Sub.none
  }


init =
  ( { posts = [], newPost = { title = "", slug = "" } }, Cmd.none )


type alias Post =
  { title : String
  , slug : String
  }


type alias Model =
  { posts : List Post
  , newPost : Post
  }


type Msg
  = NoOp
  | NewPostField Post String String


update msg model =
  case msg of

    NoOp ->
      ( model, Cmd.none )

    NewPostField currentPost field newValue ->
      case field of

        "title" ->
          ( { model | newPost = { slug = currentPost.slug, title = newValue } }, Cmd.none )

        "slug" ->
          ( { model | newPost = { slug = newValue, title = currentPost.title } }, Cmd.none )

       -- The problem is here, I have to repeat this process for every field

        _ ->
          ( model, Cmd.none )


view model =
  div []
    [ h1 [] [ text ("title : " ++ model.newPost.title ++ " | slug : " ++ model.newPost.slug) ]
    , input [ onInput (NewPostField model.newPost "title"), placeholder "Title" ] []
    , input [ onInput (NewPostField model.newPost "slug"), placeholder "Slug" ] []
    , button [] [ text "Save" ]
  ]

I minimized the fields to two (title and slug), but there are others like : content, excerpt...

Is there is anyway I can update the record without creating a case for all the fields, something like updating only the field necessary without going through all of them ?

Upvotes: 2

Views: 942

Answers (1)

mariosangiorgio
mariosangiorgio

Reputation: 5543

I would change the following thing in your code:

If you have a specific message for each action you can have much cleaner update and view functions. The compiler will also help you checking that you handle all the cases and that you don't pass a meaningless argument.

type Msg
  = NoOp
  | NewPostTitle Post String
  | NewPostSlug Post String

This doesn't save you too much typing, but your update will look like this. Note that you don't have nested pattern matching anymore. Also, note the syntax for updating records, one field at a time.

update msg model =
  case msg of
    NoOp ->
      ( model, Cmd.none )
    NewPostTitle currentPost value ->
      ( { model | newPost = { currentPost | title = value } }, Cmd.none )
    NewPostSlug currentPost value ->
      ( { model | newPost = { currentPost | slug = value } }, Cmd.none )

Finally, in your view you won't have to pass the string parameter, which makes the code a bit more concise. But the real important part is that now it's type-safe.

view model =
  div []
    [ h1 [] [ text ("title : " ++ model.newPost.title ++ " | slug : " ++ model.newPost.slug) ]
    , input [ onInput (NewPostTitle model.newPost), placeholder "Title" ] []
    , input [ onInput (NewPostSlug model.newPost), placeholder "Slug" ] []
    , button [] [ text "Save" ]
  ]

Upvotes: 5

Related Questions