2016-08-03 29 views
5

Entschuldigung wegen des ungenauen Fragetitels. Konnte das fragliche Problem nicht genau bestimmen.Wie erstellt man eine Aufgabe mit Nachrichten, die Payload in Elm erfordern?

Ich versuche zu verwenden "Übersetzer Muster" in diesem tollen Blog-Eintrag von Alex Lew hier erwähnt: The Translator Pattern: a model for Child-to-Parent Communication in Elm.

Aber insgesamt Elm Neuling zu sein ich es nicht ganz in der folgenden Situation erhalten:

Ich habe ein Modul wie diese (untergeordnete Komponente im Muster):

module Pages.SignUp.Update exposing (update, Msg(..)) 
import Http 
import HttpBuilder exposing (withHeader, withJsonBody, stringReader, jsonReader, send) 
import Task exposing (Task) 
import Json.Decode exposing (Decoder, bool, (:=)) 
import Json.Encode exposing (encode, object, string) 
import String 
import Update.Extra exposing (andThen) 
import Debug 


type alias Model = 
    { displayName : String 
    , displayNameErrors : List (Maybe String) 
    , email : String 
    , emailErrors : List (Maybe String) 
    , password : String 
    , passwordConfirmation : String 
    , passwordErrors : List (Maybe String) 
    , modelValid : Bool 
    , emailValidationPending : Bool 
    , registrationPending : Bool } 


emptyModel : Model 
emptyModel = 
    { displayName = "" 
    , displayNameErrors = [] 
    , email = "" 
    , emailErrors = [] 
    , password = "" 
    , passwordConfirmation = "" 
    , passwordErrors = [] 
    , modelValid = False 
    , emailValidationPending = False 
    , registrationPending = False } 

type InternalMsg 
    = SetEmail String 
    | SetDisplayName String 
    | SetPassword String 
    | SetPasswordConfirm String 
    | Register 
    | RegisterSucceed (HttpBuilder.Response Bool) 
    | RegisterFail (HttpBuilder.Error String) 
    | ValidateModel 
    | Noop 

type OutMsg 
    = UserRegistered 

type Msg 
    = ForSelf InternalMsg 
    | ForParent OutMsg 

type alias TranslationDictionary msg = 
    { onInternalMessage: InternalMsg -> msg 
    , onUserRegistered: msg 
    } 

type alias Translator msg = 
    Msg -> msg 


translator : TranslationDictionary msg -> Translator msg 
translator { onInternalMessage, onUserRegistered } msg = 
    case msg of 
     ForSelf internal -> 
      onInternalMessage internal 
     ForParent UserRegistered -> 
      onUserRegistered 

never : Never -> a 
never n = 
    never n 

generateParentMessage : OutMsg -> Cmd Msg 
generateParentMessage outMsg = 
    Task.perform never ForParent (Task.succeed outMsg) 

init : (Model, List Notification) 
init = 
    (emptyModel, []) 

update : InternalMsg -> Model -> (Model, Cmd Msg) 

update msg model = 
    case Debug.log "Signup action" msg of 
     SetEmail emailStr -> 
      let model' = 
       {model | email = emailStr } 
      in 
       update ValidateModel model' 

     SetDisplayName nameStr -> 
      let model' = 
       { model | displayName = nameStr } 
      in 
       update ValidateModel model' 

     SetPassword passwordStr -> 
      let model' = 
       { model | password = passwordStr } 
      in 
       update ValidateModel model' 

     SetPasswordConfirm passwordConfirmStr -> 
     let model' = 
      { model | passwordConfirmation = passwordConfirmStr } 
     in 
      update ValidateModel model' 

     ValidateModel -> 
      let validatedModel = 
        validateModel model 
       test = Debug.log "validated model" validatedModel 
      in 
       (validatedModel, Cmd.none) 

     Register -> 
      ({ model | registrationPending = True }, registerUser model) 

     RegisterSucceed _ -> 
      ({ model | registrationPending = False }, (generateParentMessage UserRegistered)) 

     RegisterFail error -> 
      case error of 
       HttpBuilder.BadResponse response -> 
        case Debug.log "Register response status" response.status of 
         422 -> 
          ({ model | registrationPending = False }, Cmd.none) 
         _ -> 
          ({ model | registrationPending = False }, Cmd.none) 
       _ -> 
        ({ model | registrationPending = False }, Cmd.none) 
     Noop -> 
      (model, Cmd.none) 


