Fork me on GitHub

Getting realllly close to 2.0. All docs updated on the 2.0 branch. A little more verification and (finally) some new additions to the forms left. Might be ahead of schedule.


@tony.kay I am not familiar with web ui, I just want to know why you select bootsrap as default part in fulcro, which does not have many ui components such as drawer, autocomplete inputbox.


@tony.kay Is there a fulcro Websockets component? Can Untangle Websockets works with fulcro? I want auto-sync front database with backend, so I need a push from Datomic in backend through listening tx-report-queue, Is there a fulcro demo for this? and any advice?


@tpliliang So, bootstrap was really easy to throw together quickly as a sample set of components. They could use a little work, and are there to just get you going quickly. I made an external lib that wraps semantic-ui as well (semantic-ui-wrappers). blueprintjs can be used just as an NPM dependency.


Fulcro has websocket support. It is just part of the library (used to be a separate lib when it was Untangled


There is a websocket demo, and a readme about them.


@tony.kay yes, I just saw the websocket parts , and read the demo codes now. thanks


as far as sync goes…yes, pretty straightforward. I wrote a prototype at one point. Here is what I did, basically:


Query : Graph query Datomic : Has tx listener functionality: before, after, datoms that changed - So, observation 1: For all property changes, you can derive the entity IDs affected from tx log datoms, INCLUDING ref changes. So, a ref-many addition will give you datom about the parent, but not necessarily the child. tx-change->id-set - A graph query generates a tree of data, and as long as you include :db/id in each level of that query, you can also technically derive the list of IDs of interest from that tree of data (e.g. with clojure.walk). Query->id-set - A graph query (based on UI components with Idents) + A tree of data response === merge! ===> Normalized database in UI - Websockets, can use transact! and merge! on reconciler to take incoming data and put it in the database. - Sente (or other websocket tech) can be programmed to know which UI it is hooked to....can create a session of sorts. So, we can define a subscription in terms of a graph query as follows: 1. Define a mutation on a client, where the client is speaking over websockets: subscribe which takes a graph query, and a tempid. This is a remote mutation, where the server remaps the tempid to the real subscription it created. Along with that, I can perhaps associate a localized name (e.g. keyword) with that tempid. For example, I could transact the kw/tempid pair into my database, then send the subscribe. The remap on return, will rewrite all tempids to the real I can easily FIND the id for unsubscribe. API can end up being just

(transact! this `[(subscribe {:query ~(om/get-query Person) :root-id 23 :key :my-sub :server-id ~(om/tempid)}))
(transact! this `[(unsubscribe {:key :my-sub}))
NOTE: You have to keep the component-based query in app state. Be careful not to munge it and lose metadata (and break normalization). 2. On server, handle the mutation as follows: - Generate a unique subscription ID to track the subscription (can dedupe if you want...) - Run the query against Datomic, use query->id-set to get IDs as a set, and store THAT (plus the query, root, and users websocket handle) under the subscription ID - Return a tempid -> realid mapping of the subscription 3. In your tx logging tracker, for each tx that happens: - Run tx-change->id-set = change-set - Intersect sets of subscriptions with change-set. For any NON-EMPTY result, re-run that query, and push to client - The push contains the client’s keyword or ID of the subscription, and the tree of data 4. Make a websocket push handler on the client, that runs merge! using the subscription’s query and the incoming data - recieves ID and tree of data - Looks up query in subscriptions by ID (which has normalization metadata) - Runs (merge! reconciler tree-of-data query) Major Caveat: Merge is possibly going to stomp on active user edits...which means you might want to make form editing work on a copy.


I don’t have a demo at the moment, and I have not had time to build something production-ready. There are a number of things to deal with to make an actual library for doing this…but mostly around memory footprint and overall performance.


the core code for a demo would be pretty tight and small. actually pretty amazing, so at some point I’ll probably make more of a demo


@tony.kay Great, Thanks, I will try your advice, and waiting for your demo:grinning:


@tpliliang I wrote a blog post about I did this with fulcro (at the time called untangled), some things have changed but the concepts remain similar


forgive me if this question is already confused; does the default fulcro parser support recursive uis using {:children ...} in the query?


so, if you pull the parser from the env, you can call it recursively…and that will trigger on the multimethods (which is all the macros are wrappers around)


My assumption is that you’ll often want to use some other library to deal with the recursive graph processing…pathom, fulcro-sql, Datomic, etc.


@uwo if the question is regarding the client-side, yes


thanks. and, yes, i meant on the client. like described here


@uwo Oh, sorry misunderstood. Yes, there is a hook to add in your own read handler that is used “before” the built-in one


See read-local option on new-fulcro-client


remember that the parser is only called on top-level keys. So, you have to handle any subtree you start handling…but just use db->tree to finish out anything that you don’t want to handle.


but you don’t need parsers to do what he’s talking about in the article…


recursive queries are supported in Fulcro’s default…you just have to make the data model recursive through ident joins.


in 2.0 will the {:children-key ...} query syntax still be recognized, if you’re dropping om-next as a dep?


The short answer is: almost everything you’re used to in Fulcro 1.0 will have an API-equivalent that is just in a new namespace


the internals are cleaned up, dynamic queries are consistent and work across the platform, and there are a lot of new things.


Fulcro 2.0 alpha5 on clojars. The primary change is that it now supports ::prim/tempids as well as :tempids on server return of tempids. This allows me to spec it, and it also helps ensure we don’t collide on user keys in maps. That latter advantage cannot come until we deprecate and remote :tempids…but it is a start.


It has come down to forms! So, 2.0.0-alpha5 should be ready for people to try porting. I’m not planning on making any more namespace changes. All of the hard stuff has been done. The forms stuff is documentation, more demos, and expansion.


@tony.kay what ::prim means in this context?




it’s the prefix I’m using in all the new docs, so I assume you’ve seen it…but not a good idea to do that 🙂


The new dev guide has been expanded quite a bit, better coverage and separation of server topics


I combed over it completely, and edited quite a bit.


nice, I didn't had time to stop and read all of the new stuff, but I'm looking forward to it


I’m also dying to do some A-B (Fulcro 1 Om Next vs Fulcro 2) rendering performance tests


There is one algorithm I’d like to speed up, if anyone is into performance optimization…it is relatively small: add-basis-time


I did the quick, cheap, and easy prewalk.


but this little function runs on every query result from the database, so any bit of performance boost it can get would be good. Then again, perhaps if we do some testing (always advisable) it’ll turn out it isn’t all that bad (compared to the other phases).


This is the replacement for Om Next’s internal path-meta stuff. One way to improve it, by the way, is to walk the query with the result and stop when the query stops (it is possible for a query result to contain an opaque thing that contains a bunch of nested maps). There’s no need to mark those, so that is possibly the biggest win….but that makes it a much more complex algorithm 😕


I’ll open an issue and explain more ideas