Fork me on GitHub

Are people really not needing join placeholders very often? I feel like I must be doing something wrong, or else everyone is querying for data in the root and passing it down. Take the example of a root component, with two toolbars and a main content area. All the root component is doing is passing data to these other three child components, needing no data itself. What would people do in this case? Query for the data in root, and pass it down? Or query the data where it's needed and use join placeholders?


If you use pathom on the server you can still use placeholders. That is what we do


@danielstockton Something like this I guess. Root composes get-query & get-initial-state and passes that data down. If the children have subcildren intial state will work for them also. The toolbars & main-area would be components that have ident .


Thanks both. @U060GQK8U is it something common, or do you really find it a special case?


It is especially useful for reusable components. If it is one off you can just move the query one component up

👍 4

@danielstockton we use placeholders heavily at nubank, they are a great tool to break down components in smaller ones


@U066U8JQJ Wrong tag, but thanks, good to know


ups, fixed 🙂


And do you use the same technique, adding ident links on state to reify the joins? Or recursive parsing/pathom takes care of this?


I mean like

tony.kay   `{:table {1 {:join-key [:table 2]} ...}}` (edited)
tony.kay   so that a join on whatever compnent uses table 1 can have `[{:join-key …}]` just “follow the link”….via `db->tree`


not sure if I understand the question


I rarely to ident links, just in case of globals


to be honest I don't like the concept of the root either, I think makes more sense if you think that everything has identities, I usually just have a very dummy root, all it does is point to some component with identity


If you have a :join/senseless that is just to pass props down into another component in the UI, does that :join/senseless appear anywhere in app-state, or hidden completely by parsing?


I think the concept of ident and structure of the db is very important, and I usually think about things all wrong


depends on how you want ot behave, in the case of pathom placeholders, you are going to specific place, which is the same you were before, its important to think that IMO because its important to keep track of your context


but in globals case wouldn't matter, but its good to have it well defined


so in pathom case, you can do :>/senseless and it should work


just to clarify a bit more, this context is specially important for pathom because of caching optimizations, if you have a query like:





to pathom, in terms of optimization, its like this query is flat, so it will coordinate all the optimizations as is this query was flat (for the most part, there can be differences when considering same keyword with different params)


I think I understand it, it's just surprising that I don't often see it being talked about when imo it's the hardest thing to get right. If you know any non-trivial open source examples I can take a look at. I still have to delve more into pathom. Using it on the server to handle some of my parser, but I assume it's meant as a complete solution on both client and server side? Need to discover it's full power.


the client support is for reach, if you are using on the server already, there is not much difference now (except the types of resources you can access, which are greater on a server side)


in the future we might support partial parser delegation, that could be interesting to move parts of the logic to the front, but not there yet


This is obviously a simple example with only two levels, so querying the data in root is probably fine, but you can imagine further nesting inside the subcomponents.


In this is the single hardest thing to get to grips with (imo)


I'd love to study a more complex fulcro example, that has a fairly deep component hierarchy and lots of data, if anyone knows of one?


The usual todomvc type applications usually don't address this problem. Root component is a list and children the individual items with idents.


Might want to look at or Not sure there complex enough or should be taken as a reference 🙂


If you have a screenshot of the application/scenario that might help. Don't really know about nesting stuff, usually with fulcro you have intial app state, just plug the components there and you think in isolation, mostly parent & child.


