如何在 Elm 中提交表单?
How to submit a form in 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
]
]
因此它显示一个包含内容的表单。
因此,如果我在我的输入和文本区域中写入更新内容,我如何 "catch" 我更新的模型在按钮上的 onClick
事件上发送它?
在 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
字段对象,其中包含文本字段的完整值。
Full example on ellie for elm-0.18, based on 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
这是我发现在 Elm (0.18) 中定义 HTML 表单的 "newest" 方式,如下所示。请注意,它挂钩到表单标记的 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"
]
]
这是一个非常基本的问题,但我没有找到任何示例。
我有这样的看法:
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
]
]
因此它显示一个包含内容的表单。
因此,如果我在我的输入和文本区域中写入更新内容,我如何 "catch" 我更新的模型在按钮上的 onClick
事件上发送它?
在 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
字段对象,其中包含文本字段的完整值。
Full example on ellie for elm-0.18, based on 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
这是我发现在 Elm (0.18) 中定义 HTML 表单的 "newest" 方式,如下所示。请注意,它挂钩到表单标记的 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"
]
]