registerUser : Model -> Cmd Msg 
registerUser model = 
    let url = 
      "/api/users" 

     user = 
      object [ 
       ("user", 
        object 
        [ 
         ("display_name", (string model.displayName)), 
         ("email", (string model.email)), 
         ("password", (string model.password)), 
         ("passwordConfirmation", (string model.passwordConfirmation)) 
        ] 
       ) 
      ] 

     postRequest = 
      HttpBuilder.post url 
      |> withHeader "Content-type" "application/json" 
      |> withJsonBody user 
      |> send (jsonReader decodeRegisterResponse) stringReader 
    in 
     Task.perform ForSelf RegisterFail ForSelf RegisterSucceed postRequest 

decodeRegisterResponse : Decoder Bool 
decodeRegisterResponse = 
     "ok" := bool 

validateRequired : String -> String -> Maybe String 

validateRequired fieldContent fieldName = 
      case String.isEmpty fieldContent of 
       True -> Just <| String.join " " [ fieldName, "required" ] 
       False -> Nothing 

validateEmail : String -> List (Maybe String) 

validateEmail email = 
    let requiredResult = 
      validateRequired email "Email" 
    in 
     [requiredResult] 

validatePassword : String -> String -> List (Maybe String) 
validatePassword password passwordConf = 
    let requiredResult = 
      validateRequired password "Password" 
     confirmResult = 
      case password == passwordConf of 
       True -> Nothing 
       False -> Just "Password confirmation does not match" 
    in 
     [ requiredResult, confirmResult ] 

validateModel : Model -> Model 
validateModel model = 
    let emailResult = 
      validateEmail model.email 
     displayNameResult = 
      validateRequired model.displayName "Displayname" :: [] 
     passwordResult = 
      validatePassword model.password model.passwordConfirmation 
     errors = 
      List.concat [emailResult, displayNameResult, passwordResult ] |> List.filterMap identity 
     modelValid = List.isEmpty errors 
    in 
     { model | 
      emailErrors = emailResult, 
      displayNameErrors = displayNameResult, 
      passwordErrors = passwordResult, 
      modelValid = modelValid 
     } 

Das Problem ist, die registerUser-Funktion, die offensichtlich nicht so funktioniert, wie sie jetzt ist. Ich kann es nicht bekommen, um Cmd Msg zurückzugeben. Ich kann so machen, dass es Cmd InternalMsg zurückgibt aber dann stoße ich natürlich auf Probleme in den Update-Funktionen Nachricht registrieren. Dort müsste ich Cmd InternalMsg in Cmd Msg umwandeln.

Ich habe versucht, dies an beiden Orten zu lösen, aber immer kurz kommen. Es ist wahrscheinlich eine einfache Lösung, aber leider keine Fähigkeiten, um das zu tun, wie es scheint.

Jede Hilfe würde sehr geschätzt werden.

+1

Dies ist ein rate, aber hast du versucht ['Cmd.map ForSelf: Cmd InternalMsg -> Cmd Msg] (http://package.elm-lang.org/packages/elm-lang/core/4.0.0/Platform-Cmd# Karte)? – Lynn

Antwort

7

Das ist eine hässliche Teil der Übersetzer Muster ist, sollten Sie Ihren Befehl zum Msg Nachricht, Cmd.map so statt:

Task.perform ForSelf RegisterFail ForSelf RegisterSucceed postRequest 

Sie sollten wie etwas haben:

Cmd.map ForSelf (Task.perform RegisterFail RegisterSucceed postRequest) 
+0

Wenn Sie 'Cmd.map' hier nicht gerne verwenden, warum nicht stattdessen Task.perform (ForSelf << RegisterFail) (ForSelf << RegisterSucceed) postRequest? –

+0

@ Zimmi48 Funktion Zusammensetzung Operatoren sind verwirrend für Leute, die neu zu Elm und FP, würde ich nicht empfehlen, sie zu verwenden. Der hässliche Teil ist diese zusätzliche Komplexität, nicht die 'Cmd.map'. – halfzebra

+0

Ich stimme nicht zu. Die Zusammensetzung der Funktionen ist eine Vorstellung, von der jede Person, die High-School-Mathe gelernt hat, weiß. Der Vorteil der Verwendung in diesem Fall ist, dass es zeigt, wo der Fehler war (5 Argumente für "Task.perform" anstatt nur drei). –