如何在Elm提交表格?

时间:2016-04-03 15:34:32

标签: forms submit elm

这是一个非常基本的问题,但我没有找到任何例子。
我有这样的观点:

view address model =
  div []
    [ div [] [ text <|"ID : " ++ toString model.id ]
    , form
        []
        [ input [ value model.title ] []
        , textarea [ value model.content ] []
        , button [ onClick address ( SubmitPost model ) ] [ text "Submit" ] // Here is the issue, I want to send my updated model
        ]
    ]

因此它会显示一个包含内容的表单。
因此,如果我在输入和textarea中写入更新内容,我该如何捕捉&#34;我在发送它的按钮上onClick事件的更新模型?

3 个答案:

答案 0 :(得分:24)

在Elm中处理表单的标准方法是,只要任何在表单上发生更改,就会触发对模型的更新。您通常会看到某种on事件属性附加到每个表单元素。

对于您的示例,您需要使用on "input"来触发使用最新值更新模型的事件。但在我们能够做到这一点之前,我们需要创建一些响应来自任一领域的更新的动作。

type Action
  = SubmitPost
  | UpdateTitle String
  | UpdateContent String

我冒昧地将SubmitPost Model动作更改为SubmitPost。由于我们将您的代码更改为始终是最新的,因此除了操作SubmitPost之外,您不需要触发执行提交的事件。

现在您已经有了其他操作,您需要在update函数中处理它们:

update action model =
  case action of
    UpdateTitle s -> 
      ({ model | title = s }, Effects.none)
    UpdateContent s -> 
      ({ model | content = s }, Effects.none)
    ...

我们现在可以在文本字段中添加on属性,以便在发生任何更改时触发更新。 "input"是浏览器在文本内容发生变化时触发的事件,它为您提供的覆盖范围远远超过仅仅观看keypress事件等内容。

view address model =
  div []
    [ div [] [ text <| "ID : " ++ toString model.id ]
    , form
      []
      [ input
        [ value model.title
        , on "input" targetValue (Signal.message address << UpdateTitle)
        ]
        []
      , textarea
        [ value model.content
        , on "input" targetValue (Signal.message address << UpdateContent)
        ]
        []
      , button [ onClick address SubmitPost ] [ text "Submit" ]
      ]
    ]

targetValue解码器是一个Json解码器,它检查被触发的javascript事件,向下钻取到javascript对象内的event.target.value字段,其中包含文本字段的完整值。

答案 1 :(得分:7)

对于elm-0.18,

Full example on ellie,基于http://musigma.org/elm/2016/11/28/elm.html

将以下文件另存为Main.elm

module Main exposing (main)

import Html exposing (Html, div, text, form, textarea, button, input)
import Html.Attributes exposing (type_, action, value, disabled)
import Html.Events exposing (onSubmit, onInput)
import Http
import Json.Decode as Json
import Json.Encode


type alias Model =
    { newComment : NewComment
    , comments : List Comment
    }


emptyModel : Model
emptyModel =
    { newComment = emptyNewComment
    , comments = []
    }


emptyNewComment =
    NewComment -1 "" ""


type alias NewComment =
    { userId : Int
    , title : String
    , body : String
    }


type Msg
    = AddComment
    | UpdateComment NewComment
    | AddCommentHttp (Result Http.Error Comment)


update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        AddComment ->
            let
                newComment =
                    Debug.log "model.newComment" model.newComment
            in
                ( { model | newComment = emptyNewComment }, postComment newComment )

        UpdateComment newComment ->
            ( { model | newComment = newComment }, Cmd.none )

        AddCommentHttp (Ok response) ->
            let
                _ =
                    Debug.log "response" response
            in
                ( { model | comments = model.comments ++ [ response ] }, Cmd.none )

        AddCommentHttp (Err err) ->
            let
                _ =
                    Debug.log "err" err
            in
                ( model, Cmd.none )


postComment newComment =
    Http.send AddCommentHttp
        (Http.post "https://jsonplaceholder.typicode.com/posts"
            (encodeNewComment newComment)
            decodeComment
        )


encodeNewComment : NewComment -> Http.Body
encodeNewComment newComment =
    Http.jsonBody <|
        Json.Encode.object
            [ ( "title", Json.Encode.string newComment.title )
            , ( "body", Json.Encode.string newComment.body )
            , ( "userId", Json.Encode.int newComment.userId )
            ]


type alias Comment =
    { title : String
    , body : String
    , userId : Int
    , id : Int
    }


decodeComment : Json.Decoder Comment
decodeComment =
    Json.map4 Comment
        (Json.field "title" Json.string)
        (Json.field "body" Json.string)
        (Json.field "userId" Json.int)
        (Json.field "id" Json.int)


view : Model -> Html Msg
view model =
    div [] <|
        [ viewForm model.newComment UpdateComment AddComment
        ]
            ++ List.map (\comment -> div [] [ text <| toString comment ]) model.comments


viewForm : NewComment -> (NewComment -> msg) -> msg -> Html msg
viewForm newComment toUpdateComment addComment =
    form
        [ onSubmit addComment, action "javascript:void(0);" ]
        [ div []
            [ input
                [ value newComment.title
                , onInput (\v -> toUpdateComment { newComment | title = v })
                ]
                []
            ]
        , textarea
            [ value newComment.body
            , onInput (\v -> toUpdateComment { newComment | body = v })
            ]
            []
        , div []
            [ button
                [ type_ "submit"
                , disabled <| isEmpty newComment.title || isEmpty newComment.body
                ]
                [ text "Add Comment" ]
            ]
        ]


isEmpty : String -> Bool
isEmpty =
    String.isEmpty << String.trim


main : Program Never Model Msg
main =
    Html.program
        { view = view
        , update = update
        , subscriptions = \_ -> Sub.none
        , init = ( emptyModel, Cmd.none )
        }

并运行:

elm package install -y elm-lang/http
elm-reactor

在网络浏览器http://localhost:8000/Main.elm

中打开

答案 2 :(得分:4)

这是&#34;最新的&#34;我发现在Elm(0.18)中定义HTML表单的方式如下。请注意,它挂钩到表单标记的onSubmit属性,而不是特定按钮的onClick。

view : Model -> Html Msg
view model =
    Html.form
        [ class "my-form"
        , onWithOptions
            "submit"
            { preventDefault = True, stopPropagation = False }
            (Json.Decode.succeed SubmitPost)
        ]
        [ button []
            [ text "Submit"
            ]
        ]