dq-charlie
dq-charlie

Reputation: 247

Elm: making successive network requests

I am trying to learn elm from the past week and want build a simple Hacker News client by calling the official Hacker News API.

I'm calling https: //hacker-news.firebaseio.com/v0/topstories.json to get the top stories which would return an array of story Ids. Once I have the Ids I need to make subsequent calls to https ://hacker-news.firebaseio.com/v0/item/[/* Id goes here */].json fetch the details of each story item.

I have a Main.elm file which would fetch list of top stories.

type Msg = Request 
  | RequestSuccess (List Int) 
  | RequestFail Http.Error


update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
 case msg of
  Request ->
   (model, getTopNews)
  RequestSuccess list->
   (Model list, Cmd.none)
  RequestFail error->
   (Model [], Cmd.none)

Next part is where I am confused, fetching details for each of the item returned. I also have a NewsItem component to display the details of each news item.

How can I solve this, by creating union types inside NewsItem component(child component) to fetch details? If thats how I should do it..

  1. How can I can call the fetch details api from the NewsItem component as soon as the first api call inside Main.elm is complete?
  2. Or am I missing something obvious here? That's not the correct approach at all?

You can see what I have tried so far here.

Upvotes: 1

Views: 149

Answers (1)

lukewestby
lukewestby

Reputation: 1207

Here's my recommendation. It assumes that you'll be loading each NewsItem independently and that they can all fail independently as well. If this isn't the case then we can definitely come up with something that works better for you.

1) Represent your NewsItem not as just a record but a record wrapped in a type to represent the possibility that loading the details could fail. See http://blog.jenkster.com/2016/06/how-elm-slays-a-ui-antipattern.html for more info.

module NewsItem

-- imports and such

type Model = Loading | Success NewsItem | Failure Http.Error

2) Write an init function in your NewsItem module that accepts an Int and returns a (NewsItem.Model, Cmd NewsItem.Msg) pair

init : Int -> (Model, Cmd Msg)
init newsItemId =
    ( Loading
    , getNewsItem newsItemId |> Task.perform GetItemFailure GetItemSuccess
    )

3) In your Main module, once you've fetched your list of IDs, map them onto a List (NewsItem.Model, Cmd NewsItem.Msg) using your init function and use the techniques of the Elm architecture to store them as children in your parent model. I recommend storing them as a Dict Int NewsItem.Model which maps ID onto child model.

RequestSuccess list ->
    let
        children =
            list |> List.map (\id -> (id, NewsItem.init id))

        childModels =
            children
                |> List.map (\(id, (model, cmd)) -> (id, model))
                |> Dict.fromList

        childCmds =
            children
                |> List.map (\(id, (model, cmd)) -> Cmd.map (NewsItemMsg id) cmd)
                |> Cmd.batch
    in
        (Model childModels, childCmds)

Upvotes: 1

Related Questions