Fork me on GitHub
#hyperfiddle
<
2022-09-24
>
tlonist15:09:32

I'm trying to implement a simple filter with data fetched from a server, but photon keeps complaining about unresolved let, can anybody help?

tlonist15:09:40

(p/defn App []
  (p/client
    (dom/h1 "CRUD-MySQL")
    (let [!age-filter (atom nil) age-filter (p/watch !age-filter)]
      (ui/input {::dom/type        :search
                 ::dom/placeholder "Age above~"
                 ::ui/input-event  (p/fn [e]
                                     (do (reset! !age-filter (.. e -target -value))
                                         (prn @!age-filter)))})
      (dom/table
        (p/server
          (let [users (fetch-all-users database)]
            (p/for [user users]
              (p/client
                (dom/th
                  (dom/td "Name")
                  (dom/td "Country")
                  (dom/td "Age")
                  (dom/td "language"))
                (dom/tr
                  (dom/td (p/server (:users/name user)))
                  (dom/td (p/server (:users/country user)))
                  (dom/td (p/server (:users/age user)))
                  (dom/td (p/server (:users/language user))))))))))))

tlonist15:09:51

hyperfiddle.photon-jetty-adapter: Websocket handler failure #error {
 :cause Unable to resolve symbol: let
 :data {}
 :via
 [{:type clojure.lang.ExceptionInfo
   :message Unable to resolve symbol: let
   :data {}
   :at [hyperfiddle.photon_impl.runtime$eval$fn__10675 invoke runtime.cljc 1164]}]

👀 1
tlonist15:09:26

The error goes away once I delete p/server, but can't understand why it is the solution.

Dustin Getz15:09:51

it works on my machine what commit are you on

Dustin Getz15:09:08

well, i disabled

#_(fetch-all-users database)

tlonist15:09:47

I'm using today version of photon-starter-app

Dustin Getz15:09:10

ok let me try it, that is very old

tlonist15:09:33

cc18854cfffa5993199cd9707659ad4914b0261f

Dustin Getz15:09:20

you pulled today?

tlonist15:09:52

yea I did.

Dustin Getz15:09:57

you overrode the photon dep in photon-starter app?

tlonist15:09:28

com.hyperfiddle/photon         {:mvn/version "20220827-153507"}
is this outdated?

Dustin Getz15:09:40

that is not cc18854

tlonist15:09:12

oh I didn't override it then. Will update first

Dustin Getz15:09:14

can you push your whole project to a branch so i can see exactly what you have

1
tlonist15:09:51

oh as a branch of photon-starter-app right?

Dustin Getz15:09:01

however you want to do it

Dustin Getz15:09:12

It works on my machine in photon-starter-app at master

tlonist15:09:22

I'd prefer that if you think it's okay. much better than making another private repo

Dustin Getz15:09:25

except for this difference

#_(fetch-all-users database)

tlonist15:09:26

I can't upload it with a branch, no permission. I made a private branch and shared with you just now.

tlonist15:09:52

> It works on my machine in photon-starter-app at master but the photon version has to be up-to-date, right?

Dustin Getz15:09:06

i used the version on master

Dustin Getz15:09:17

photon-starter-app as is

Dustin Getz15:09:10

Ok I see your repo that you invited me to but i dont think you pushed your code

tlonist15:09:01

just did!

1
Dustin Getz15:09:27

CDN replication lag maybe

tlonist15:09:07

strangely, it is working now.

tlonist15:09:13

:face_palm:

Dustin Getz15:09:47

as configured or with the overridden dep (the repo has a pinned stale photon dep afaict)

Dustin Getz15:09:24

Works for me too as cloned - but I really want your photon dep to track photon/master

Dustin Getz15:09:34

we have tons of fixes and improvements that you need

1
tlonist15:09:15

since I got your attention, I have one question. suppose the users were declared in the upper most let bindings together with filter atom. Why can't the data be passed into dom/tables?

👀 1
Dustin Getz15:09:22

this line is wrong, it's calling a CLJ function from the client

1
tlonist15:09:39

I need to wrap the line with p/server to make it work.

tlonist15:09:48

yea I caught that.

Dustin Getz15:09:15

I am not surprised by bad error messages – we did an error handling pass recently, i dont know if it is in the coord you were using

Dustin Getz15:09:35

the errors are a lot more helpful now, not perfect but enough to reasonably pinpoint problems

tlonist15:09:20

(p/defn App []
  (p/client
    (dom/h1 "CRUD-MySQL")
    (let [!age-filter (atom nil) age-filter (p/watch !age-filter)
          users (fetch-all-users database)]
      (ui/input {::dom/type        :search
                 ::dom/placeholder "Age above~"
                 ::ui/input-event  (p/fn [e] (reset! !age-filter (.. e -target -value)))})
      (dom/table
        (p/server
          (p/for [user users]
            (p/client
              (dom/th
                (dom/td "Name")
                (dom/td "Country")
                (dom/td "Age")
                (dom/td "language"))
              (dom/tr
                (dom/td (p/server (:users/name user)))
                (dom/td (p/server (:users/country user)))
                (dom/td (p/server (:users/age user)))
                (dom/td (p/server (:users/language user)))))))))))
something like this.

Dustin Getz15:09:38

fetch-all-users is still not wrapped in p/server

tlonist15:09:04

yes, but I thought the data fetched from the fetch-all-users must be bound to 'users'.

