2016-04-03 27 views
9

È una domanda molto semplice ma non ho trovato alcun esempio.
Ho una visione come questa:Come inviare un modulo 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 
     ] 
    ] 

Quindi visualizzare un modulo con il contenuto al suo interno.
Quindi se scrivo nel mio input e textarea per aggiornare il contenuto, come faccio a "catturare" il mio modello aggiornato sull'evento onClick sul pulsante per inviarlo?

risposta

20

Il modo standard di gestire i moduli in Elm è di attivare gli aggiornamenti del modello ogni volta che qualsiasi modifica nel modulo. In genere, viene visualizzato un tipo di attributo evento on associato a ciascun elemento del modulo.

Per il tuo esempio, ti consigliamo di utilizzare on "input" per attivare eventi che aggiornano il tuo modello con l'ultimo valore. Ma prima di poterlo fare, dovremo creare alcune azioni che rispondano agli aggiornamenti da entrambi i campi.

type Action 
    = SubmitPost 
    | UpdateTitle String 
    | UpdateContent String 

mi sono permesso di cambiare il tuo SubmitPost Model azione da solo SubmitPost. Poiché stiamo modificando il tuo codice per essere sempre aggiornato, non è necessario altro che l'azione SubmitPost per attivare un evento che esegue l'invio.

Ora che avete le azioni aggiuntive, è necessario gestirli nella funzione update:

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

possiamo ora aggiungere gli attributi on sui vostri campi di testo per attivare gli aggiornamenti ogni volta che qualcosa cambia. "input" è l'evento in cui i browser si attivano quando il contenuto del testo cambia e offre più copertura rispetto alla semplice visualizzazione di eventi come 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" ] 
     ] 
    ] 

Il decodificatore targetValue è un decodificatore JSON che controlla l'evento JavaScript che è stato sparato, drill-down al campo event.target.value all'interno dell'oggetto JavaScript, che contiene l'intero valore del campo di testo.

+0

Grazie per il vostro aiuto. Rinuncerò all'olmo e aspetto un vero metodo per farlo. Nel 2016, dover hackerare Olmo per inviare moduli è semplicemente stupido. Aspetterò che l'elm sia maturo – BoumTAC

+13

@BoumTAC questo non è "hacking", questo è il modo in cui si codifica in Elm. – halfzebra

+1

@halfzebra Capisco, ma capisco anche che siamo nel 2016, non nel 2005.Capisco anche che la gente non userà mai ELM se è così. – BoumTAC

4

Full example on ellie per olmo-0,18, sulla base di http://musigma.org/elm/2016/11/28/elm.html

Salva file come di seguito 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) 
     } 

ed eseguire:

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

Open in browser Web http://localhost:8000/Main.elm

+0

Molto bello! Ma mi ci è voluto del tempo per capire quel "titolo" in "onInput <| (toUpdateComment << \title -> NewComment 1 title newComment.body) 'arriverà direttamente da' onInput'. – farmio

+1

L'ho modificato in 'onInput (\ v -> toUpdateComment {newComment | title = v})'. Dovrebbe essere più chiaro – rofrol

3

Questo è il modo "più nuovo" che ho trovato definire un modulo HTML in Elm (0.18) è sotto. Si noti che si aggancia alla proprietà onSubmit del tag form piuttosto che a un clic su un particolare pulsante.

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

Cosa fai nella funzione 'update' quando ricevi un metodo' SubmitPost' –

+0

Sembra "azione" javascript: void (0); "non è necessario. https://ellie-app.com/9zTH65GHXa1/0 e 'onSubmit' hanno già' preventDefault' impostato su 'True' https://github.com/elm-lang/html/blob/2.0.0/src/Html /Events.elm#L130 – rofrol