2016-04-03 23 views
9

Đó là một câu hỏi thực sự cơ bản nhưng tôi không tìm thấy bất kỳ ví dụ nào.
tôi có một cái nhìn như thế này:Làm cách nào để gửi biểu mẫu trong 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 
     ] 
    ] 

Vì vậy, nó hiển thị một hình thức với nội dung bên trong.
Vì vậy, nếu tôi viết nội dung nhập và văn bản để cập nhật nội dung, làm cách nào để "bắt" mô hình cập nhật của tôi trên sự kiện onClick trên nút để gửi?

Trả lời

20

Cách tiêu chuẩn để xử lý biểu mẫu trong Elm là kích hoạt cập nhật cho mô hình của bạn bất cứ khi nào mọi thứ thay đổi trên biểu mẫu. Thông thường, bạn sẽ thấy một loại thuộc tính sự kiện on được đính kèm với mỗi phần tử biểu mẫu.

Ví dụ của bạn, bạn sẽ muốn sử dụng on "input" để kích hoạt các sự kiện cập nhật mô hình của bạn với giá trị mới nhất. Nhưng trước khi chúng ta có thể làm điều đó, chúng ta sẽ cần phải tạo ra một số hành động phản hồi các cập nhật từ một trong hai trường.

type Action 
    = SubmitPost 
    | UpdateTitle String 
    | UpdateContent String 

Tôi đã tự do thay đổi hành động SubmitPost Model thành chỉ SubmitPost. Vì chúng tôi đang thay đổi mã của bạn để luôn được cập nhật, bạn không cần bất kỳ điều gì khác ngoài hành động SubmitPost để kích hoạt sự kiện gửi.

Bây giờ bạn có các hành động khác, bạn sẽ cần phải xử lý chúng trong update chức năng:

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

Bây giờ chúng ta có thể thêm các thuộc tính on vào lĩnh vực văn bản của bạn để kích hoạt bản cập nhật bất cứ khi nào bất cứ điều gì thay đổi. "input" là sự kiện mà trình duyệt sẽ kích hoạt khi nội dung văn bản thay đổi và nó mang đến cho bạn nhiều mức độ phù hợp hơn là chỉ xem một số sự kiện như 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" ] 
     ] 
    ] 

Các bộ giải mã targetValue là một Decoder Json mà kiểm tra các sự kiện javascript đã bị sa thải, khoan xuống event.target.value lĩnh vực bên trong đối tượng javascript, trong đó có đầy đủ giá trị của trường text.

+0

Cảm ơn sự giúp đỡ của bạn. Tôi sẽ từ bỏ elm và chờ đợi phương pháp thực sự để làm điều này. Trong năm 2016, phải hack elm để gửi mẫu chỉ là ngu ngốc. Tôi sẽ chờ đợi cho elm để được trưởng thành – BoumTAC

+13

@ BoumTAC đây không phải là "hack", đây là cách bạn mã trong Elm. – halfzebra

+1

@halfzebra Tôi hiểu, nhưng tôi cũng hiểu rằng chúng tôi đang trong năm 2016 không phải năm 2005.Tôi cũng hiểu mọi người sẽ không bao giờ sử dụng elm nếu nó như thế này – BoumTAC

4

Full example on ellie cho elm-0.18, dựa trên http://musigma.org/elm/2016/11/28/elm.html

Lưu dưới tập tin như 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) 
     } 

và chạy:

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

Mở trong trình duyệt web http://localhost:8000/Main.elm

+0

Rất đẹp! Nhưng phải mất một thời gian để tôi tìm ra rằng "tiêu đề" trong 'onInput <| (toUpdateComment << \title -> NewComment 1 title newComment.body) 'sẽ đến trực tiếp từ' onInput'. – farmio

+1

Tôi đã thay đổi nó thành 'onInput (\ v -> toUpdateComment {newComment | title = v})'. Nên rõ ràng hơn – rofrol

3

Đây là cách "mới nhất" mà tôi đã tìm thấy xác định một hình thức HTML trong Elm (0,18) là dưới đây. Lưu ý rằng nó móc vào thuộc tính onSubmit của thẻ biểu mẫu chứ không phải là onClick của một nút cụ thể.

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

Bạn làm gì trong hàm 'update' khi bạn nhận được phương thức' SubmitPost' –

+0

Trông giống như 'action' javascript: void (0); "' là không cần thiết. https://ellie-app.com/9zTH65GHXa1/0 và 'onSubmit' đã có' preventDefault' được đặt thành 'True' https://github.com/elm-lang/html/blob/2.0.0/src/Html /Events.elm#L130 – rofrol

Các vấn đề liên quan