【问题标题】:Halogen keyboard-input example and unsubsribing to the events?卤素键盘输入示例和取消订阅事件?
【发布时间】:2017-06-03 11:14:12
【问题描述】:

如何取消订阅keyboard-input example中除HandleKey之外的其他操作的键盘事件? (问题与 Halogen 版本 2.0.1 和 purescript 0.11.4 有关。)

在示例中,输入/返回有效。我有一组元素可以通过按下关闭按钮用鼠标折叠,关闭操作会处理它。此外,enter/return 可用于折叠元素,它可以正常工作。

Open next -> do
  st <- H.get
  if not st.open
    then do
      H.modify (_ { open = true })
      eval (H.action Init)
    else pure unit
  pure next

Close next -> do
  H.modify (_ { open = false })
  st <- H.get
  case st.unsubscribe of
    Nothing -> pure unit
    -- Just smth -> H.liftAff smth
    Just _ -> H.modify (_ { unsubscribe = Nothing}) -- H.liftAff us
  pure next

Init next -> do
  H.modify (_ { open = true})
  document <- H.liftEff $ DOM.window >>= DOM.document <#> DOM.htmlDocumentToDocument
  H.subscribe $ ES.eventSource'
    (K.onKeyUp document)
    (Just <<< H.request <<< HandleKey)
  -- H.modify (_ { unsubscribe = ??? })
  pure next

问题是当我用鼠标关闭(折叠)元素时,事件侦听器没有被删除(没有完成)并且它仍然存在。当我重新打开元素时,上面的代码将建立第二个(第三个、第四个等)侦听器,然后每次按键都会被处理多次。

我正在考虑创建一个键盘事件,但在这里听起来不正确。

所以问题是:

  • 如何检查是否已经有键盘监听器?
  • 然后如何在 Close 动作中停止它?
  • 或者是否可以制作 Done SubscribeStatus 并将其发送到 Close -action 的键盘侦听器?

其他问题:

  • example 在状态中有unsubscribe。如何使用它?
  • (我尝试在 Init -action 和 Close -action 中添加一些内容,但纯属猜测。)

后期编辑: 这似乎与有关dynamically attached event listeners 的问题有关,另请参阅thisthisthis

其中一个答案说只有通过使用状态才能了解动态处理程序,这对我的问题起到了作用。无论如何,我认为这里所说的问题仍然存在(如何使用卤素删除处理程序,尤其是示例中的取消订阅字段 - 它是否有用?)

【问题讨论】:

  • 我实际上在弄清楚如何取消订阅事件源时遇到了同样的问题。当我找到一个好的解决方案时,我会写一个答案。

标签: purescript halogen


【解决方案1】:

我花了一些时间来理解 eventSource 的签名,但这里有一个细分可能有助于澄清发生了什么。

这是真正的签名...

eventSource' :: forall f m a eff. MonadAff (avar :: AVAR | eff) m =>
  ((a -> Eff (avar :: AVAR | eff) Unit) -> Eff (avar :: AVAR | eff) (Eff (avar :: AVAR | eff) Unit)) ->
  (a -> Maybe (f SubscribeStatus)) ->
  EventSource f m

如果我们使用一种伪代码来命名签名的组件...

type Callback a = (a -> Eff (avar :: AVAR | eff) Unit)
type MaybeQuery a = (a -> Maybe (f SubscribeStatus))
type RemoveEventListener = (Eff (avar :: AVAR | eff) Unit)

我们得到...

eventSource' :: forall f m a eff.
  (Callback a -> Eff (avar :: AVAR | eff) RemoveEventListener) ->
  MaybeQuery a ->
  EventSource f m

我认为您缺少的部分是 RemoveEventListener 部分。这是 HTMLDocument 上 onMouseUp 的示例 EventSource,它不使用您引用的示例中的外部函数接口 (FFI)。

import DOM.Event.Types as DET
import DOM.Event.EventTarget as DEET
import DOM.HTML.Event.EventTypes as EventTypes

onMouseUp :: forall e
   . DHT.HTMLDocument
  -> (DET.Event -> Eff (CompEffects e) Unit)
  -> Eff (CompEffects e) (Eff (CompEffects e) Unit)
onMouseUp doc callback = do
  let eventTarget = (DHT.htmlDocumentToEventTarget doc)

  -- Create an EventListener that will log a message
  -- and pass the event to the callback
  let listener =
        DEET.eventListener (\event -> do
          log "mouseUp"
          callback event
        )

  -- Add the EventListener to the
  -- document so it will fire on mouseup
  DEET.addEventListener
    EventTypes.mouseup
    listener true eventTarget

  -- Return the function that will later 
  -- remove the event listener
  pure $ DEET.removeEventListener
    EventTypes.mouseup
    listener true eventTarget

因此回调将获得a 事件(在本例中为通用Event)。请注意,我们必须创建对侦听器的引用,因此我们可以将相同的引用传递给 addEventListener 作为 removeEventListener(函数引用的作用就像一个 id 来确定要删除哪个侦听器函数)。

MaybeQuery 函数也将传递a 事件,因此 Halogen 可以执行查询,您可以决定是否应该继续侦听事件。该查询需要像这样构造......

data YourQuery =
  | HandleEvent Event (SubscribeStatus -> a)

eval (HandleEvent event reply) =
  -- Decided if you want to keep listening
  -- or now.

  -- Keep listening...
  -- pure $ reply (H.Listening)

  -- Stop listening.  Halogen will call the removeEventListener
  -- function you returned from onMouseUp earlier
  -- pure $ reply (H.Done)

只展示如何订阅 EventSource...

import DOM.HTML.Window as Win
import Halogen.Query.EventSource as HES

document <- H.liftEff $ (window >>= Win.document)
H.subscribe $ HES.eventSource'
  (onMouseUp document)
  (Just <<< H.request <<< HandleEvent)

【讨论】:

  • 还有一个问题:在“停止收听”评论中,回复将通过回复 H.Done 来调用 removeEventListener。是否可以直接从 eval 函数中的其他操作(查询)调用 removeEventListener?或者是否可以将 H.Done 从 mouslistener 发送到 key-listener(或以其他方式从正在侦听 mouseEvents 的处理程序中删除 keyEventListener)?如果我正确理解了 eventSource' 的类型,它似乎不会返回 removeEventListener。嗯,或者我可以只制作第一个“onMouseUp 文档”并将该侦听器放入状态记录中?
  • 我认为这是可能的,但我不确定最好的方法或者它是否是反模式。我的想法是以某种方式存储 EventSource 以便您可以在其他查​​询中访问它。然后,您可以使用 unEventSource 解开 EventSource 新类型,您将可以访问 EventSource 中的 Producer 和 done 函数。看一下 eventSource' 的实现 - 它可能会让您了解 EventSource 是如何展开并用于调用 removeEventListener。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-01-05
  • 1970-01-01
  • 2017-03-07
  • 2017-04-25
  • 1970-01-01
相关资源
最近更新 更多