I don't have a screenshot, but imagine a sidebar within a main component, with a list of fonts. Only the sidebar needs that list of fonts, so the :sidebar/fonts key is in that component. This requires something like {:join/sidebar-props (om/get-query Sidebar)} and :join/sidebar-props is 'senseless` join.


Hmmm. If you view in terms of joins. Don't know what's the right approach here, might be a mindset shift from om-next (I played around with it a few years ago but just scratched the surface no real project).


Usually I write the UI how it makes sense for the ui. If I need data from multiple components that each as a mutation that goes to the server and maybe some reformatting. Since multiple mutations will get batched together in 1 request I think about things locally for that component.


For example I would write the join as query {`:ui/status-bar (prim/get-query SIdebar)` so that it will not be sent to the server.


For inital component data there is also {:ui/statsubar (prim/get-initial-state Sidebar {:a-prop true})} Haven't felt like the additional joins matter in any significant way.


Right, you can do that in fulcro because loads and reads are separated. You still have the problem of needing the data in the db to match the query though right? So you would need a :ui/statusbar [:statusbar 1] type ident on state, to point it back to root and the sidebar object


There prim/get-initial-state + :initial-state help you out with that one for UI data stuff. For loading from the server, it can match or if it does not mach then you can just not use fulcro post-mutations. Fulcro will put the data in a root key :data-loaded-from-server1 and you can just use a post mutation to morph the data and add it where it needs to be in the state. If it matches the server then it's easier since fulcro can do more of the merging stuff for you.


Usually for UI data component query & initial-state have to match. ex:

(defsc Person [this]
  {:query         [:db/id :person/name :person/age] 
   :initial-state (fn [x] {:person/name "abc" :person/age "222"})
   :ident         [:person/by-id :db/id] ; (1)
And you have to make the connection in the parent to link the child prim/get-query & prim/get-initial-state. But it's quite nice because then you can move UI components around the UI really smoothly 🙂


What if there is no initial state?


I don't really understand the link between initial state and flattening the data. I would have thought you need join-key and ident and then I guess it is possible to walk the query adding :join-key ident to the app-state whenever you meet a 'sensless' join key

claudiu12:04:12 Fulcro kinda does all the query parsing stuff for you. I just think in terms of component (ident, query, instial-state) + linking the component to the parent + state hash-map, most of the time.


Only doing clojurescript & fulcro in my free-time so might be missing something 😅 But it's working pretty well for me on a few decently complex projects 🙂


Yeah, just trying to understand what's going on under the hood. My interest is mostly academic at the moment, I appreciate that if I just read and followed the docs things would probably just work out without fully understanding how. Have one project in production, but am also shipping things in rum, reagent, re-frame... The model is the one that interests me the most, but I'm motivated more by seeing where I can push it more than getting things done atm.


Ahh oky doky 🙂 I'm more of a hands-on learner... The thing I found most useful is just to lein new fulcro my-app, add fulcro-inspect chrome extension (it's crazy cool for debugging/understanding) and then just copy paste the demo code from the book and experiment 🙂


I'm more interested in the underlying concepts, than specific syntax, but syntax is sometimes required for illustration, which is the only place i think or fulcro needs to be mentioned.


Yes, that's good advice, I would say I'm more of a hands on learner too.


🙂 fulcro does a lot, and the architecture behind it seems crazy nice... There is quite a lot to learn, if you're interested in a brain twister of how well it's designed and how far can you push it then the fulcro incubator stuff think is just crazy nice especially state-machines


Thanks, looks like a nice read. I think a lot of nice things fall out of the basic model, which is what originally caught my interest. Fulcro certainly covers a lot of ground; it's hard trying to keep up.


Yep. The basic model is pretty nice and you can get really far with just that... Ex: never used form-state although that would certainly make my life a lot easier. Fulcro has a decent upfront learning curve but the end result is a pretty solid and easy to maintain/enhance code-base. Personally did not think I could ever say that about front-end 😄


I work on a number of them, but unfortunately none of them are open-source @danielstockton. To answer your original question (from my standpoint): Yes. all of the apps I work on join those components back to root. The combination of Fulcros’ initial app state and defsc concision mean it has rarely if ever been a problem. I occasionally use placeholder joins, but really not that often. The problem with Om Next doing that is you have to hand modify your parser, your initial state, etc….in Fulcro things are structured to pretty much just self-heal.

Drew Verlee19:04:16

at > you say fulcro makes a choice to let the user put idents in the client db as a cache, which is faster then if you used OM to pass a query to a parser expression. As a starting point, I think of a list of idents is a static set, a query is a function that runs over data, i'm not sure i understand how your comparing the two given these assumptoins

Drew Verlee20:04:02

I think your explaining that queries in fulcro are aware of they context their in and so only trigger based on changes to that context (user events, timmers, etc..). This mean's we don't run queries that wouldn't return new results. Maybe? It's not clear, your example is that the parser expression seems to imply the parsers expressions has did more computational work then fulcro now requires. e.g in the case of pagination, it searches all the people to get the next ten. As where fulcro just gets the ten idents. Thats the part i dont' get, how does it know the ten idents? wouldnt it have to know based off (in this case). the pagination number and the list of people, which is the same information i would assume the function you pass to the parser expression would need?


@U0DJ4T5U1 you’re missing when it knows. In Om Next you’d possibly use a dynamic query to change the component’s query (bit of hazard there, as Om Next’s dyn queries don’t persist across component mount/umounts, whereas Fulcro’s do), but anyway: in Om Next you change the query, and now the parser runs. The logic in the parser looks for some parameters on the queries and modifies a computation to find the correct ones and return them. In Fulcro, a mutation puts the 10 idents in place (instead of you changing the query, you call a mutation…same information from the ui (page number, etc)).

(set-query this {:query [...figure out the other stuff to include based on current query, and add parameters ...]})

;; OR

(transact! this `[(set-page ~{:list-id id :page n})])


