为一个模型组合多个信号(使用Html.Events和键盘信号)

时间:2015-12-16 20:00:01

标签: elm

我是榆树的新人。我正在试验它时发现这种语言。虽然信号的使用看起来很复杂。下面是我的网页的一个工作示例,它处理多个段落(添加,删除,编辑,......)。

-- start
main: Signal Html.Html
main = Signal.map (view actions.address) model

view: (Signal.Address Action) -> Model -> Html
view address model = ... (displaying paragraphs with buttons, ect)

此代码段启动模型。通过使用Signal.map将操作映射到模型,我能够处理按钮中的点击事件(导入的Html +事件模块)

model: Signal Model
model = Signal.foldp update makeEmptyModel actions.signal

在这里,我从一个空模型开始。 update功能允许我在按钮上单击事件后更新模型。

update: Action -> Model -> Model
update action model = ....

Action是一种处理我已定义的多个动作的类型,例如" AddParagraph"," RemoveParagraph" ...

到目前为止这是有效的。现在,在检查包裹时,我找到了Keyboard。这看起来很有趣,所以我想添加一个新功能。如果用户按下Alt + A,则会选择所有段落。 (如文件浏览器中的Ctrl + A)

但似乎并不容易将其与我现有的信号映射结合起来。我决定深入研究Signal并找到Signal.merge。所以我可以使用它吗?我将键盘信号合并到当前信号的尝试是

main = Signal.merge
    (Signal.map (view actions.address) model)
    (Signal.map (view actions.address) keysDown)

(keysDown来自import Keyboard exposing (keysDown) import)

但这并不奏效。我收到了下一个错误

The 2nd argument to function `map` is causing a mismatch.
160│      Signal.map (view actions.address) keysDown)
                                            ^^^^^^^^
Function `map` is expecting the 2nd argument to be:
Signal { focused : Bool, selected : Bool, ... }

But it is:
    Signal (Set.Set Char.KeyCode)

看来,当使用Signal.merge时,它需要多个处理相同输出的信号。那就是我想要的。但这似乎并不起作用。

问题:如何将键盘信号添加到当前设计中?

或者我对Signal.merge的期望是否错误?我是否将Signal.merge用于错误目的?我应该使用Signal.map2吗?如果是这样,我如何在当前示例*中使用它?或者有更好的方法吗?

  • 如果解决方案是map2,您可以添加解释吗?那是因为我不明白" map2"东西

1 个答案:

答案 0 :(得分:3)

您发送给merge的信号必须属于同一类型。在合并之前,您需要添加另一个将键盘输入映射到Action的函数。

有几种方法可以实现这一目标。以下是使用基本计数器示例和Signal.merge的示例。重要的是使用keyPressesToAction信号映射函数

import Signal
import Html exposing (..)
import Html.Events exposing (..)
import Keyboard
import Char

type Action = NoOp | Increment | Decrement

actions : Signal.Mailbox Action
actions =
  Signal.mailbox NoOp

update action model =
  case action of
    NoOp -> model
    Increment -> model + 1
    Decrement -> model - 1

model =
  Signal.foldp update 0 (Signal.merge actions.signal keyPressesToAction)

keyPressesToAction =
  let
    keyCodeToAction keyCode =
      case Char.fromCode keyCode of
        '+' -> Increment
        '-' -> Decrement
        _ -> NoOp
  in
    Signal.map keyCodeToAction Keyboard.presses

main =
  Signal.map (view actions.address) model

view address model =
  div []
    [ button [ onClick address Decrement ] [ text "-" ]
    , text <| toString model
    , button [ onClick address Increment ] [ text "+" ]
    ]

实现所需结果的另一种方法是使用port专用于将按键转换为action信号。这消除了合并信号的需要,因为它反而导致按键触发您已经设置的邮箱。

在这种情况下,model函数会恢复原样,然后我们添加名为triggerActionOnKeyPress的端口,它使用上面相同的keyPressesToAction信号映射函数。以下是相关部分:

model =
  Signal.foldp update 0 actions.signal

port triggerActionOnKeyPress : Signal (Task.Task Effects.Never ())
port triggerActionOnKeyPress =
  Signal.map (Signal.send actions.address) keyPressesToAction