如何在Elm中将操作传递给子组件
我只试了几天Elm,遇到了一个我无法理解的常见场景 我有一个包含项目列表的父组件。为了呈现列表,我有一个子组件,这样父级调用传递地址和模型的子级的view函数。由于动作类型不同,我想编译器会对此抱怨,但我真的不确定 父组件:如何在Elm中将操作传递给子组件,elm,Elm,我只试了几天Elm,遇到了一个我无法理解的常见场景 我有一个包含项目列表的父组件。为了呈现列表,我有一个子组件,这样父级调用传递地址和模型的子级的view函数。由于动作类型不同,我想编译器会对此抱怨,但我真的不确定 父组件: type alias Model = List ToDoItem.Model type Action = Remove Int update : Action -> Model -> Model update action model = case
type alias Model = List ToDoItem.Model
type Action
= Remove Int
update : Action -> Model -> Model
update action model =
case action of
Remove id ->
List.filter (\todo -> todo.id /= id) model
view : Signal.Address Action -> Model -> Html
view address model =
let
buildToDos =
List.map (ToDoItem.view address) model
in
div [] [ buildToDos ]
type alias Model =
{ id : Int
, name : String
, description : String
, complete: Bool
}
type alias ID = Int
type Action
= Toggle Bool
update : Action -> Model -> Model
update action model =
case action of
Toggle toggle ->
if toggle == True then
{ model | complete = False }
else
{ model | complete = True }
view : Signal.Address Action -> Model -> Html
view address model =
let
toggleText : Bool -> String
toggleText complete =
case complete of
True -> "Incomplete"
False -> "Complete"
in
div
[ class "wrapper" ]
[ span [] [ text ("[" ++ toString model.id ++ "]") ]
, span [ class "name" ] [ text model.name ]
, div [ class "description" ] [ text model.description ]
, a [ onClick address (Toggle model.complete)] [ text (toggleText model.complete)]
]
子组件:
type alias Model = List ToDoItem.Model
type Action
= Remove Int
update : Action -> Model -> Model
update action model =
case action of
Remove id ->
List.filter (\todo -> todo.id /= id) model
view : Signal.Address Action -> Model -> Html
view address model =
let
buildToDos =
List.map (ToDoItem.view address) model
in
div [] [ buildToDos ]
type alias Model =
{ id : Int
, name : String
, description : String
, complete: Bool
}
type alias ID = Int
type Action
= Toggle Bool
update : Action -> Model -> Model
update action model =
case action of
Toggle toggle ->
if toggle == True then
{ model | complete = False }
else
{ model | complete = True }
view : Signal.Address Action -> Model -> Html
view address model =
let
toggleText : Bool -> String
toggleText complete =
case complete of
True -> "Incomplete"
False -> "Complete"
in
div
[ class "wrapper" ]
[ span [] [ text ("[" ++ toString model.id ++ "]") ]
, span [ class "name" ] [ text model.name ]
, div [ class "description" ] [ text model.description ]
, a [ onClick address (Toggle model.complete)] [ text (toggleText model.complete)]
]
编译器错误:
-- TYPE MISMATCH ----------------------------------------------
The type annotation for `view` does not match its definition.
20│ view : Signal.Address Action -> Model -> Html
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The type annotation is saying:
Address Action -> List ToDoItem.Model -> Html
But I am inferring that the definition has this type:
Address ToDoItem.Action -> List ToDoItem.Model -> Html
-- TYPE MISMATCH ----------------------------------------------
The 2nd argument to function `div` is causing a mismatch.
26│ div [] [ buildToDos ]
^^^^^^^^^^^^^^
Function `div` is expecting the 2nd argument to be:
List VirtualDom.Node
But it is:
List (List Html)
如何正确声明子组件的视图函数,或者如何从父组件将参数正确地传递给子组件的视图函数
实际上,我并不想将任何操作传递给子组件——我只想渲染它。子组件中的操作用于onClick
再说一次,也许是因为这是我的榆树生活的第二天,所以我走得太远了。涵盖了儿童观问题。再看看示例4是如何实现的
简言之,您需要在父级中有一个动作来包装子动作:
type Action = Remove Int | ToDo Int ToDoItem.Action
您需要将此操作转发到update
中的相应项目
在视图中
需要为每个ToDoItem视图创建转发地址
view : Signal.Address Action -> Model -> Html
view address model =
let
fwd idx = Signal.forwardTo address (ToDo idx)
toDoList =
List.indexedMap (\(idx, m) -> ToDoItem.view (fwd idx) m) model
in
div [] toDoList
请注意,您以前的
buildToDos
已经是一个列表,并且说[buildToDos]
实际上是说list(list Html)
,这就是您出现第二个错误的原因 编译器已经告诉你答案了。首先是
地址操作->列表到文档模型->Html
此处的操作必须指定给子操作。就像编译器告诉您的那样修复它:
视图:Signal.Address TodoItem.Action->Model->Html
第二个,因为您的buildToDos
已经是一个列表,所以您只需要:
div[]buildToDos
花一些时间理解类型注释并仔细遵循编译器,然后您应该能够自己解决此类问题 我以前确实试过这个。更改该类型签名的问题是,我正在使用start app,并且start app希望Signal.Address的类型为List ToDoItem.Model.Thank。这个例子我已经读了很多遍,但它似乎不是我所需要的。我实际上不需要将信号转发给子组件,因为它不需要知道有关父操作的任何信息。我能不能用一些noOp之类的东西来称呼child view fn?我能够根据这个答案来解决这个问题。我刚刚创建了一个与子组件操作关联的NoOp操作-与您的操作类似,我刚刚删除了索引,因为我不需要它。然后我就通过Signal.forwardTo发送了。我仍然不清楚的是信号是如何流动的。每个父子组件关系都需要这样做吗?是否始终需要转发操作?如果ToDoItem有自己的状态,并且有更改此状态的操作,则需要在视图中的更新和转发中进行此类委托。这主要是由单一状态生成的。[此图像]()直观地显示了Elm中发生的情况。对于Elm架构,人们很少使用信号。StartApp内部有一个很大的
信号。foldp
和几个输入信号,就是这样。这是一个很棒的图形,谢谢。当你说人们很少在Elm架构中使用信号时,你的意思是用动作来代替,还是说构图是平面的,通常没有传递任何信息?是的,人们使用动作。信号是随时间变化的值,使用起来非常棘手。Elm架构通过主要在控制状态的大foldp中处理时间,极大地简化了事情。应用程序的其余部分是具有常规值的常规函数,易于理解,易于编写。