what I was talking about in the optimization step is that with Om Next you’re then stuck with dealing with the optimization of that computation (of the list of idents) at every refresh no matter what…unless you cache the info. In Fulcro, your state that set-page creates is the cache. Same overall computation.


Now, your opinion of which way you like better…well, that’s a bit subjective..but I can point to a whole bunch of objective benefits from an engineering and maintenance standpoint when doing it the Fulcro way….and I can point out some “niceties” of using dynamic queries, but those seem, to me, more theoretical than practical…the big benefit of query parameters is when talking to the server IMO, and that is very easily supported by separation of concerns: Fulcro treats loads as loads, not a job for a parser against the “current UI concern”.


conflating the two just doesn’t work well in practice…what if you want to load the next 1000??? You don’t set a paramter on the UI query for that…you end up messing with an AST buried deep in the parser….total mess.


Wilker’s work on pathom does making writing a parser more tractable, and even attractive for portions of the UI…there are definite advantages to being able to “plug in” your choice of query interpreter, which I will likely “re-enable” in Fulcro 3 (but not exactly in the Om Next way). I’m also considering the possibility of letting you define “virtual data roots” that can each have their own query interpreter, which would allow for interesting compositions. Not certain if that makes things better, so I’m not sure yet; however, I think it is an interesting direction to explore. Even then, for much of the UI I write I will continue to stick to the simplified (default) model of the current Fulcro for the forseeable future.

Drew Verlee22:04:00

> what I was talking about in the optimization step is that with Om Next you’re then stuck with dealing with the optimization of that computation (of the list of idents) at every refresh no matter what…unless you cache the info. In Fulcro, your state that set-page creates is the cache. Are you saying that Fulcro you work within a cotext which knows to query from the cache, as where in Om Next, you would have to set that up yourself?

Drew Verlee22:04:28

I'll read a bit through the docs, i'm not sure i have even wrapped my head around a 10th of whats going on here. For example, i read the dynamic query section but don't understand it. i'll need to build some understanding of things mean in this context.


The only place where that is not true is where a join placeholder would not work anyhow: where there is a distinct (non-unique) component that is placed into state at a “future time”…in Fulcro, the addition of the ident pointer to satisfy the join is seen as the (trivial) cost of doing that business, and with load targeting doesn’t even require a mutation.


Ok, thanks, will give it a watch


I have existing form that loads data from the server (using the edit-existing pattern from the demos). On blur I am triggering a mutation to fetch some updated values from the server (based on the values of the field). Whats the best way to update the form data on response of the fetch?

Drew Verlee19:04:39

Are the fulcro video's still one of the recommended ways to get started?


Think so. A few things might be a bit outdated like the lein template setup


Are we to use f/form-root-key w/ the newer forms?