Fork me on GitHub
#fulcro
<
2018-08-10
>
currentoor00:08:30

messaging works both ways

currentoor00:08:55

i think i’ll just use the websockets networking component, not make a headless client app

currentoor00:08:03

@wilkerlucio one of my builds shows No app connected. in the fulcro inspect tab in the chrome extension, even though i see Installing Fulcro Inspect in the js logs

currentoor00:08:25

i’m guessing something about my app is causing an exception in fulcro inspect, any suggestions for how to debug?

currentoor00:08:36

oh i guess i can just inspect it like a regular webpage

currentoor00:08:54

yeah there was an exception

Error in event handler for (unknown): #error {:message "No reader function for tag fulcro/tempid.", :data {:type :reader-exception, :ex-kind :reader-error}}

currentoor00:08:44

for some reason fulcro inspect is not loading the definition of fulcro/tempids when it tries to load

wilkerlucio00:08:39

which versions are you in @currentoor?

currentoor00:08:04

"2.2.1" in project.clj

currentoor00:08:19

it works fine for my other builds with the same dependencies

currentoor00:08:24

in the same project

currentoor00:08:21

the chrome extension is 1.0.2

wilkerlucio00:08:42

yeah, versions are good

wilkerlucio00:08:31

that sounds a strange issue, its suppose to have the fulcro/tempid set up

wilkerlucio00:08:10

is your app doing some load at start? I just wonder if it is and you comment, we can check if it's some kind of network issue

wilkerlucio00:08:25

are you setting custom networks?

currentoor00:08:46

yes it doing some loading at the start, the network is the fulcro websockets component

currentoor00:08:56

nothing custom, i’ll check

wilkerlucio00:08:20

can probably be it, I never tried with websockets on network

currentoor00:08:23

i disabled both websockets and any load calls

currentoor00:08:27

this issue still happens

wilkerlucio00:08:47

that's strange, where the error comes from?

currentoor00:08:04

i removed fulcro inspect from chrome and added it back

currentoor00:08:07

now it’s working

currentoor00:08:41

i can no longer reproduce the error 😅

wilkerlucio00:08:57

that's the second time it happens, chrome seems to be corrupting files -.-

currentoor00:08:58

well at least we know this is something that could fix issues

wilkerlucio00:08:14

what OS are you in?

currentoor00:08:22

macos yosemite

wilkerlucio00:08:57

thanks, just trying to see a pattern, so I can compare with the next in case it happens

currentoor00:08:30

thanks for helping simple_smile

hjrnunes18:08:05

Hi all! I don't understand why I'm getting should have a unique "key" prop on this code:

(def portfolio-snap-header {:share/name     "Name"
                            :share/sym      "Symbol"
                            :share/broker   "Broker"
                            :share/currency "Currency"})

(def portfolio-snap-header-ks (sort (keys portfolio-snap-header)))

(defn portfolio-snapshot-header []
  (dom/thead
    (dom/tr
      (vec (for [k portfolio-snap-header-ks]
             (dom/th (get portfolio-snap-header k)))))))

(defsc PortfolioRow [this row]
  {:initial-state (fn [p] p)}
  (dom/tr
    (vec (for [k portfolio-snap-header-ks]
           (dom/td (get row k))))))

(def ui-portfolio-snap-row (prim/factory PortfolioRow {:keyfn :db/id}))

(defsc PortfolioTable [this {:keys [snapshot/rows]}]
  {:initial-state (fn [{:keys [rows]}] {:snapshot/rows (if (seq rows) rows [])})}
  (dom/table
    (portfolio-snapshot-header)
    (dom/tbody
      (mapv ui-portfolio-snap-row rows))))

(def ui-portfolio-snap-table (prim/factory PortfolioTable))

(defsc ShareRoot [this {:keys [summary/portfolio]}]
  {:initial-state (fn [p] {:summary/portfolio (prim/get-initial-state PortfolioTable {:rows [{:db/id          1
                                                                                              :share/name     "Facebook"
                                                                                              :share/sym      "FB"
                                                                                              :share/currency "USD"}
                                                                                             {:db/id          2
                                                                                              :share/name     "Apple"
                                                                                              :share/sym      "AAPL"
                                                                                              :share/currency "USD"}]})})}
  (dom/div
    (ui-portfolio-snap-table portfolio)))

(defcard-fulcro share-card
  "# Full App"
  ShareRoot
  {}
  {:inspect-data true})
