This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-05-25
Channels
- # announcements (21)
- # babashka (7)
- # beginners (27)
- # calva (7)
- # chlorine-clover (3)
- # cider (1)
- # clerk (21)
- # clojure (24)
- # clojure-europe (28)
- # clojure-finland (3)
- # clojure-nl (1)
- # clojure-norway (5)
- # clojure-uk (2)
- # clojurescript (13)
- # clr (2)
- # conjure (1)
- # consulting (1)
- # datahike (1)
- # datomic (13)
- # fulcro (3)
- # graalvm (33)
- # gratitude (7)
- # honeysql (7)
- # humbleui (12)
- # hyperfiddle (26)
- # interop (11)
- # introduce-yourself (4)
- # jobs-discuss (8)
- # lsp (26)
- # malli (6)
- # nbb (11)
- # polylith (26)
- # practicalli (1)
- # rdf (3)
- # re-frame (7)
- # reitit (10)
- # releases (2)
- # shadow-cljs (1)
- # tools-deps (15)
I’m trying to internalize the use of new
in electric. It’s not quite clicking with me when it should be used, and why, if everything in clojure is ostensibly about functions, it’s not just a function call?
for the same reason reagent uses square brackets. we could have chosen square brackets.
I remember seeing that in the docs, but this isn’t a compenent, right?
clojure is dynamically typed so there is no robust way to distinguish between reactive fn call and regular function call, and since Electric prioritizes interop with regular clojure, you need a way to indicate the call convention
db (new (db/latest-db> user/!xtdb))
ah that is the second usage of new, which is to await a missionary flow
Sounds like I need to do a little reading on missionary
awaiting a missionary flow is like awaiting a promise, except instead of a promise to 1 async future value, a flow is a sequence (possibly infinite) of async future values
That’s the streams and signals part, right?
yes, streams and signals are kinds of flows
you only need to learn missionary if you're going to write integrations at the edge of the system (e.g. bridging a database to electric, a third-party js API that returns promises, ...). Electric largely abstracts missionary away. If you need help integrating you can ask us here and we'll see if we can help
cool, thanks.
We’re currently working on an in-house app for sprint planning poker, so we can get a feel for it
I'm trying to render markdown text in electric. I'm using https://github.com/nextjournal/markdown which works in cljc and I'm not really picky if the parsing occurs on the client or server. I've tried the below approaches and a few others but not having any luck. At a high level is there an obvious approach to this? Is just parsing the markdown to an html string and setting innerHTML of the dom/node the way to go here? (Edit: I think for the markdown case here it probably is good to just use innerHTML or maybe just use a js library to handle all the parsing after the text hits the dom, but I am sort of curious about the way in electric to think about a transformation of data to nodes)
(defmacro electric-markdown1 [text]
(let [data (md/parse text)]
(walk/postwalk
(fn [x]
(if (vector? x)
(let [[tag & content] x]
(case tag
:<> `(dom/text ~@content)
(list* (symbol "hyperfiddle.electric-dom2" (name tag)) content)))
x))
(md.transform/->hiccup data))))
;; Can't take value of macro
(e/defn hiccup-to-dom
[hiccup-data]
(let [tag (name (first hiccup-data))
children (if (map? (second hiccup-data))
(drop 2 hiccup-data)
(drop 1 hiccup-data))]
(println "tag" tag)
(dom/element tag
(map (fn [child]
(if (string? child)
(dom/text child)
(hiccup-to-dom child)))
children))))
(e/defn electric-markdown3 [text]
(let [data (md/parse text)
hiccup (md.transform/->hiccup data)]
(hiccup-to-dom. hiccup)))
;;; stack overflow during compilation
• I don't see any usage of electric-markdown1
• in hiccup-to-dom
you have to use e/for
instead of map
on the children. map
is clojure land so you cannot call electric code like dom/text
in it
• electric-markdown3
looks alright except it's calling the broken hiccup-to-dom
the advantage of feeding data into electric instead of a raw string is you'll get fine-grained reactive DOM updates as the interpreter walks the data. Unchanged data nodes, provided they are properly keyed, will leave the DOM untouched
there's also an overhead to running electric, so it also might be that setting innerHTML will be faster
Our tutorial app has a working example - https://github.com/hyperfiddle/electric-examples-app/blob/60e2e6f8c34c6397da91af7d067bd977a75240c1/src/user_main.cljc#L137-L145C47
> • electric-markdown3 looks alright except it's calling the broken hiccup-to-dom Trying this give me:
(e/defn hiccup-to-dom [hiccup-data]
(let [tag (name (first hiccup-data))
children (if (map? (second hiccup-data))
(drop 2 hiccup-data)
(drop 1 hiccup-data))]
(dom/element tag
(e/for [child children]
(if (string? child)
(dom/text child)
(hiccup-to-dom. child))
children))))
(e/defn electric-markdown3 [text]
(let [data (md/parse text)
hiccup (md.transform/->hiccup data)]
(hiccup-to-dom. hiccup)))
...
(e/client
(electric-markdown3. "#hi"))
Encountered error when macroexpanding hyperfiddle.electric/boot.
Failed to analyse form
{:in [(map? (second hiccup-data))]}
`
> Our tutorial app has a working example
Thanks yea I have the innerHTML method working fine
(edit: edited this code a bit to add a .
to the recursive hiccup-to-dom call. still getting compilation errors, will report back if something is meaningfully different/promising though)ah, recursive self calls not implemented yet, https://github.com/hyperfiddle/electric/blob/15fb0219ff0d4daf507bd513750c698502700d2a/src-docs/user/electric/electric_recursion.cljc for a workaround
Ok yeah I think this works, using code from dom2/element. Thanks for the help!
(e/def h2e)
(e/defn electric-markdown3 [text]
(let [data (md/parse text)
hiccup (md.transform/->hiccup data)]
(binding [h2e (e/fn [hiccup-data]
(let [tag (first hiccup-data)
children (if (map? (second hiccup-data))
(drop 2 hiccup-data)
(drop 1 hiccup-data))]
(dom/with (dom/new-node dom/node (name tag))
;; hack: speed up streamy unmount by removing from layout first
;; it also feels faster visually
(e/on-unmount #(set! (.. node -style -display) "none")) ; hack
(e/for [child children]
(if (string? child)
(dom/text child)
(h2e. child))))))]
(h2e. hiccup))))
fwiw the streamy unmount lag might be fixed in the next version, we have a pretty good idea of what is happening, it was a mistake in how unmounts are synchronized over network
Here's a bit of a refactored version if anyone's interested in the future. supports :div.classes.like.this
and optional attriburtes, but not :div#id
yet. (had enough of yak shaving on this for today)
(e/def H2e)
(e/defn ElectricMarkdown [text]
(let [data (md/parse text)
hiccup (md.transform/->hiccup data)]
(binding [H2e (e/fn [hiccup-data]
(let [[tag & tagclasses] (str/split (name (first hiccup-data)) #"\.")
[children attrs] (if (map? (second hiccup-data))
[(second hiccup-data) (drop 2 hiccup-data)]
[(drop 1 hiccup-data)])
attrs (cond-> attrs
(seq tagclasses) (update :class concat tagclasses))]
(dom/with (dom/new-node dom/node tag)
(when (seq attrs)
(dom/props attrs))
(e/on-unmount #(set! (.. dom/node -style -display) "none")) ; hack
(e/for [child children]
(if (string? child)
(dom/text child)
(H2e. child))))))]
(H2e. hiccup))))
(comment
(ElectricMarkdown. "# Hello"))