Any comment on this so far would already be appreciated. Now onto the question …
I’m using halogen and i have the modal as halogen types for the view. However the new() method i made requires an HTMLElement (because the underlying javascript code requires an element which is the html of the model). To be able to reference this HTMLElement between view renders (because i need it to call actions on it), i need to put it in my Application state. Right now the new function returns type Modal which is a placeholder for the moment, but i need access to HTMLElement somehow it seems. Following a straight forwarded approach leads me to this design.
This feels very strange to me having an HTMLElement in my application State. I feel like maybe i should wrap it in some special type and then let the HTMLElement be hidden somehow. Perhaps it shouldn’t even be stored in the purescript side but be hidden somehow in the javascript code.
What’s the best way to model this modal? And keep good separation between state and view?
You can attach a HP.ref property to the element and then acquire the element in HalogenM with getHTMLElementRef. Sometime it does make sense to have it in the State, but otherwise you could just re-acquire it with getHTMLElementRef each time you need to use it.
You can set up an initialize action in the mkEval options record - this action will be raised inside the component immediately after it has been rendered with the initialState (since rendering is strict we can’t wait for an action to complete here, so often this means you’ll end up with a Maybe in your state for the element or other-effectful-thing you need to set up in component init).
Hi @garyb i understood the first part that rendering needs to be done once before we can call an action on the DOM elements.
I looked at the ACE example and implemented the initialize action successfully. I noticed that in the Ace example the editor is stored in the application state. At the moment i am not doing this and instead try to grab the reference again.
getModalElem :: forall surface action slots output m. H.HalogenM surface action slots output m HTMLElement
getModalElem = (unsafePartial $ fromJust) <$> (getHTMLElementRef $ RefLabel "modal")
handleAction ∷ forall o m. MonadEffect m => Action → H.HalogenM State Action () o m Unit
handleAction = case _ of
Initialize -> do
modal_elem <- getModalElem
H.liftEffect $ BSM.new modal_elem Set.empty
pure unit
OpenModal -> trace "open modal" \_ -> do
modal_elem <- getModalElem
H.liftEffect $ BSM.show2 modal_elem
pure unit
And then in the FFI code
exports.new = (element) => {
return (options) => {
return () => {
let modal = new bsn.Modal(element, options);
console.log(modal)
modal.show()
return {}
}
}
};
exports.show2 = modal_elem => {
console.log(modal_elem); // ok got an HTMLElement here
return () => {
console.log(modal_elem); // undefined
modal.show();
}
}
This code opens the modal on page load (as a test). But then when clicking a button to show the modal the modal element is undefined at the point when the effect is executed.
I think that like in the ACE example i should store the javascript object that is constructed from the HTMLElement. So return modal; instead of return {};, otherwise i have to construct this again later and possibly have a memory leak.
However i’m still puzzled why the HTMLElement is no longer available in the inner function of show2(). I feel like this could also pose a problem later when i would store the modal into the application state and the underlying HTMLElement disappears.
At the moment i don’t understand why the HTMLElement is no longer there as a i attached the reference to it. Does the reference not guarantee that the HTMLElement is not destroyed? Or should i grab the reference again from the javascript side of things?
handleAction ∷ forall o m. MonadEffect m => Action → H.HalogenM State Action () o m Unit
handleAction = case _ of
Initialize -> do
modal_elem <- getModalElem
modal <- H.liftEffect $ BSM.new modal_elem (Set.fromFoldable [BSM.Keyboard true, BSM.Backdrop BSM.BD_True])
H.modify_ (_ { modal = Just modal })
pure unit
OpenModal -> trace "open modal" \_ -> do
-- No longer grabbing the reference:
-- modal_elem <- getModalElem
-- H.liftEffect $ BSM.show2 modal_elem
-- Instead using the application state:
modal <- (unsafePartial $ fromJust) <$> (H.gets _.modal)
H.liftEffect $ BSM.show2 modal
pure unit
This didn’t improve the situation, the effect no longer has access to the modal. @garyb i would really appreciate any hints you could give me. I’m stuck on this issue for continuing the project.