问题
How to unsubscribe to keyboard events from other actions than HandleKey in the keyboard-input example? (The question is related to Halogen version 2.0.1 and purescript 0.11.4.)
In the example the enter/return works. I have a group of elements that can be collapsed with mouse by pressing close-button and Close-action takes care of it. Also, the enter/return can be used to collapse the elements and it works as it should.
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
The problem is that when I close (collapse) the elements with mouse, the event listener is not removed (not Done) and it will be still there. When I re-open the elements, the above code will establish a second (a third, fourth, etc) listener and then every keypress will be handled many times.
I was considering to create a keyboard event but it doesn't sound a correct way here.
So the questions are:
- How to check if there is already a keyboard listener?
- And then how to stop it at the Close -action?
- Or is it possible to make the Done SubscribeStatus and send it to the keyboard listener at the Close -action?
Further questions:
- The example has
unsubscribe
in the state. How to use it? - (I tried to put something into it at the Init -action and at the Close -action but it was pure guessing.)
Late edit: This seems to be related to questions about dynamically attached event listeners and see also this, this and this.
One of the answers said that it is only possible to know about dynamic handlers through the use of states and that did the trick for my problem. Anyhow, I think that the questions stated here remain (how to remove handler with Halogen and especially about the unsubscribe field in the example - is it somehow useable?)
回答1:
It took me a while to understand the eventSource' signature, but here's a breakdown that might help clarify what's going on.
This is the actual signature...
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
If we use a sort of pseudocode to name the components of the signature...
type Callback a = (a -> Eff (avar :: AVAR | eff) Unit)
type MaybeQuery a = (a -> Maybe (f SubscribeStatus))
type RemoveEventListener = (Eff (avar :: AVAR | eff) Unit)
We get...
eventSource' :: forall f m a eff.
(Callback a -> Eff (avar :: AVAR | eff) RemoveEventListener) ->
MaybeQuery a ->
EventSource f m
I think the part you're missing is the RemoveEventListener part. Here's an example EventSource for onMouseUp on an HTMLDocument that doesn't use the foreign function interface (FFI) like the example you referenced.
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
So the Callback will get the a
event (In this case a generic Event
). Note that we have to create a reference to the listener, so we can pass the same reference to addEventListener as removeEventListener (the function reference acts like an id to determines which listener function is removed).
The MaybeQuery function will also be passed the a
event, so Halogen can execute a query and you can decide if you should keep listening for events or not. That query will need to be structured like this...
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)
And just show how to subscribe to the 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)
来源:https://stackoverflow.com/questions/44343300/halogen-keyboard-input-example-and-unsubsribing-to-the-events