I’ve been doing workarounds like calling document.querySelector()
via FFI, but this came up once and keeps coming back again, so I’m wondering isn’t there a proper solution?
Given the following dialog implementation taken from here:
<button onclick="this.nextElementSibling.showModal()">Test</button>
<dialog style="padding: 0" onclick="event.target==this && this.close()">
<form style="padding: 1rem" method="dialog">
<p>Click outside to close the dialog.</p>
<button>Click here</button>
</form>
</dialog>
I want to implement the onclick="event.target==this && this.close()"
code.
Now, in Halogen design onClick
triggers a handleAction
where I’d have to execute the effects of event.target==this && this.close()
. But inside handleAction
the this
refers to whole window and not to the component being rendered. So how you supposed to handle such reference?
Not sure if there is something to get this
inbuild, but I’d go with RefLabel
s and just pass the label along as a first parameter to your Event/Handler. Then you can get the element from the label in HalogenM
/handler via getRef
or getHTMLElementRef
(just bellow)
1 Like
I’m trying to use RefLabel
but I can’t seem to replicate the expected HTML + JS code.
The TL;DR is I have this:
…
focusRef :: H.RefLabel
focusRef = H.RefLabel "mymodal"
…
dialog :: H.ComponentHTML DialogAction () m
dialog = HH.dialog [HP.style "padding: 0", HP.ref focusRef, HE.onClick \ev -> CloseDialog ev]
…
handleAction :: DialogAction -> H.HalogenM DialogState DialogAction () Unit m Unit
handleAction (CloseDialog ev) =
H.getHTMLElementRef focusRef >>= traverse_ \elem -> H.liftEffect $ closeDialogIfItsTarget ev elem
…so basically, in dialog
rendering I 1. assign it a ref
and 2. assign a onClick
that saves the event value.
Then during the click handling I call closeDialogIfItsTarget ev elem
which is supposed to mimic the event.target==this && this.close()
and looks like this:
FFI.js
:
"use strict";
export const closeDialogIfItsTarget = function(mouseEvent, dialogRef) {
return function () {
if (mouseEvent.target === dialogRef)
dialogRef.close();
};
};
FFI.purs
:
module FFI where
import Web.HTML.HTMLElement
import Effect (Effect)
import Prelude (Unit)
import Web.UIEvent.MouseEvent (MouseEvent)
foreign import closeDialogIfItsTarget :: MouseEvent -> HTMLElement -> Effect Unit
Well, the close()
never gets executed. If I add console.log()
of the parameters, the dialogRef
is undefined
! But it isn’t in the PureScript code! If I replace closeDialogIfItsTarget ev elem
with closeDialogIfItsTarget ev (spy "dbg: " elem)
, I can perfectly see the elem
has value
Oh, I figured it out, it’s just that I didn’t know that multiple-params FFI functions should be embedded into one another due to carrying. So with this change it works as expected:
export const closeDialogIfItsTarget = function(mouseEvent) {
return function (dialogRef) {
return function () {
if (mouseEvent.target === dialogRef)
dialogRef.close();
}
};
};
So, to recap: this
isn’t immediately available in handleAction
, but you can add HP.ref …
property to an element, and then retrieve it with H.getHTMLElementRef
inside handleAction
, which when passed via FFI will be same as this
would.
And then, the event.target
from the JS code can be extracted from the parameter to the onClick
lambda. On the PureScript side target
isn’t exported (I sent a PR), but if you pass this param via FFI, the target
field will be there.
Regarding how to use ref
s, I was referred to this example which is what I used.
2 Likes