如何在Elm中将操作传递给子组件

如何在Elm中将操作传递给子组件,elm,Elm,我只试了几天Elm,遇到了一个我无法理解的常见场景 我有一个包含项目列表的父组件。为了呈现列表,我有一个子组件,这样父级调用传递地址和模型的子级的view函数。由于动作类型不同,我想编译器会对此抱怨,但我真的不确定 父组件: type alias Model = List ToDoItem.Model type Action = Remove Int update : Action -> Model -> Model update action model = case

我只试了几天Elm,遇到了一个我无法理解的常见场景

我有一个包含项目列表的父组件。为了呈现列表,我有一个子组件,这样父级调用传递地址和模型的子级的view函数。由于动作类型不同,我想编译器会对此抱怨,但我真的不确定

父组件:

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中处理时间,极大地简化了事情。应用程序的其余部分是具有常规值的常规函数,易于理解,易于编写。