Chain http request and merge json response in ELM

2019-09-14 06:11发布

问题:

I've succeeded in triggering a simple http request in ELM and decoding the JSON response into an ELM value - [https://stackoverflow.com/questions/43139316/decode-nested-variable-length-json-in-elm]

The problem I'm facing now-

How to chain (concurrency preferred) two http requests and merge the json into my new (updated) model. Note - please see the updated Commands.elm

Package used to access remote data - krisajenkins/remotedata http://package.elm-lang.org/packages/krisajenkins/remotedata/4.3.0/RemoteData

Github repo of my code - https://github.com/areai51/my-india-elm

Previous Working Code -

Models.elm

type alias Model =
    { leaders : WebData (List Leader)
    }

initialModel : Model
initialModel =
    { leaders = RemoteData.Loading
    }

Main.elm

init : ( Model, Cmd Msg )
init =
    ( initialModel, fetchLeaders )

Commands.elm

fetchLeaders : Cmd Msg
fetchLeaders =
    Http.get fetchLeadersUrl leadersDecoder
        |> RemoteData.sendRequest
        |> Cmd.map Msgs.OnFetchLeaders


fetchLeadersUrl : String
fetchLeadersUrl =
    "https://data.gov.in/node/85987/datastore/export/json"

Msgs.elm

type Msg
    = OnFetchLeaders (WebData (List Leader))

Update.elm

update msg model =
    case msg of
        Msgs.OnFetchLeaders response ->
            ( { model | leaders = response }, Cmd.none )

Updated Code - (need help with Commands.elm)

Models.elm

type alias Model =
    { lsLeaders : WebData (List Leader)
    , rsLeaders : WebData (List Leader)           <------------- Updated Model
    }


initialModel : Model
initialModel =
    { lsLeaders = RemoteData.Loading
    , rsLeaders = RemoteData.Loading
    }

Main.elm

init : ( Model, Cmd Msg )
init =
    ( initialModel, fetchLeaders )

Commands.elm

fetchLeaders : Cmd Msg
fetchLeaders =                                  <-------- How do I call both requests here ? And fire separate msgs
    Http.get fetchLSLeadersUrl lsLeadersDecoder    <----- There will be a different decoder named rsLeadersDecoder
        |> RemoteData.sendRequest
        |> Cmd.map Msgs.OnFetchLSLeaders


fetchLSLeadersUrl : String
fetchLSLeadersUrl =
    "https://data.gov.in/node/85987/datastore/export/json"


fetchRSLeadersUrl : String                     <------------------ New data source
fetchRSLeadersUrl =
    "https://data.gov.in/node/982241/datastore/export/json"

Msgs.elm

type Msg
    = OnFetchLSLeaders (WebData (List Leader))
    | OnFetchRSLeaders (WebData (List Leader))         <-------- New message

Update.elm

update msg model =
    case msg of
        Msgs.OnFetchLSLeaders response ->
            ( { model | lsLeaders = response }, Cmd.none )

        Msgs.OnFetchRSLeaders response ->                  <--------- New handler
            ( { model | rsLeaders = response }, Cmd.none )

回答1:

The way to fire off two concurrent requests is to use Cmd.batch:

init : ( Model, Cmd Msg )
init =
    ( initialModel, Cmd.batch [ fetchLSLeaders, fetchRSLeaders ] )

There is no guarantee on which request will return first and there is no guarantee that they will both be successful. One could fail while the other succeeds, for example.

You mention that you want to merge the results, but you didn't say how the merge would work, so I'll just assume you want to append the lists of leaders together in one list, and it will be useful to your application if you had only to deal with a single RemoteData value rather than multiple.

You can merge multiple RemoteData values together with a custom function using map and andMap.

mergeLeaders : WebData (List Leader) -> WebData (List Leader) -> WebData (List Leader)
mergeLeaders a b =
    RemoteData.map List.append a
        |> RemoteData.andMap b

Notice that I'm using List.append there. That can really be any function that takes two lists and merges them however you please.

If you prefer an applicative style of programming, the above could be translated to the following infix version:

import RemoteData.Infix exposing (..)

mergeLeaders2 : WebData (List Leader) -> WebData (List Leader) -> WebData (List Leader)
mergeLeaders2 a b =
    List.append <$> a <*> b

According to the documentation on andMap (which uses a result tuple rather than an appended list in its example):

The final tuple succeeds only if all its children succeeded. It is still Loading if any of its children are still Loading. And if any child fails, the error is the leftmost Failure value.



标签: json http elm