I am building a Clojurescript frontend with Re-Frame and Reagent. At the moment the frontend only contains a map which is created using Leaflet. On click on the map a new marker is set to that position. When clicking on any of those markers a popup is shown.
So far so good. Now the problem is that I want to adjust exactly this Leaflet popup to also contain a delete button, which then should be used to delete the marker again. Here is where I am having problems. The code looks like the following (boiled down to the minimum):
(setup-map!
(-> js/L
(.map "mapid")
(.setView #js [12.34 56.78] 10))]
(.on map "click" add-marker-to-map! (aget % "latlng" "lat") (aget %
"latlng" "lng"))))
(defn add-marker-to-map!
[lat long map-object]
(-> js/L
(.marker #js [lat long])
(.addTo map-object)
(.bindPopup "<div>Hello World!</div>")
This also works out. But I do not want to write inline HTML as a string. I want to write the content of the popup in hiccup syntax and then pass that in some way to ".bindPopup". I want to do this, because the on-click will contain some Clojurescript which I can not put in there if I just write the HTML inline as a string.
I tried for example with:
(.bindPopup (reagent.dom.server/render-to-string [:div "Hello World"]))
And this also works fine, but any on-click I put in there is getting lost in translation. I know that this is not working this way (see onClick handler not registering with ReactDOMServer.renderToString or React.js Serverside rendering and Event Handlers).
Therefore I want to find another way of passing my Reagent (which just wraps React) component to Leaflet so that it satisfies the interface of the popup:
setContent(<String|HTMLElement|Function> htmlContent)
(https://leafletjs.com/reference-1.3.2.html#popup-setcontent)
So basically I want to render the component into a HTMLElement and not directly into the virtual DOM. I want to call the reagent render function and do not render the result into the app, but get it returned as a HTMLElement that I can pass to Leaflet.
(reagent/render [hello] (.getElementById js/document "app"))
TLDR: How do I render a Reagent component into a HTMLelement including its on-click function so that I can hand that over to the Leaflet popup?
Any help appreciated.
You can use your own DOM container to render elements:
(def custom-parent (atom (dom/createDom "div")))
I assume you need more than just one, but for the sake of brevity I'm having one.
(defn add-marker-to-map!
[evt]
(let [evt (js->clj evt :keywordize-keys true)
{{:keys [lat lng]} :latlng} evt]
(-> js/L
(.marker #js [lat lng])
(.addTo m)
(.bindPopup (r/render [:div "Marker here"
[:br]
[:button {:on-click (fn [] (js/console.log "clicked"))} "delete me"]]
@custom-parent)))))