Пример ввода галогенной клавиатуры и отписка от событий?

Как отказаться от подписки на события клавиатуры от других действий, кроме 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

Проблема в том, что когда я закрываю (сворачиваю) элементы мышью, слушатель событий не удаляется (не Готово) и он все еще там. Когда я заново открываю элементы, приведенный выше код установит второго (третьего, четвертого и т. Д.) Прослушивателя, а затем каждое нажатие клавиши будет обрабатываться много раз.

Я думал о создании события клавиатуры, но здесь это звучит неправильно.

Итак, вопросы:

  • Как проверить, есть ли уже прослушиватель клавиатуры?
  • И как тогда остановить это на закрытии?
  • Или возможно создать Done SubscribeStatus и отправить его слушателю клавиатуры при действии Close?

Дальнейшие вопросы:

  • В примере есть unsubscribe в состоянии. Как это использовать?
  • (Я пытался вставить что-то в него в действии инициации и в действии закрытия, но это было чистое предположение.)

Позднее редактирование: похоже, это связано с вопросами о динамически подключенных слушателях событий, а также смотри это, это и это.

В одном из ответов говорилось, что о динамических обработчиках можно узнать только через использование состояний, и это помогло решить мою проблему. Во всяком случае, я думаю, что вопросы, изложенные здесь, остаются (как удалить обработчик с галогеном и особенно о поле отказа от подписки в примере - это как-то полезно?)

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. Вот пример EventSource для onMouseUp для HTMLDocument, который не использует интерфейс внешней функции (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 (ссылка на функцию действует как идентификатор, определяющий, какая функция слушателя удаляется).

Функция 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)
Другие вопросы по тегам