Elm中的序列Http.get

时间:2018-06-29 00:52:13

标签: elm elm-architecture

下面我有一个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       => [ {...}, {...}, ... ]

因此,我必须先 取得作者才能获得他的id,然后然后我可以使用作者的ID来获取帖子...

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

如何在下一个请求之后排序?理想情况下,我想将作者缓存在模型中的某个位置,以便我们只请求以前从未见过的作者。

3 个答案:

答案 0 :(得分:5)

您可以使用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进行编译

答案 1 :(得分:3)

您可以使用Task.andThen将任务链接在一起。首先,您必须使用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

答案 2 :(得分:2)

应该使用a dictionary和更多Msg选项。 您必须为Author响应编写解码器,但除此之外应该可以工作

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