TBatist
TBatist

Reputation: 163

In Elm 0.19.1, is it possible to make an http-get request within a decoder for the initial model?

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

Answers (1)

bdukes
bdukes

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

Related Questions