如图所示,我创建了一个小样本程序,其中包含一些控件来修改模型的各个部分。
我尝试做的不成功的是发出HTTP请求以获取初始数据(现在它已经硬编码),或者稍后用来自所述HTTP请求的响应替换数据收到重置消息。我确实阅读了榆树介绍的the HTTP chaper,但我似乎无法把事情拼凑起来。
目标是有一个 loadTraits 函数,它接受一个String( SomeId )并返回一个类型为 TraitWithRelevance 的List,所以我可以用传入的数据替换模型。
module Main exposing (..)
import Html exposing (Html, button, div, text, input, ul, img)
import Html.Attributes as Attr
import Html.Events exposing (onClick, onInput)
import Http exposing (..)
import Json.Decode as Decode
main =
Html.program
{ init = init
, view = view
, update = update
, subscriptions = \_ -> Sub.none
}
-- MODEL
type alias ContentWithTraits =
{ someId : SomeId
, traits : List TraitWithRelevance
}
type alias MetaInfo =
{ name : String
, imageUrl : String
}
type alias Name =
String
type alias SomeId =
String
type alias Relevance =
String
type alias TraitWithRelevance =
( Name, SomeId, Relevance )
type TraitToAdd
= Nothing
| TraitWithRelevance
type alias Model =
{ contentWithTraits : ContentWithTraits
, metaInfo : MetaInfo
, traitToAdd : TraitToAdd
}
init : ( Model, Cmd Msg )
init =
( Model contentWithTraits { name = "content name", imageUrl = "http://weknowmemes.com/generator/uploads/generated/g1369409960206058073.jpg" } Nothing, Cmd.none )
contentWithTraits : ContentWithTraits
contentWithTraits =
{ someId = "some default id"
, traits =
[ ( "name for trait a", "a", "1" )
, ( "this is the name for trait b", "b", "50" )
]
}
-- UPDATE
type Msg
= EditTrait SomeId Relevance
| Reset
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
EditTrait someId relevance ->
let
_ =
Debug.log "model: " model
in
( replaceTraits model <| List.map (updateTrait ( someId, relevance ))
, Cmd.none
)
Reset ->
-- ( replaceTraits model <| loadTraits model.contentWithTraits.someId, Cmd.none )
{-
NOTE: I'm stuck here...
should make HTTP GET request, then replace the model.contentWithTraits.traits with the decoded JSON's traits field
-}
( model, Cmd.none )
replaceTraits : Model -> (List TraitWithRelevance -> List TraitWithRelevance) -> Model
replaceTraits model func =
{ model
| contentWithTraits =
{ someId = model.contentWithTraits.someId
, traits = func model.contentWithTraits.traits
}
}
updateTrait : ( SomeId, Relevance ) -> TraitWithRelevance -> TraitWithRelevance
updateTrait updatedTrait originalTrait =
let
( name, someId, _ ) =
originalTrait
( someIdVerification, newValue ) =
updatedTrait
_ =
Debug.log "updatedTrait: " updatedTrait
in
if someId == someIdVerification then
( name, someId, newValue )
else
originalTrait
-- VIEW
valueRange : String -> TraitWithRelevance -> Html Msg
valueRange typ trait =
let
( name, someId, relevance ) =
trait
in
input [ Attr.type_ typ, Attr.min <| toString 0, Attr.max <| toString 100, Attr.value relevance, Attr.step <| toString 1, onInput <| EditTrait someId ] []
traitView : TraitWithRelevance -> Html Msg
traitView trait =
let
( name, someId, relevance ) =
trait
in
div []
[ text someId
, valueRange "range" trait
, valueRange "number" trait
, text name
]
view : Model -> Html Msg
view model =
div []
[ text model.contentWithTraits.someId
, img [ Attr.src model.metaInfo.imageUrl, Attr.width 300 ] []
, ul [] (List.map traitView model.contentWithTraits.traits)
, button [ onClick Reset ] [ text "Reset" ]
]
这是来自http服务器的示例响应。我选择了这种格式,因为我认为它最容易映射到榆树模型。如果有更好的方法在elm中使用这些数据,我可以轻松更改响应。
{"traits":[["name for trait a","a",1],["this is the name for trait b,"b",50]]}
P.S。即使有大量代码,请注意我尽可能地解决问题,同时保持足够的上下文。
答案 0 :(得分:1)
您需要有一条返回数据的消息。假设它是Trait
s:
type Msg
= EditTrait SomeId Relevance
| Reset
| OnFetchTraits (Result Http.Error (List Traits))
然后你需要一个发送请求的命令,比如
fetchTraits : Cmd Msg
fetchTraits =
Http.get "http://localhost:4000/traits" traitListDecoder
|> Http.send OnFetchTraits
并且您需要实现traitListDecoder
,将您的JSON解码为msg中返回的列表。
然后,您返回Cmd.none
而不是在update
函数中返回fetchTraits
。然后,Elm将发出请求,您将OnFetchTraits
msg传递到update
。您需要一个单独的案例来处理这个问题。如果请求成功,则解压缩Result
类型并提取数据,否则处理错误。
答案 1 :(得分:1)
所以基本上你需要做两件事,重置按钮应该调用命令来获得特征。然后,在更新中,您必须处理命令的响应。获得结果后,您可以使用它来更新模型。
以下是您的代码的更新。我在模型中添加了一个人,当用户按下重置按钮时,该模型会更新。
module Main exposing (..)
import Html exposing (Html, button, div, text, input, ul, img)
import Html.Attributes as Attr
import Html.Events exposing (onClick, onInput)
import Http exposing (..)
import Json.Decode exposing (Decoder, string)
import Json.Decode.Pipeline exposing (decode, required)
main =
Html.program
{ init = init
, view = view
, update = update
, subscriptions = \_ -> Sub.none
}
-- Commands
type alias Person =
{ name : String
, gender : String
}
decodePerson : Decoder Person
decodePerson =
decode Person
|> required "name" string
|> required "gender" string
getTraits =
let
url =
"http://swapi.co/api/people/1/"
request =
Http.get url decodePerson
in
Http.send GetTraitResponse request
-- MODEL
type alias ContentWithTraits =
{ someId : SomeId
, traits : List TraitWithRelevance
}
type alias MetaInfo =
{ name : String
, imageUrl : String
}
type alias Name =
String
type alias SomeId =
String
type alias Relevance =
String
type alias TraitWithRelevance =
( Name, SomeId, Relevance )
type TraitToAdd
= Nothing
| TraitWithRelevance
type alias Model =
{ contentWithTraits : ContentWithTraits
, metaInfo : MetaInfo
, traitToAdd : TraitToAdd
, person : Person
}
init : ( Model, Cmd Msg )
init =
( Model contentWithTraits { name = "content name", imageUrl = "http://weknowmemes.com/generator/uploads/generated/g1369409960206058073.jpg"} Nothing {name = "", gender=""}, Cmd.none )
contentWithTraits : ContentWithTraits
contentWithTraits =
{ someId = "some default id"
, traits =
[ ( "name for trait a", "a", "1" )
, ( "this is the name for trait b", "b", "50" )
]
}
-- UPDATE
type Msg
= EditTrait SomeId Relevance
| GetTraitResponse (Result Http.Error Person)
| Reset
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
EditTrait someId relevance ->
let
_ =
Debug.log "model: " model
in
( replaceTraits model <| List.map (updateTrait ( someId, relevance ))
, Cmd.none
)
-- handle the response
GetTraitResponse resp ->
let
_ =
Debug.log "response" resp
person =
case resp of
Ok val ->
val
Result.Err e ->
{name = "", gender=""}
in
( {model | person = person }, Cmd.none )
Reset ->
-- ( replaceTraits model <| loadTraits model.contentWithTraits.someId, Cmd.none )
{-
NOTE: I'm stuck here...
should make HTTP GET request, then replace the model.contentWithTraits.traits with the decoded JSON's traits field
-}
-- call the command to get the traits
( model, getTraits )
replaceTraits : Model -> (List TraitWithRelevance -> List TraitWithRelevance) -> Model
replaceTraits model func =
{ model
| contentWithTraits =
{ someId = model.contentWithTraits.someId
, traits = func model.contentWithTraits.traits
}
}
updateTrait : ( SomeId, Relevance ) -> TraitWithRelevance -> TraitWithRelevance
updateTrait updatedTrait originalTrait =
let
( name, someId, _ ) =
originalTrait
( someIdVerification, newValue ) =
updatedTrait
_ =
Debug.log "updatedTrait: " updatedTrait
in
if someId == someIdVerification then
( name, someId, newValue )
else
originalTrait
-- VIEW
valueRange : String -> TraitWithRelevance -> Html Msg
valueRange typ trait =
let
( name, someId, relevance ) =
trait
in
input [ Attr.type_ typ, Attr.min <| toString 0, Attr.max <| toString 100, Attr.value relevance, Attr.step <| toString 1, onInput <| EditTrait someId ] []
traitView : TraitWithRelevance -> Html Msg
traitView trait =
let
( name, someId, relevance ) =
trait
in
div []
[ text someId
, valueRange "range" trait
, valueRange "number" trait
, text name
]
view : Model -> Html Msg
view model =
div []
[ text model.contentWithTraits.someId
, img [ Attr.src model.metaInfo.imageUrl, Attr.width 300 ] []
, ul [] (List.map traitView model.contentWithTraits.traits)
, button [ onClick Reset ] [ text "Reset" ]
, text <| toString model
]
答案 2 :(得分:0)
虽然这两个答案都是正确且有帮助的,但我还有一英里到达目的地。
首先,我并不想依赖非琅琅包。 因此,Json.Decode.Pipeline不能满足这一要求。
通过发布的示例,我仍然有解码特征列表的麻烦。我最终做的是更改服务器的响应,以便traits列表不是类型列表,而是类型为object的对象,someId和相关性作为键,其值作为字符串。这有助于我区分从服务器返回的内容以及我的榆树模型内部代表的内容。
因此,重置功能可以很好地工作,下一步是在没有用户交互的情况下将这些值放入模型的初始状态。
module Main exposing (..)
import Html exposing (Html, button, div, text, input, ul, img)
import Html.Attributes as Attr
import Html.Events exposing (onClick, onInput)
import Http exposing (..)
import Json.Decode as Decode exposing (Decoder)
main =
Html.program
{ init = init
, view = view
, update = update
, subscriptions = \_ -> Sub.none
}
-- MODEL
type alias ContentWithTraits =
{ someId : SomeId
, traits : List TraitWithRelevance
}
type alias MetaInfo =
{ name : String
, imageUrl : String
}
type alias Name =
String
type alias SomeId =
String
type alias Relevance =
String
type alias TraitWithRelevance =
( Name, SomeId, Relevance )
type TraitToAdd
= Nothing
| TraitWithRelevance
type alias Model =
{ contentWithTraits : ContentWithTraits
, metaInfo : MetaInfo
, traitToAdd : TraitToAdd
}
type alias TraitInfo =
{ traits :
List TraitObject
}
type Traits
= TraitInfoFromServer (List TraitObject)
type alias TraitObject =
{ name : String, someId : String, relevance : String }
fetchTraits : String -> Cmd Msg
fetchTraits someId =
Http.get
("http://localhost:8000/traits/" ++ someId)
decodeTraits
|> Http.send OnFetchTraits
decodeTraits : Decoder TraitInfo
decodeTraits =
Decode.map TraitInfo
(Decode.field "traits" (Decode.list decodeTrait))
decodeTrait : Decoder TraitObject
decodeTrait =
(Decode.map3 TraitObject
(Decode.field "name" Decode.string)
(Decode.field "someId" Decode.string)
(Decode.field "relevance" Decode.string)
)
init : ( Model, Cmd Msg )
init =
( Model contentWithTraits { name = "content name", imageUrl = "http://weknowmemes.com/generator/uploads/generated/g1369409960206058073.jpg" } Nothing, Cmd.none )
contentWithTraits : ContentWithTraits
contentWithTraits =
{ someId = "someIdToStartWith"
, traits =
[ ( "trait a", "a", "1" )
, ( "trait b", "b", "50" )
]
}
-- UPDATE
type Msg
= EditTrait SomeId Relevance
| Reset
| OnFetchTraits (Result Http.Error TraitInfo)
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
EditTrait someId relevance ->
( replaceTraits model <| List.map (updateTrait ( someId, relevance ))
, Cmd.none
)
Reset ->
( model, fetchTraits model.contentWithTraits.someId )
OnFetchTraits resp ->
let
newTraits =
case resp of
Ok val ->
val.traits
Result.Err e ->
[]
in
( { model
| contentWithTraits =
{ someId = model.contentWithTraits.someId
, traits = List.map traitObjToTuple newTraits
}
}
, Cmd.none
)
traitObjToTuple : TraitObject -> TraitWithRelevance
traitObjToTuple obj =
( obj.name, obj.someId, obj.relevance )
replaceTraits : Model -> (List TraitWithRelevance -> List TraitWithRelevance) -> Model
replaceTraits model func =
{ model
| contentWithTraits =
{ someId = model.contentWithTraits.someId
, traits = func model.contentWithTraits.traits
}
}
updateTrait : ( SomeId, Relevance ) -> TraitWithRelevance -> TraitWithRelevance
updateTrait updatedTrait originalTrait =
let
( name, someId, _ ) =
originalTrait
( someIdVerification, newValue ) =
updatedTrait
in
if someId == someIdVerification then
( name, someId, newValue )
else
originalTrait
-- VIEW
valueRange : String -> TraitWithRelevance -> Html Msg
valueRange typ trait =
let
( name, someId, relevance ) =
trait
in
input [ Attr.type_ typ, Attr.min <| toString 0, Attr.max <| toString 100, Attr.value relevance, Attr.step <| toString 1, onInput <| EditTrait someId ] []
traitView : TraitWithRelevance -> Html Msg
traitView trait =
let
( name, someId, relevance ) =
trait
in
div []
[ text name
, valueRange "range" trait
, valueRange "number" trait
, text someId
]
view : Model -> Html Msg
view model =
div []
[ text model.contentWithTraits.someId
, img [ Attr.src model.metaInfo.imageUrl, Attr.width 100 ] []
, ul [] (List.map traitView model.contentWithTraits.traits)
, button [ onClick Reset ] [ text "Reset" ]
]