I was strugling to make Custom Elements hot reloadable as the CustomElementRegistry
does not allow redefinitions for a single tag. It’s basically a follow up to the topic how-to-create-custom-elements-web-components-with-clojurescript that I thought was worth documenting here.
If you call CustomElement.define
twice for the same tag it’ll throw the error
Uncaught DOMException: Failed to execute 'define' on 'CustomElementRegistry': the name "k-component" has already been used with this registry
Here is my solution.
(defonce already-defined-components (atom {})) ;; This is used for hot reloading.
(defn webcomponent! [name view-component]
(if (js/window.customElements.get name)
(swap! already-defined-components #(assoc % name view-component))
(let [set-shadow #(do (set! (.-shadow %) (.attachShadow % #js {:mode "open"}))#
_ (swap! already-defined-components #(assoc % name view-component))
render #(do (reagent.dom/render [(@already-defined-components name)] (.-#
;; defines the constructor function, which is the "class" object used by#
component (fn component []
(-> (js/Reflect.construct js/HTMLElement #js [] component)
set-shadow
render))]
(set! (.-prototype component)
;; establishes prototype hierarchy
(js/Object.create (.-prototype js/HTMLElement) #js {}))
;;finally, defines the component with these values
(js/window.customElements.define name component))))
(defn m-component []
[:div
[:style ;; Scoped style
"* {color: blue;}"]
[:h1 "Ex 11"]])
(webcomponent! "k-component" m-component)
(defn my-page []
[:k-component])