卤素键盘输入示例和取消对事件的取消?

时间:2017-06-03 11:14:12

标签: purescript halogen

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

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

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 -action停止它?
  • 或者是否可以制作完成的SubscribeStatus并将其发送到Close -action的键盘监听器?

进一步的问题:

  • example州内有unsubscribe。如何使用它?
  • (我试图在Init -action和Close -action中加入一些内容,但这只是猜测。)

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

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

1 个答案:

答案 0 :(得分: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

因此Callback将获得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)