I get it on PortfolioRow, because there's no :keyfn there (btw, how do I do it here? tried ^{:key k} but didn't work) But what about PortfolioTable? Why doesn't it use the :keyfn in the factory? Thanks!

cjmurphy18:08:26

PortfolioRow would need to have a query, and the query would need to have :db/id in it, so that the :keyfn can pick it up.

cjmurphy18:08:44

You don't have to use :keyfn. If every 'detail' was rendered using a component that had a query and each query had :key in it, and the value was unique by (gensym), then you would never get one of those errors. Not recommended of course - :keyfn allows :db/id or similar to be used.

hjrnunes18:08:49

@cjmurphy in PortfolioRow, how do I give a key to these: (dom/td (get row k)) ?

hjrnunes18:08:09

I didn't realise :initial-state depended on :query

cjmurphy18:08:47

TBH I don't know for certain :key needs to be in a query, but I do know it needs to exist.

cjmurphy18:08:33

Can you look at your state using Fulcro Inspect?

cjmurphy18:08:29

Much better to construct a structure using queries and idents.

hjrnunes18:08:34

@cjmurphy Kind of. I watched the videos a couple of times and skimmed the docs. I definitely need to read more.

hjrnunes18:08:58

You're probably right re. queries and idents

cjmurphy18:08:27

The 'secret sauce' bit is the hard/important part.

hjrnunes19:08:36

So, I got things working with queries. But I'm still seeing the warnings, but I think I now get what's happening. I need to get a key to those td elements. How do I do that? Pure JS?

dvingo20:08:02

hey @hjrnunes it looks like it is a react warning from the td's

dvingo20:08:24

anytime you have a collection returned react will warn you if there are not unique keys on them

hjrnunes20:08:41

I just don't know how to get those a key]

dvingo20:08:54

