Elm 中的序列 Http.get

Sequence Http.get in Elm

下面我有一个 button 试图加载远程内容 ...

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" ]

这是一个有效的 Elm 程序,只有一个小问题:API 不允许我按字符串搜索。 不允许

/posts?author=amelia  => Malformed Request Error

但是,允许的

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

所以我必须找一个作者才能得到his/herid,然后然后我才能拿到使用作者 ID 的帖子...

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

如何让一个请求接一个请求排序?理想情况下,我想将作者缓存在模型中的某处,这样我们就只请求我们以前没有见过的作者。

A a dictionary 和几个 Msg 选项应该可以做到。 您必须为作者响应编写解码器,但除此之外这应该可以工作

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" ]

您可以使用 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

您可以使用 Task.andThen 将两个任务链接在一起。假设 /posts 响应包含作者 ID,然后您可以在处理响应时将该作者 ID 添加到您的模型中。

    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 )

如果有帮助的话,我已经在 https://ellie-app.com/DBJc6Kn3G6a1 编译了这个