I have a user
entry in my model with details of the current user. I decode it from flags.user
and that can fail. But the whole app is basically meaningless if the user data isn’t there so I don’t particular want it to be a Maybe User
or put the whole model in a Maybe
based on the success of the decoding.
I realise that the answer might just be to deal with it and choose one of those two options but I’m curious what others think for this. I currently have the approach of unpacking the decode result and calling Debug.crash
but obviously that feels terrible and doesn’t communicate anything to the user.
I guess I’d almost like the init
function to be able to fail and the error be returned to the Javascript code that is mounting it to be handled there.
Any advice would be very welcome.
If the functioning of your app is dependent on this User
being successfully decoded, and there is truly nothing to do without it, I would have the user field in my model be a User
and not a Maybe User
, decode the user in my init
function, and Debug.crash if a user could not be decoded.
If you would like a more friendly error message and to eliminate all calls to Debug.crash, which are worthy goals, you could make your App.Model
an ADT of something like:
Model
= Invalid String
| Valid ModelProps
Then in init
if your user didn’t decode you’d set model
equal to Invalid "Must have a valid user"
or something and case off of your model type in update
and view
Edit:
Here’s a link to an example that avoids Debug.crash
in Ellie https://ellie-app.com/BCthmbKya1/0
if you change "a"
on line 30 to a string of an int like "0"
, you can see how it works on successful decoding.
2 Likes
I’d do something similar to what @itsgreggreg suggested:
main =
Html.program
{ init = init
, update = updateResult
, view = viewResult
, subscriptions = always Sub.none
}
viewResult : Result String Model -> Html Msg
viewResult result =
case result of
Ok model ->
view model
Err error ->
viewError error
updateResult : Msg -> Result String Model -> ( Result String Model, Cmd Msg )
updateResult msg result =
case result of
Ok model ->
let
( newModel, newCmd ) =
update msg model
in
( Ok newModel, newCmd )
Err _ ->
( result, Cmd.none )
init : Flags -> ( Result String Model, Cmd Msg )
view : Model -> Html Msg
viewError : String -> Html Msg
update : Msg -> Model -> ( Model, Cmd Msg )
In this way, your update
and view
still work in terms of Msg
and the validated Model
, but you also have a viewError : String -> Html Msg
that lets you display something nice to the user about what went wrong!
We use a strategy like this at NoRedInk. Works great!
3 Likes