something like this (dom/td {:key (get row k)} (get row k)

hjrnunes20:08:07

ah right, makes sense

dvingo20:08:24

or just: (dom/td {:key k} (get row k)

hjrnunes20:08:16

yeah, I know in reagent is ^{:key k}, and I tried that. Didn't occur to just pass as argument

hjrnunes20:08:15

sweet, that was it

pvillegas1220:08:40

Is there any good example of using fulcro with a datomic backend?

tony.kay21:08:38

@pvillegas12 So the queries (for the most part) match Datomic pull….so for a given load, you can often just do a datomic pull. For schema/UI structure mismatches, I recommend Pathom as the intermediary

tony.kay21:08:21

This YouTube video shows some pathom in the context of SQL (about 20 mins in), but it is trivial to convert the examples to Datomic: https://www.youtube.com/watch?v=gbrdnSsUerI&amp;index=15&amp;t=1619s&amp;list=PLVi9lDx-4C_Rwb8LUwW4AdjAu-39PHgEE

tony.kay21:08:41

In terms of mutations, well, you just need to transact the right stuff 🙂

pvillegas1221:08:02

I’m currently investigating a green field project with fulcro + datomic on the backend, any pointers @tony.kay?

tony.kay21:08:37

What have you learned already?

pvillegas1221:08:24

Assessing the landscape, I’m fixed on Datomic as the backend. So for now I have clojure, clojurescript, react, datomic on my plate

tony.kay21:08:19

ok, so the most important thing to understand is the unifying effect of the co-located UI query, and the (critical) join point functionality of pathom. Using Datomic gives you a boost because the graph query langauges match, but there is no real need to tie anything in Fulcro to a specific back-end. Giving your UI components queries means you have to do very very little work (on the client) to read exactly the data you want from your server, and have it normalize into your client DB and refresh the UI. On the server, you need to understand how you code up the “mismatches” between what the client wants (UI tree structure) and how your db schema is actually shaped. When they match: perfect….`datomic/pull`. When they don’t, something like pathom/connect, which is described in the above video. Wilker also gave a talk at Dutch Clojure Days about some more advanced and general concepts ideas…but the video will really give you a feel for that the server coding is like.

pvillegas1221:08:20

What about offline support?

pvillegas1221:08:38

Fulcro uses datascript as the underlying db in the client side right?

wilkerlucio21:08:01

fulcro uses regular map dbs (they are much faster in the end then datascript for processing the query)

tony.kay21:08:02

depends on how hairy you want to get. Fulcro does not use datascript, for various reasons I’ve talked about in the past

wilkerlucio21:08:38

about offline support, pathom can build parsers on both clj and cljs, so one way to do it has having a local network that can target queries to some offline source (like indexedb or local storage)

pvillegas1221:08:44

I want to get really hairy at the offline support level

tony.kay21:08:18

or Datascript if you must 🙂

wilkerlucio21:08:22

so, if you wanna go full offline, I suggest you can start with a cljs parser that fetchs data from indexeddb, and then you just sync it at some point with your server

wilkerlucio21:08:51

datascript problem is that its not durable, and you have to load all in memory, that can get out of track depending on your content size

tony.kay21:08:09

unless you serialize it to local storage…which can be slow

wilkerlucio21:08:54

yeah, those work for small data sets, after that I think indexeddb is the way to go, so you can load pieces of the database

tony.kay21:08:07

At the end of the day, it depends on what you mean by “offline”…your app can’t work unless it has all the data it needs, which implies you’re going to pre-query a load of stuff. Updates are a “hard problem” if there is any chance of concurrent access. You can cache them and sync them later, but they should probably use optimistic concurrency or CAS semantics in certain situations, which then makes recovery for the client a bit wiggy (how do you roll back something they did yesterday in the UI??? Well, you could use UI history…)

tony.kay21:08:25

If what you mean is just “I want to tolerate network flakiness”, then that is much easier

tony.kay21:08:53

Fulcro is optimistic by default, so you can code your network layer to just retry things on failures. The websockets support has an auto-retry feature for just that kind of mode

tony.kay21:08:14

That can get you operation up to the point of needing a new query of data from the server

tony.kay21:08:55

But all that aside: Fulcro gives you good isolation layers to make it as easy as it can be…it’s a hard problem that I don’t consider “part of” Fulcro proper.

pvillegas1221:08:59

I’m looking for the hard offline problem 🙂 that’s why I need something like datomic to be able to go back and forward to be able to solve concurrency problems

tony.kay21:08:25

I think you may not be thinking that through well enough

tony.kay21:08:44

Datomic lets you view history, but it isn’t going to be easy to use that to “fix” conflicts in history.

tony.kay21:08:54

you don’t “fork” datommic databases

pvillegas1221:08:15

yeah, I know, it has the proper semantics (I feel) to be able to resolve those issues

tony.kay21:08:17

distributed computing is still distributed computing

tony.kay21:08:54

well, Fulcro will make hooking it all up simple…but it won’t solve your real hard problem, which is distributed disconnected operation. 🙂

tony.kay21:08:32

If you can prevent conflicts by design (e.g. no one ever edits the same thing at the same time), then it is trivial

pvillegas1221:08:36

From what I understood in the introductory video, it seems that I can queue up mutations I perform in the UI (assuming I have all the relevant data) and at some point in the future, when the user has a connection again, push all those mutations to the server

tony.kay21:08:18

yes, that is simple…and the websockets support will already do that. The non-websockets stack would need retry code, which isn’t too hard

pvillegas1221:08:10

Perfect, I would start with a constrained version of offline (as you suggest). Thanks for the pointers, I would still need to serialize the db to something like indexdb right?

tony.kay21:08:37

I’m designing a system that tolerates failures, and I use some of those elements. I’m also using CAS kinds of semantics to help detect collisions (mutations send what it was, and what it will be…but on conflict I’m letting user retry)

tony.kay21:08:51

WIlker’s suggestions are best on that

tony.kay21:08:51

In Fulcro you can add any number of “remotes”, and queries/mutations can target any of them (mutations can target multiple). So, your writes could hit both local db and send remote. Return of mutation from server could help fix local db to match if there were conflicts.

tony.kay21:08:06

So, one of your “remotes” could just hook to a browser indexeddb

👍 4
tony.kay21:08:22

Then leverage pathom to run the graph queries against that…again refer to the video for the idea

pvillegas1221:08:48

What is the om-next, fulcro story btw?

Chris Swanson21:08:34

i can see how using datomic makes queries easy , but what about mutations? like if i've got a diff generated by form-state, what's the best way to turn that into a transaction back to datomic?

Chris Swanson21:08:26

right now i just reshape it myself, but i'm curious if there's a better way , maybe pathom has some way to do it out of the box

wilkerlucio23:08:02

@pvillegas12 fulcro 1.0 was just a library for om-next, but after some point it required more internal structural changes, while om.next tries to have a very open architecture (like open to pick which data structure you will use for local db, clojure maps vs datascript for eg), fulcro goes and takes some decisions for you (use the map db for example), and that made a case were fulcro cold run faster as fork then having keep compat with om.next

wilkerlucio23:08:20

since then fulcro grew very fast and still improving on a regular basis