Dustin Getz15:09:09

> suppose the users were declared in the upper most let bindings together with filter atom. Why can't the data be passed into dom/tables? is the users collection on the client or server? you likely want the bulk collection to stay on the server (transferring the minimum needed amount of data that the client actually needs)

tlonist15:09:20

so that it can be passed onto anything without specifying where it came from

Dustin Getz15:09:04

Try this

(p/defn App []
  (p/client
    (dom/h1 "CRUD-MySQL")
    (p/server
      (let [users (fetch-all-users database)]
        (p/client
          (let [!age-filter (atom nil) age-filter (p/watch !age-filter)]

Dustin Getz15:09:27

But first set your photon dep to

com.hyperfiddle/photon         {:git/url "[email protected]:hyperfiddle/photon.git"
                                :git/sha "30daf27a4639df7baca14df9ba93a66d66d36ebc"}

tlonist15:09:30

I want the initial first collection to be made on the server side, and the rest is just a client side filter manipulation.

Dustin Getz15:09:32

just so we see the same thing

1
Dustin Getz15:09:32

Did I answer the question properly?

Dustin Getz15:09:28

hmmm

Encountered error when macroexpanding hyperfiddle.photon/boot.
Unable to resolve symbol: let

tlonist15:09:21

yep that one.

tlonist15:09:48

trying this

(p/defn App []
  (dom/h1 "CRUD-MySQL")
  (p/server
    (let [users (fetch-all-users database)]
      (p/client
        (let [!age-filter (atom nil) age-filter (p/watch !age-filter)]
          (ui/input {::dom/type        :search
                     ::dom/placeholder "Age above~"
                     ::ui/input-event  (p/fn [e] (reset! !age-filter (.. e -target -value)))})
          (dom/table
            (p/for [user users]
              (p/client
                (dom/th
                  (dom/td "Name")
                  (dom/td "Country")
                  (dom/td "Age")
                  (dom/td "language"))
                (dom/tr
                  (dom/td (p/server (:users/name user)))
                  (dom/td (p/server (:users/country user)))
                  (dom/td (p/server (:users/age user)))
                  (dom/td (p/server (:users/language user))))))))))))
as suggested

Dustin Getz15:09:11

the :require-macros is wrong

Dustin Getz15:09:27

(ns app.mysql-crud
...
  #?(:cljs (:require-macros app.core)))

tlonist15:09:19

ah, right. why did it work before?

tlonist15:09:40

now I have DEBUG : Unserializable reference transfer: [{:users/id 100, :

Dustin Getz15:09:58

That i can help with, let me explain the first issue first

🆗 1
Dustin Getz15:09:51

What I think happened is the code that shadow sees (filesystem) was out of sync with what the JVM server has loaded. What :require-macros does is force shadow to reload the JVM as it compiles the file, to guarantee the filesystem and jvm are in sync

Dustin Getz15:09:37

Which is why you got a nonsense error – the compiler state got corrupted basically

Dustin Getz15:09:44

Does that make sense?

tlonist15:09:11

I see, that's why it produced the errors once I restarted repl.

Dustin Getz15:09:29

yeah, and at some point you likely loaded the file to the JVM repl

tlonist15:09:23

oh, and the unserializable reference transfer is solved by adding p/server just outside of p/for

Dustin Getz15:09:47

yeah, the users collection is on the server and likely contains sql database cursor objects of some sort

Dustin Getz15:09:16

you will also need to use p/for-by with a keyfn, (p/for-by identity [user users] ...)

tlonist15:09:40

so basically p/server, p/client are like tags attached to data to tell them about their origin.

Dustin Getz15:09:16

they are compile time markers that mark a region of code as client or server

1
Dustin Getz15:09:35

for p/for-by – instead of identity, choose a database identity (record id or something) – this is like in react.js, it is used to make the for loop stable and efficient across reactive updates

👍 1
tlonist15:09:23

I see many core functions are overriden by photon (starting with p/..), what do they mean in simple terms?

tlonist15:09:51

great to see some SPA features loaded in photon!

Dustin Getz15:09:13

(ns hyperfiddle.photon
  (:refer-clojure :exclude [eval def defn fn for empty?])

tlonist15:09:27

yea those I meant.

Dustin Getz15:09:33

p/defn defines a reactive function

Dustin Getz15:09:49

it macroexpands to (p/def ... (p/fn ...

Dustin Getz15:09:55

p/def defines a reactive global; they behave like clojure's ^:dynamic by default and work with (binding [])

tlonist16:09:23

how should I expect them to behave differently? or should I look up for their implementation? I roughly thought it had something to do with the missionary part you explained earlier.

Dustin Getz16:09:50

you are already using p/defn in your app!

tlonist16:09:17

I know haha, but that was c&p from the demos. I used it without understanding what I'm really doing.

Dustin Getz16:09:11

p/defn defines a reactive function and it also handles p/client p/server

Dustin Getz16:09:05

p/for is a more efficient reactive loop thing, don't use clojure.core/for in photon

Dustin Getz16:09:02

beyond that it's easiest to see how to use them by going through the examples

tlonist16:09:29

so it's recommended (if not necessary) to use functions from photon(p/~) within the definition of p/defn?

tlonist16:09:47

because the reactive nature can be broken.

Dustin Getz16:09:16

that's right - reactive functions can only be called from other reactive functions

tlonist16:09:03

got it. thanks, I will find you with other questions later!

👍 1