【问题标题】:Combine multiple signals for one model (using Html.Events and Keyboard signals)为一个模型组合多个信号(使用 Html.Events 和键盘信号)
【发布时间】:2015-12-16 20:00:00
【问题描述】:

我是 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)

这个 sn-p 启动模型。通过使用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”的东西

【问题讨论】:

    标签: elm


    【解决方案1】:

    您发送给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
    

    【讨论】:

    • 所以,我错误地使用了Signal.merge ...但是,现在我无法添加段落。我有一个文本区域,我可以在其中插入文本。当我单击按钮时,它会将该文本添加到我创建的段落中。即使使用您的端口解决方案,我什至无法将文本数据输入到我的文本区域。那是因为 keyPressesToAction 的 _ -&gt; NoOp 覆盖了它。如何确保仅在按 Alt 时才发送信号?然后我可以简单地使用 Alt+A
    • 不看代码就很难诊断。听起来可能有其他问题。处理按键信号不应影响输入文本框。
    • 我已将您的示例(第一个代码块)复制到 online editor 上,使用“+”和“-”键不起作用。但是,单击这些按钮可以工作。可能有些地方不对劲:)
    • 看起来Keyboard.presses in Firefox 存在一个未解决的问题,应该很快就会解决。该示例在 Chrome 中运行良好。也许您的代码也可以在 Chrome 中运行?
    猜你喜欢
    • 2013-10-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-24
    • 1970-01-01
    • 2013-07-04
    • 1970-01-01
    相关资源
    最近更新 更多