Fork me on GitHub

messaging works both ways


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


@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


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


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


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}}


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


which versions are you in @currentoor?


"2.2.1" in project.clj


it works fine for my other builds with the same dependencies


in the same project


the chrome extension is 1.0.2


yeah, versions are good


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


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


are you setting custom networks?


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


nothing custom, i’ll check


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


i disabled both websockets and any load calls


this issue still happens


that's strange, where the error comes from?


i removed fulcro inspect from chrome and added it back


now it’s working


i can no longer reproduce the error 😅


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


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


what OS are you in?


macos yosemite


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


thanks for helping simple_smile


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 []
      (vec (for [k portfolio-snap-header-ks]
             (dom/th (get portfolio-snap-header k)))))))

(defsc PortfolioRow [this row]
  {:initial-state (fn [p] p)}
    (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 [])})}
      (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"}]})})}
    (ui-portfolio-snap-table portfolio)))

(defcard-fulcro share-card
  "# Full App"
  {: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!


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.


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.


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


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


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


Can you look at your state using Fulcro Inspect?


Much better to construct a structure using queries and idents.


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


You're probably right re. queries and idents


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


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?


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


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


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


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


ah right, makes sense


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


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


sweet, that was it


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


@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


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:;index=15&amp;t=1619s&amp;list=PLVi9lDx-4C_Rwb8LUwW4AdjAu-39PHgEE


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


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


What have you learned already?


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


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.


What about offline support?


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


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


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


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)


I want to get really hairy at the offline support level


or Datascript if you must 🙂


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


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


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


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


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…)


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


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


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


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.


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


I think you may not be thinking that through well enough


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


you don’t “fork” datommic databases


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


distributed computing is still distributed computing


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


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


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


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


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?


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)


WIlker’s suggestions are best on that


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.


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

👍 4

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


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


@pvillegas12 fulcro 1.0 was just a library for om-next, but after some point it required more internal structural changes, while 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


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