Reputation: 163
I have a component created in Elm where users can create a list of different criteria. For this component, users should be able to create criteria to search for contacts. These contacts will be shown on a different (non-Elm) page. If the users return to the criteria builder, the previous filled in criteria should be shown again.
To do this, I use the JSON that was used to create the query in Elm. This should be decoded to create the objects that will show the input that the user has made before.
One of the objects I use is a list. This list contains of tuples with id and name. For the query builder, I only send the id of the objects in the JSON to the back-end. This means that, if a user returns to the criteria builder, the decoder can only decode the list of id's. For my list selection, I also want to fetch the names of the objects with the id's.
Now this is where I have some problems. To make an http-request, I have to catch the result with an Cmd.Msg
. In the update function, I must then update my Model. The problem is, I don't have a model yet, because I'm still decoding my initial model. Also, I guess using a Decoder
(for the result of the http-request) within a Decoder
(for my initial model) is not the best of ideas.
Is there a way to solve this problem where I am making an http-request within a Decoder
for my initial Model?
Upvotes: 5
Views: 129
Reputation: 156075
There are a handful of options here, depending on how complicated your situation is. At the end of the day, init
produces a (Model, Cmd Msg)
value, and your HTTP request(s) can be part of that Cmd
.
Option 1: Generate HTTP requests based on the decoded model
init : Decode.Value -> (Model, Cmd Msg)
init json =
let
initialRequests model =
List.map initialRequest model.items
in
case Decode.decodeValue flagsDecoder json of
Ok model ->
( model, initialRequests model |> Cmd.batch )
Err _ ->
( initialModel, Cmd.none )
Option 2: Generate HTTP requests while decoding
init : Decode.Value -> ( Model, Cmd Msg )
init json =
let
flagsDecoder =
Decode.map (\items -> ({ items = List.map Tuple.first items }, List.map Tuple.second items))
(Decode.field "items" (Decode.list itemDecoder))
itemDecoder =
Decode.field "name" Decode.string
|> Decode.map (\name -> (name, initialRequest name))
in
case Decode.decodeValue flagsDecoder json of
Ok ( model, cmds ) ->
( model, cmds |> Cmd.batch )
Err _ ->
( initialModel, Cmd.none )
Your init
function will need to be able to return a Model
value that is "incomple", waiting on these responses to come back. Sometimes that's as simple as modeling values that you're waiting on as Maybe
values, sometimes you might want to wrap a large portion of your model in a value that indicates if it's valid or not.
type Model
= Initializing String (Maybe Thing1) (Maybe Thing2)
| Ready { baseUrl : String, thing1 : Thing1, thing2 : Thing2 }
Upvotes: 5