How to create a task with messages that require a payload in Elm?

Sorry for the inaccurate title of the question. Failed to pinpoint the problem.

I am trying to use the “translator pattern” mentioned in this wonderful blog post by Alex Lew: Translator Sample: Model for Children - Knit Parent Link .

But, being a newbie to Elm, I don't quite understand him in the following situation:

I have a module like this (a child component in a template):

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 } 

The problem is the registerUser function, which obviously does not work as it is now. I can not get him to return Cmd Msg. I can make it return Cmd InternalMsg, but then, of course, I ran into problems in the update function. There I will need to convert Cmd InternalMsg to Cmd Msg.

I tried to solve this in both places, but always came up with a short position. Most likely, this is a simple solution for this, but, alas, there are no skills for this, as it seems.

Any help would be greatly appreciated.

+5
source share
1 answer

This is the ugly part of the Translator template, you have Cmd.map your command send an Msg message, so instead:

 Task.perform ForSelf RegisterFail ForSelf RegisterSucceed postRequest 

You should have something like:

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

All Articles