Sequence Http.get in Elm

我的梦境 提交于 2019-12-14 03:46:23

问题


Below I have a button that attempts to load remote content ...

import Post exposing (Post)
import Html exposing (..)
import Html.Events exposing (..)
import Http
import Json.Decode as Decode


type alias Model =
    { posts : List Post }


type Msg
    = Search String
    | PostsReceived (Result Http.Error (List Post))


update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        Search s ->
            let
                cmd =
                    (Decode.list Post.decode)
                        |> Http.get ("/posts?author=" ++ s)
                        |> Http.send PostsReceived
            in
                ( model, cmd )

        PostsReceived (Ok posts) ->
            { model | posts = posts }
                ! []

        PostsReceived (Err error) ->
            ( model, Cmd.none )


view : Model -> Html Msg
view model =
    button
        [ onClick (Search "amelia") ]
        [ text "Read posts by Amelia" ]

This is a valid Elm program, only there's one little problem: The API doesn't allow me to search by string. This is not allowed

/posts?author=amelia  => Malformed Request Error

However, this is allowed

/posts?author=2       => [ {...}, {...}, ... ]

So I must first fetch an author to get his/her id, and then I can fetch posts using the author's id...

/author?name=amelia => { id: 2, name: "amelia", ... }
/posts?author=2

How can I sequence one request after the next? Ideally I'd like to cache the authors somewhere in the model so we're only requesting ones that we haven't seen before.


回答1:


You can use Task.andThen to chain two tasks together. Assuming that the /posts response includes the author ID, you can then add that author ID into you model when you handle the response.

    Search s ->
        let
            getAuthor =
                Author.decode
                    |> Http.get ("/author?name=" ++ s)
                    |> Http.toTask
            getPosts author =
                (Decode.list Post.decode)
                    |> Http.get ("/posts?author=" ++ author.id)
                    |> Http.toTask
            cmd =
                getAuthor
                    |> Task.andThen getPosts
                    |> Task.attempt PostsReceived
        in
            ( model, cmd )

I've got this compiling at https://ellie-app.com/DBJc6Kn3G6a1 if that helps




回答2:


You can chain together tasks using Task.andThen. You'll first have to convert the web requests to tasks using Http.toTask:

postsByAuthorName : String -> Cmd Msg
postsByAuthorName name =
    Http.get ("/author?name=" ++ name) (Decode.field "id" Decode.int)
        |> Http.toTask
        |> Task.andThen (\id ->
            Http.get ("/posts?author=" ++ toString id) (Decode.list decodePost)
                |> Http.toTask)
        |> Task.attempt PostsReceived



回答3:


A a dictionary and a couple more Msg options should do it. You'll have to write the decoder for the Author response, but other than that this should work

type alias Model =
    { posts : List Post
    , authors : Dict String Int }


type Msg
    = Search String
    | SearchAuthor String
    | AuthorReceived (Result Http.Error Int String)
    | PostsReceived (Result Http.Error (List Post))


update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        Search author ->
            case (Dict.get author model.authors) of
                Nothing ->
                    let 
                        cmd =
                            (Decode.list Post.decode)
                                |> Http.get ("/author?name=" ++ author)
                                |> Http.send AuthorReceived
                    in
                        (model,cmd)

                Just num -> 
                    let
                        cmd =
                            (Decode.list Author.decode)
                                |> Http.get ("/posts?author=" ++ num)
                                |> Http.send PostsReceived
                    in
                        ( model, cmd )

        AuthorReceived (Ok number name) ->
            let
                updatedAuthors = Dict.inster name number model.authors
                cmd =
                    (Decode.list Post.decode)
                        |> Http.get ("/posts?author=" ++ number)
                        |> Http.send PostsReceived 
            in
                {model | authors = updatedAuthors } ! [cmd]

        AuthorReceived (Err error) ->
            (mode, Cmd.none )

        PostsReceived (Ok posts) ->
            { model | posts = posts }
                ! []

        PostsReceived (Err error) ->
            ( model, Cmd.none )


view : Model -> Html Msg
view model =
    button
        [ onClick (Search "amelia") ]
        [ text "Read posts by Amelia" ]


来源:https://stackoverflow.com/questions/51092903/sequence-http-get-in-elm

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!