榆树用http更新模型

时间:2017-12-04 19:56:19

标签: http functional-programming elm

我正在尝试在我的更新功能中更新我的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.

1 个答案:

答案 0 :(得分:4)

类型错误从根本上告诉您您的问题 - 您正在尝试使用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将用于标识令牌。