Fork me on GitHub

I have no idea if this is a problem in fulcro source, or in cljdoc


I'm guessing latter though, but don't any shadow projects then work there

Jakub Holý (HolyJak)08:01:55

You are right. Let me fix it.


I see the fix, of course!


NPM requires using strings is a standard feature, and necessary


some NPM stuff starts with @ and has / etc


Sure, just wasn't sure how to make cljdoc deal with it, but of course I hadn't noticed that the require was in a cljc and wasn't inside the conditional

Jakub Holý (HolyJak)17:01:32

the only problem is the require should have been wrapped with #?(:cljs ...)


oh, ooops 😄


thanks for the PR

Jakub Holý (HolyJak)08:01:26

I think it is actually me who broke it in the first place 😅


Bah, I'm having problems understanding how to model queries when refactoring into separate components. I have a frontend fulcro db with :events, :legs, and :feature-flags . Then Root had UI parts where it was mapping over the events data giving each event to EventRow, legs data to each Leg, and feature-flags was used in navigation where showing the events was either active (and in navigation, or not). The navigation was in many places, next to events and to legs. Now I wanted to instead wrap the event part and legs part into new components without changing the data. So then the tree would be from Root→EventList→[Event]+Navigation and Root→LegList→[Leg]+Navigation. But how am I supposed to write the queries now? I could "cheat" and make the EventList do a link query, but that will break the moment where I want to split the :events into :warning-events and :error-events and render two lists using the same component. I was thinking about Pathon placeholders, but couldn't figure out how the Root can give both the feature flags and the list of data to each child.


I could just make the Root, EventList and LegList "know" that they have to pass the feature-flags data even though they don't use it, but I was thinking is there a way to make the query just flow up from the Navigation (which actually uses the feature-flags). Then again, I couldn't figure out the right queries even without the navigation, yet.


Without navigation it would seem logical that Root for example queries for {:legs (get-query plan/LegList)} and then the list for {:>/legs (get-query Leg)}, but something is screwed up with my ident for the list (fn [] [:component/id ::EventList]) and the

