Fork me on GitHub
#hyperfiddle
<
2024-01-30
>
joshcho08:01:23

fiddles are quite nice. thanks team!

Eric Dvorsak08:01:49

Hello! I've been trying to run this gist https://gist.github.com/dustingetz/1960436eb4044f65ddfcfce3ee0641b7 The second snippet with the !dirty gets me unbound electric var electric-starter-app.main/!dirty`, did I somehow manage to break something is so few lines or is the snippet not working? If so what is happening exactly? does binding not "see" previous bindings in the same form?

Geoffrey Gaillard08:01:44

It looks like there’s a mistake in the snippet. Sorry. binding binds in parallel (in both Electric and Clojure). Until we revise the snippet, you can try with:

(binding [!dirty (atom 0)]
  (binding [dirty (e/watch !dirty)]
    ...)

Eric Dvorsak08:01:26

Do you still use something like mount with electric to manage eg database connections that need to be closed?

Geoffrey Gaillard09:01:37

No need, Electric has the mount/unmount lifecycle baked in.

(e/defn GetDbConn []
  (let [my-closable-conn (get-db)]
    (e/on-unmount #(.close my-closable-conn)) 
    my-closable-conn))

Eric Dvorsak09:01:56

I get

ERROR hyperfiddle.electric: #error {
 :cause Wrong number of args (0) passed to: hyperfiddle.electric.impl.runtime/constant/fn--7241

...

in dynamically bound ( electric-starter-app.main/GetDbConn )
In a binding like this:
(binding [pg-conn (GetDbConn)] ... )

Geoffrey Gaillard09:01:46

Electric functions (`e/defn` and e/fn) are called with new: (new GetDbConn) or the equivalent (GetDbConn.).

Eric Dvorsak10:01:46

Ah thanks! but how does it work for e/fn then, eg in that snippet:

(CodeMirror. {:parent dom/node}
                            (e/fn [x]
                              (try (edn/read-string x)
                                   (e/server (reset! !sql-map x))     
                                   (catch #?(:clj Throwable :cljs :default) t
                                     nil))
                              x)
                            contrib.str/pprint-str
                            (e/server sql-map))
I suppose that is why I get "#object[TypeError TypeError: n.call is not a function]"

Geoffrey Gaillard10:01:06

I don’t see anything wrong with this snippet. e/fn is called like e/defn:

(let [Identity (e/fn [x] x)]
  (new Identity 1))
or shorter: (new (e/fn [x] x) 1)

Eric Dvorsak10:01:03

I think what is wrong is that the code mirror expects a clojure/script write-fn, but then you can't have e/server in it?

xificurC11:01:03

a cljs fn and an electric fn are 2 distinct objects. If you are interoperating with a js library that expects a (cl)js fn you cannot pass it an electric fn. Are you trying to use our contrib codemirror namespace?

Eric Dvorsak11:01:20

I'm trying to update the value on the server when the content changes

Vincent12:01:09

My guess is that the try catch must be fully insulated on one side or the other

Eric Dvorsak13:01:28

event just that fails:

(CodeMirror. {:parent dom/node}
                              (e/fn [x]
                                x)
                              identity
                              @!input)
#object[TypeError TypeError: n.call is not a function]
```

Vincent19:01:20

What is CodeMirror and what do you want to achieve

Eric Dvorsak19:01:06

it's from the contribs: [contrib.electric-codemirror :as codemirror :refer [CodeMirror]]

Eric Dvorsak19:01:22

I'm trying to update the value in the server when the content changes

Eric Dvorsak09:01:27

(e/defn GetDbConn []
  (let [my-closable-conn (get-db)]
    (e/on-unmount #(.close my-closable-conn)) 
    my-closable-conn))
wouldn't that open/close the connection for every client? if I'm using a connection pool I wouldn't want that?

Geoffrey Gaillard09:01:57

Depends on what get-db does. If it acquires the connection from a pool or if it builds a fresh one.

Eric Dvorsak09:01:39

yeah in my case it creates the connection pool:

(jdbc.connection/->pool
   HikariDataSource
   {:minimum-idle 10
    :maximum-pool-size  10
    :username "user"
    :password "password"
    :driverClassName "org.postgresql.Driver"
    :jdbcUrl "jdbc:"})

joshcho09:01:30

for people who have experimented with rich text editors in electric, which libraries did you use?

wei13:01:03

i've gotten vanilla lexical to work with a lot of hacking. maybe if i used react lexical the integration would have been simpler.

❤️ 1
zeitstein05:01:23

Off topic, but wondering if you've used ProseMirror and how you would compare it to Lexical @U066TMAKS? (Unrelated to Electric)

wei06:01:42

i haven't used prosemirror, but i think it's supposed to be the more mature option

gratitude 1
Vincent11:01:25

I think it's so cool we can do this now

(e/defn Main [ring-request]
 (e/server 
  (binding [e/http-request ring-request]
   (e/client
    (binding [dom/node js/document.body]
     (ShompMain.))))))

joshcho12:01:47

what is the purpose for exposing ring-request in Main? (not too familiar with ring)

Vincent12:01:02

access to request headers and session variables

❤️ 1
joshcho12:01:25

is there an example in fiddle?

joshcho12:01:32

if not, would be very nice

Vincent12:01:52

i figured out how to do it from the extended chat demo, which may or may not exist somewhere currently

clojure-spin 1
Geoffrey Gaillard14:01:40

Yes extended chat demo reads cookies. It is in the electric-tutorial fiddle.

❤️ 1
Hendrik12:01:05

I observe a strange behaviour with e/for-by iteration over a collection, where multiple dom nodes are created in each iteration step. When a item in the middle of the collection gets removed, then the ui gets cluttered. Is this behaviour expected? (i paste an example with screenshots in the comments)

Hendrik12:01:29

(def !my-list (atom [1 2 3 4 5 6]))


(e/defn Entry [text]
  (e/client
   (e/on-unmount #(println "unmount" text))
   ;no single containing dom element
   (dom/div
    (dom/text text))
   (dom/div
    (dom/text "Foo"))))

(e/defn List []
  (e/server
   (let [my-list (e/watch !my-list)]
     (e/for-by identity [v my-list]
      (e/client
       (Entry. v))))))

(comment
  (reset! !my-list [1 2 4 5 6]); remove from the middle
  )

Hendrik12:01:01

Ui before removal:

Hendrik12:01:22

Ui after removal:

wei13:01:23

i couldn't repro that, this is what i get using your snippet:

wei13:01:23

i recently had some strange behavior around conditionals causing client/server mismatches, maybe check for that? https://clojurians.slack.com/archives/C7Q9GSHFV/p1706249048037089

Hendrik15:01:35

Thanks for that hint. I carefully checked, that I do conditional requiring of namespaces that contain electric definitions. But I still get the strange behaviour. btw I am using electric version “v2-alpha-540-ga4699532”. Did you try to reproduce with the same?

wei15:01:07

it's the conditionals that cause the undefined behavior. e/defs need to be defined on both the server and the client (no conditionals) and there's currently no warning if one side is missing

wei15:01:17

i'm on the latest master

Hendrik15:01:21

I have no e/defs in conditionals.

Hendrik15:01:37

I try latest master later this day

Hendrik15:01:06

I stil get the error with latest master. I only get corret behaviour, if I wrap each iteration step in another div like so

(dom/div (Entry. v))

wei16:01:11

how about try to repro on a clean clone of electric-starter-app or electric-fiddle? that would tell you if it's an issue with your specific app

Hendrik21:01:40

I testet with the electric fiddle repo and extended the hello fiddle:

#?(:clj (def !my-list (atom [1 2 3 4 5 6])))

(e/defn Entry [text]
  (e/client
    (e/on-unmount #(println "unmount" text))
   ;no single containing dom element
    (dom/div
      (dom/text text))
    (dom/div
      (dom/text "Foo"))))

(e/defn List2 []
  (e/client
    (dom/div
      (e/server
        (let [my-list (e/watch !my-list)]
          (e/for-by identity [v my-list]
            (e/client
              (Entry. v))))))))

(comment
  (reset! !my-list [1 2  4  5 6]); remove from the middle
  )

(e/defn Hello []
  (e/client
    (List2.)
    (dom/h1 (dom/text "Hello world"))))
I still get the wrong behaviour (see screenshot). @U09K620SG do you have any idea what could cause this behaviour?

Dustin Getz21:01:31

we will try to reproduce

👍 1
Dustin Getz21:01:55

I also suspect (like Wei) some sort of desync. The recent Electric incremental compilation changes made it easier to desync the frontend and backend using things like reader conditionals to cause the client and server to see different programs, we're still gathering info

Dustin Getz21:01:02

It could also be a bug, the IC changeset was huge

wei14:01:32

i'm mounting/unmounting a table of 100 form ui4/input elements inside an e/for-by when the user switches tabs, and there's a 1-2s delay. any suggestions for making this more performant? I also tried commenting out the input elements (keeping the row divs) and while it's better, there's still some delay. what's the overhead for e/defn and should i be trying to inline things here?

Dustin Getz14:01:29

yeah e/fn overhead is high in v2, and worse, each electric-dom2 container (div, tr, etc) has a hidden e/fn wrapping the children. Really new is what has the overhead and in electric-dom2, new is everywhere. This is a top dev priority, Electric v3 will be better due to resolving a bunch of technical debt (Spring release) and one consequence of Electric v3 is that all of the hidden e/fns inside dom containers are no longer needed, which might solve this issue outright

Dustin Getz14:01:45

Yes there are optimizations you can do in the meantime – i'll get back to you, i'll make a short list

wei14:01:41

thanks! i can also wait for v3 if the optimizations are v2-specific. targeting a late spring release for this particular project

Dustin Getz18:01:48

Can you copy paste the key parts of your code into a gist? DM me if you want to keep it private

wei18:01:37

sure, give me a few min 🙏

wei18:01:44

btw, does e/watch have significant overhead?

wei18:01:17

relative to, say, instantiating a e/defn

Dustin Getz18:01:25

um, i'm not sure. Both involve new

Dustin Getz18:01:45

do you have a lot of them?

wei18:01:48

maybe a dozen? not as many as i have inputs and defns. i'll treat them the same in terms of cost then

Dustin Getz13:02:43

However, even in that demo, navigating between tabs is not instant, it's maybe 300ms to switch tabs (though not 1-2s). Some of this can be blamed on network delay but there is also clearly a waterfall load of sorts happening because the cells stagger in if you look closely

Dustin Getz14:02:06

if the backend queries for the query/cells are slow, due to the waterfall this can really explode out, that's when you really need to optimize today. Heavy electric-dom2 usage also adds weight due to it's internal e/fns as mentioned above.

Tommy Jolly17:01:48

Is electric-starter-app intended to be maintained long-term as the clean starting point? I migrated to electric-fiddle a few weeks ago — now it seems the sensible thing is to move back. Another question -- is there any reason not to keep the remote and repeatedly rebase on top of it? I like the idea of automatically updating dependencies and so on.

Dustin Getz17:01:35

honestly we don't commit to anything, we're still triangulating what our requirements are and what user requirements are. electric-fiddle is intended to have "batteries included" and electric-starter-app is intended to document the cleanest possible entrypoint because electric-fiddle has extra complexity (due to so many apps co-existing each with its own classpath requirements)

Dustin Getz17:01:01

the reality is that your app http middlewares (auth and such) are customized in 100% of real apps, so you have no choice but to understand the 100 line example http entrypoint

👍 2
Tommy Jolly17:01:47

OK, I'll stick with electric-fiddle for now, and yes I've been putting off understanding the underlying parts properly but I'll have to figure it all out one day!

Dustin Getz17:01:49

I'll add that electric-fiddle is far too fiddley today, it was shipped quickly and we're working on ways to improve it

Tommy Jolly18:01:17

That's true! I would probably get less conflicts on electric-starter-app. Thanks for the guidance as ever 🙂 Looking forward to the Differential talk

🙂 1
Eric Dvorsak09:01:27

(e/defn GetDbConn []
  (let [my-closable-conn (get-db)]
    (e/on-unmount #(.close my-closable-conn)) 
    my-closable-conn))
wouldn't that open/close the connection for every client? if I'm using a connection pool I wouldn't want that?