【问题标题】:Elm updating model with httpElm 使用 http 更新模型
【发布时间】:2017-12-04 19:56:19
【问题描述】:

我正在尝试在我的更新功能中更新我的List Token 模型中的比特币价格字段。这是我的代码,我似乎无法让它只更新价格字段。我是否需要访问列表元素,因为我的模型是列表?这可以在 elm 中使用记录语法吗?

import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (..)
import Bass exposing (style, center, h1)
import Http
import Json.Decode as Decode




main =
  Html.program
    { init = init
    , view = view
    , update = update
    , subscriptions = subscriptions
    }



------------- MODEL


type alias Model =
  { tokens : List Token
  }

init : (Model, Cmd Msg)
init =
  (initialModel , Cmd.none)

initialModel : Model
initialModel =
  { tokens = [Token "Bitcoin" "150" "11000.00"]
  }

type alias Token =
  { name : String
  , holdings : String
  , price : String
}

------------- UPDATE


type Msg
  = FetchDatabasePrice | FetchLivePrice (Result Http.Error String)


update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
  case msg of
    FetchDatabasePrice ->
      (model, getPrice )
    FetchLivePrice (Ok newPrice) ->
      ( { model | price = newPrice }, Cmd.none )
    FetchLivePrice (Err _) ->
      (model,Cmd.none)


getPrice : Cmd Msg
getPrice  =
  let
    url = "https://api.coinmarketcap.com/v1/ticker/bitcoin/"
    request = Http.get url decodedUrl
  in
    Http.send FetchLivePrice request

decodedUrl : Decode.Decoder String
decodedUrl = Decode.at ["price_usd"] Decode.string
------------- SUBSCRIPTIONS


subscriptions : Model -> Sub Msg
subscriptions model =
  Sub.none



------------- VIEW


view : Model -> Html Msg
view model =
  div []
    [nav
    , div [] [list model.tokens]
    , div [] [ button [onClick (FetchDatabasePrice) ] [text "Fetch Price"] ]
    ]


------BROKEN INTO PIECES---------


nav : Html Msg
nav = div [Bass.style
            [center
            , Bass.h1
            , [("background-color", "black")
            , ("color", "white")
              ]
            ]
           ]
           [ div [] [text "Crypto Nutshell"]]

list : List Token -> Html Msg
list tokens =
  div [Bass.style
        [Bass.left_align
        ]
      ]
    [div [ class "p1"]
       [ table []
          [ thead []
             [ tr []
                [ th [] [text "Name"]
                , th [] [text "Holdings"]
                , th [] [text "Price"]
                , th [] [text "Actions"]
                ]
             ]
             , tbody [] (List.map tokenRow tokens)
          ]
        ]
      ]

tokenRow : Token -> Html Msg
tokenRow token =
  tr []
      [ td [] [ text token.name ]
      , td [] [ text token.holdings ]
      , td [] [ text token.price ]
      ]

这是我的错误:

-- TYPE MISMATCH ------------------------------------------------------ test.elm

The definition of `update` does not match its type annotation.

50| update : Msg -> Model -> (Model, Cmd Msg)
51| update msg model =
52|>  case msg of
53|>    FetchDatabasePrice ->
54|>      (model, getPrice )
55|>    FetchLivePrice (Ok newPrice) ->
56|>      ( { model | price = newPrice }, Cmd.none )
57|>    FetchLivePrice (Err _) ->
58|>      (model,Cmd.none)

The type annotation for `update` says it always returns:

    ( Model, Cmd Msg )

But the returned value (shown above) is a:

    ( { b | price : String }, Cmd Msg )

Hint: The record fields do not match up. One has tokens. The other has price.



-- TYPE MISMATCH ------------------------------------------------------ test.elm

`model` is being used in an unexpected way.

56|       ( { model | price = newPrice }, Cmd.none )
              ^^^^^
Based on its definition, `model` has this type:

    Model

But you are trying to use it as:

    { b | price : a }

Hint: The record fields do not match up. One has tokens. The other has price.

【问题讨论】:

    标签: http functional-programming elm


    【解决方案1】:

    类型错误从根本上告诉您您的问题 - 您正在尝试处理 Token,但您没有 - 您有一个 Model

    我们如何从一个到另一个?出色地。我们从一个模型开始,我们可以通过model.tokens 得到一个List Token。然后我们要修改该列表以包含更新的新令牌。执行此操作的正常方法是使用List.map。这对每个Token 进行操作,并为我们提供更新的列表。执行以下步骤:

    FetchLivePrice (Ok newPrice) ->
      let
        updatePrice = (\token -> { token | price = newPrice })
        updated = List.map updatePrice model.tokens
      in
        ({ model | tokens = updated }, Cmd.none )
    

    现在,我给出的解决方案是一个简单的解决方案,当您有多个不同的令牌时会崩溃(它们都会同时更改)。由于您现在只有一个,因此只需将模型简化为仅采用单个令牌而不是列表即可实现相同的目的。

    如果您希望能够使用拥有多个代币的功能,您将需要开始确定您获得的价格是哪个代币,以便更新正确的代币。

    实际上,您可能希望它最终看起来像:

    FetchLivePrice tokenId (Ok newPrice) ->
        ({ model | tokens = tokenUpdatePrice tokenId newPrice model.tokens, Cmd.none)
    

    tokenUpdatePrice 是一个函数,它可以操作您的列表(或其他数据结构 - 字典可能是合适的,尽管您可能需要存储单独的顺序以进行演示)以更新相应的记录。 tokenId 将用于识别令牌。

    【讨论】:

    • 谢谢,这很有意义并且可以编译。但是,当我发送 FetchDatabasePrice Msg 时,我的视图不会更新。
    • @Jimbo 我的猜测是您的请求失败(该 api 的结果是一个列表,但您将其视为一个对象)并且您正在丢弃错误 - 尝试使用调试器查看发生了什么或存储错误并将其显示在视图中。
    • 这是我注意到的第一件事,JSON 以列表的形式返回。即使我为获取请求输入了错误的链接,也没有任何反应。谢谢,我会找到一个调试指南。
    • 是一个 CORS 问题。我刚刚更改了我正在查询的 api,它可以工作。谢谢@Gareth Latty
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-07
    相关资源
    最近更新 更多