(fulcro.merge/merge! app
                       {:legs [#:leg{:id 1 :type :flight :origin "Oslo" :destination "Stockholm"}
                               #:leg{:id 2 :type :train :origin "Stockholm" :destination "Turku"}
                               #:leg{:id 3 :type :ferry :origin "Turku" :destination "Stockholm"}]
                        :events []
                        :feature-flags {:limit-origins true
                                        :dark-theme true}}
                       (get-query Root))
is merged into the DB as
 [[:component/id :carbonlink.travelplanner.plan/EventList]
  [:component/id :carbonlink.travelplanner.plan/EventList]
  [:component/id :carbonlink.travelplanner.plan/EventList]]


Merging with merge-component! straight into Leg with :append legs makes the db look sensible, but the query cannot get anything out of it; db->tree just puts empty maps inside the resulting tree under :legs. I wonder if this be much simpler if I'd use client side Pathom instead of merging by hand. I suppose the components could then query whatever they wanted at any level.


Like in the Root doesn't know what the Menu wants, it just passes on the query, and there is no such thing as "menu" in the data. The Menu just knows to ask for the data of two separate things.

Jakub Holý (HolyJak)14:01:47

For the flags, I would put them in the root of the client DB and have navigation all for it using link query as described in For the data, you control where they are when you merge them in so instead of putting them directly in Root, put them where you want them to support the new, richer structure. But you have essentially said so above I believe.

Jakub Holý (HolyJak)14:01:26

Beware : placeholders like :>/legs only work in Pathom, ie in df/load (and then also on client DB since it will see :>/legs just as a standsrd attribute in the data)

Jakub Holý (HolyJak)14:01:16

Doesn't the blog post linked above have an answer that would suit you? Why do you want to introduce the extra level of Event List? Just to make Root simpler? Then normally :> would be the correct solution if you had Pathom. If you merge data manually then adjust them accordingly - instead of :legs [..] in Root put there :>/legs {:leg-list [..]} - targeting should allow you to do that.


Yeah, I was already using link query for the feature-flags, but problem was that they didn't get passed in the props. And yeah, I kinda figured out that Fulcro doesn't have any special logic for the placeholders, so trying to change the UI component hierarchy without changing the manually merged data was just… impossible. And also yes, the Root was getting really big, so I wanted to put less logic there and more in separate components. I wanted a way to modify the UI without modifying the data. But I think I had a misconception of doing it on the wrong level: the fulcro DB has always the same structure as the component tree, and mapping the data to that should happen in Pathom. And I wasn't using it to make it simpler, but it was too simple at this point. Simple in amount of layers, but complected, perhaps. So I guess such a refactor, introducing more middle layer UI components has three options: 1. Make the new components UI only in that they don't have a query, and the Root knows or defines the inner structure and queries, and passes them as children to the middle component. 2. Change the structure of the data that is merged in. 3. Use client side Pathom, and let fulcro create the DB structure, keeping the data and UI layers cleanly separate


Third option seems far superior to me in anything except the shortest run, and smallest apps


A separate, but related question is that how should I even do data splits. So if I have a list of all events, and want to instead show warnings and errors separately, should I be passing the whole event list with a flag to two EventLists and let them filter, or should I populate the db with two different lists (error-events and warning-events), and then simply pass the relevant list to each component?

Jakub Holý (HolyJak)13:01:42

Both are perfectly fine solutions, IMO. Depends on your use case and preferences.


Thinking about my current refactoring problem, I guess I will go the route of client side pathom, and let the filtering happen there, not putting any data management logic in the render functions. Though I wonder how I should handle idents for components that are not really data driven nor singleton

Jakub Holý (HolyJak)16:01:17

What does it mean "not data driven" in this context? Why does the ident matter then?


Meaning that there is no lists entity in the data, that would tell that there are error-events and warning-events, but the data has many facets and the UI just wants that particular way to filter and divide them. But I cannot do anything like (map #(ui-event-list %) lists), but just manually have to define them. And as for the ident, I thought that unique idents are always required to map the incoming data to the fulcro db. But is it really?


In the example you link the Root has to know the inner structure of the middle components, but I am trying to avoid that as it's not necessary. A generic header component doesn't know what kind of things it will get; maybe title, maybe icons. But the EventList in my case will only ever render the same children, the seq of Event. Only the data changes between different list instances


So concretely I would prefer calling (div (ui-eventlist warning-events) (ui-eventlist error-events)) instead of something like (div (ui-eventlist (map #(ui-event) warning-events)) (ui-eventlist (map #(ui-event) error-events)))

Jakub Holý (HolyJak)17:01:24

I understand. Then just add an artificial link from Root to the event list entity. Similarly to what Tony does with the friends and enemies lists in the Book?

Jakub Holý (HolyJak)17:01:54

Though I still believe that EventList as a UI-only component is still a valid solution. Yes, Root needs to know where the lists are but is that a problem? The event list is nevertheless ignorant of its data, it only maps Event over it, no? So it doesn't need to be stateful.


I was trying to find the answer too late in the book, around chapter 7. I'll reread chapter 4


Rather I don't want the root (or any parent) to replicate the inner component structure every time the list component is used. Would make refactoring super annoying, as it wouldn't be enough to change the EventList itself, but also every call site.


Also I'm trying to figure out the pattern, or perhaps the architecture to use here for cases where the EventList is not simply mapping over the data and rendering other components, but does need to also check what is inside. For example to render something differently when there are enough events (some kind of folding mechanism that triggers only after certain size)


The last two situations in your blog post is more about what I mean, kinda. But instead of having some unknown amount of Teams coming from the server, or just one Menu, I want some arbitary but fixed amount of parallel components, defined in the UI instead of the backend


Need some advice. I am writing a web app in Fulcro (not RAD) that is derived from fulcro-template. I have Stripe in my app, which redirects out of the app and then redirects back to the app. What is the best way to handle the return redirection? I know I need to adjust my middleware but how do I set the app to a different starting point than just the initial load? Any suggestions, advice, etc. would be welcome.


Hi! What is the use case to starting the app on a different initial load?


I am writing a progressive web app (PWA). I have to redirect to Stripe within the same brwoser, i.e., not a different tab, and then Stripe redirects to my app.


The return redirect will cause a “re-render” of your app. This is xant you want to avoid?


I would like to return to the middle of my app, not at the start. How do I indicate this to the front-end?


Should I do a load at the start to check the state?


That is actually very complicated, given my setup.


> Middle of my app You mean the screen, right?


No. My app will have state when it invokes Stripe, I want to return to the state (and corresponding screen).


Ok, I think we can pass a query param to the stripe return_url of stripe like . In your app you shoud check of the state params and dispatch the right load to retrieve the state.

Jakub Holý (HolyJak)09:01:24

I think ☝️ is a fine idea.

sheluchin22:01:00 I'm having trouble getting this to work. What is meant by: > if you put a report on-screen initially (or don't use dynamic router), then you must call this to start your report. Is the right way to call start-report! somewhere like in the parent component's :will-enter when the body of that parent "puts the report on-screen" with an element factory like (ui-foo-report report-props)?


If you look at the source generated by the defsc-report macro, it emits a will-enter that starts the report. BUT, if you DON’T use routing, that won’t get called. So, if you put a report on-screen (without routing being responsible), then YOU must do the will-enter logic.


Thanks @U0CKQ19AQ. I got it working by using the parent's (a defsc-form) :will-enter to call start-report! after its usual logic:

  (fn []
    (form/start-form! app coerced-id TabbedForm route-params)
    (report/start-report! app FooReport {::report/id ::FooReport})))
adding the report's query to the parent's:
fo/query-inclusion [{(comp/get-ident FooReport {}) (comp/get-query FooReport)}]
and finally passing the relevant portion of props to the report factory like:
(ui-foo-report (get props (comp/get-ident FooReport {})))
If that looks about right, would it be helpful for me to add an example to the RAD docs?


If you want to improve the documentation, please feel free 😄 I did a lot somewhat recently for forms, but yeah I think details like this, when missing, would be great to add

👍 1

So yes, the parent’s will-enter, the mutation that modified state to cause it to be on screen, shouldComponentUpdate, a hook…there are many many wayts for it to end up on-screen.


you just have to do it