Fork me on GitHub
#fulcro
<
2020-07-04
>
zilti11:07:40

Has someone here worked with fulcro-garden-css? I am not really sure how to use it... The readme is a bit confusing

Jakub Holý (HolyJak)14:07:44

Yes. Though something was missing in my setup because I had to add :css-include [Header Content] into my Root to get the styles from children working, which should not be needed thanks to the "autoinclude". The defsc Header then has this among its options (here i am using global styles for simplicity):

:css-global [[:.main-header {:display 'block
                                :padding-top "3rem"
                                :padding-bottom "1.2rem"
                                :background-color "#FFF"
                                :margin 0
                                :text-align 'center}
                 [:&__logo {:margin "0 auto 5rem auto"
                            :height "3rem"
                            :padding "0"}
                  [:& :svg {:width "auto"
                            :height "3rem"}]]
                 [:&__text {:margin "0 auto 3rem auto"}]
                 [:&__step-indicator {:margin "0 auto 0 auto"}
                  [:&--hidden {:display "none"}]]]]
So it is essentially LESS-like styling on the component. In my client.cljs in refresh/init I have
(com.fulcrologic.fulcro-css.css-injection/upsert-css "componentcss" {:component root/Root})

tony.kay17:07:20

The auto-include feature uses the query to find things. If you have css in things that are not part of your (static startup) query, then it cannot work, and you must construct the tree manually with the includes.

👍 3
Jakub Holý (HolyJak)08:07:42

@U2APCNHCN don't hesitate to send a PR to improve the docs 🙏 :)

zilti09:07:50

Oh I am working on that :) And if my auth stuff from the other day works out, I'll check back with you guys if that's either something for RAD even, or if I just release it as a lib for RAD.

❤️ 3
zilti14:07:36

Oh, I didn't even know about :css-global. Do the ones defined there propagate down to the children?

Jakub Holý (HolyJak)14:07:32

The create global styles (so class main-header etc), not prefix with the component name so that yes, they apply to any component on the page that uses the given css class name.

zilti14:07:23

And I assume the & "mangles" the name for local use?

zilti14:07:14

Or is that just going to literally be &__text as class name?

Jakub Holý (HolyJak)14:07:31

No, it does the same thing as LESS so here (notice the nesting) &__logo becomes .main-header__logo - http://lesscss.org/features/#parent-selectors-feature

zilti14:07:21

And if I interpret the source code of css_injection.cljc correctly, that "componentcss" is the id of the top-level element in your DOM?

Jakub Holý (HolyJak)15:07:18

I do not think so. I think it generates a <style ...> and perhaps uses the id there somehow. I don't have the app running so cannot check.

zilti16:07:11

Yes, it does generate a style tag indeed

zilti18:07:13

Okay, there's this one thing I don't get yet. When I use a defsc component A inside a defsc component B, how can I have defsc A load data from the database and have it available in its props? And if that isn't possible, how do I idiomatically access the data in the normalized database without having it in the defsc query?

tony.kay19:07:20

don’t think of “components loading data”. Components are UI. They display things. You logic should be triggered by specific things in your system, like initialization on startup, and user-driven (or time-based) events that trigger mutations/loads.

tony.kay19:07:09

something is always happening when you want a load: either you just started the app, or somebody interacted.

tony.kay19:07:34

If the interaction was a “route”, then the logic that triggers (or accepts) the route is where the loading goes

tony.kay19:07:34

Again, see RAD’s UISM implementations for forms or reports. Most everything there is either triggered off of a state machine handler due to a user interaction, or the result of a route.

zilti19:07:07

Well in this case I want to load when the component gets initialized. I have it in :componentDidMount currently. And it does load the data successfully into the database. But since the props are controlled by the parent...

tony.kay19:07:40

“props controlled by the parent” is important why? And CDM is a bad place for loading.

tony.kay19:07:14

“component gets initialized” is also too vague

zilti19:07:20

Hmm which place is better?

tony.kay19:07:22

initialized by what?

zilti19:07:35

By the parent component

tony.kay19:07:56

parent component doesn’t initialize things

tony.kay19:07:04

it isn’t an “actor” in a system…it is UI

tony.kay19:07:32

the USER does something…routes to a page, starts the app, clicks a button?

zilti19:07:40

Yes, but when, in a component B, I do (ui-component-a props) then ui-component-a (or rather ComponentA behind it) gets initialized?

zilti19:07:09

Not sure what else to call it. Displayed?

lgessler19:07:58

that renders component A inside component B but it doesn't have anything to do with the loading of data

zilti19:07:40

Okay, so :componentDidMount is a bad place to call df/load!. What place is better? Yes, so basically, it is routing. The user goes to the page, and the data is supposed to load. But I don't want to clutter the parent with it.

tony.kay19:07:53

or you could code a concept similar to that if you’re not using Fulcro’s dynamic router

lgessler19:07:05

i guess once reason why CDM is bad is that the component will be rendered but without data to hydrate it for a bit

lgessler19:07:50

is that the main reason or is there also a reason to be hesitant to co-locate loads with components?

tony.kay19:07:56

well, and a component itself probably does not know it’s own identity, and CDM can be called for spurious other rendering concerns (i.e. a parent’s key changes)

lgessler19:07:13

oh that's a good point

tony.kay19:07:35

coupling I/O with UI rendering is just a bad practice…subscriptions can be ok in CDM/CWU

zilti19:07:21

So I guess when I use will-enter, that means I have to put all the loading for that route into the parent, right? Well I guess in some way that might be the sane approach anyway

zilti19:07:16

Except for e.g. loading values for dropdown completion

tony.kay19:07:14

ok, so dropdown options are something I make an exception for

tony.kay19:07:44

I will sometimes put those in CDM, but not directly as loads. I’ll make some kind of cache for them, and CDM will have something like ensure-picker-options

tony.kay19:07:55

where if they are already loaded it can skip the load

zilti19:07:13

How would you check while in CDM though if they are loaded?

zilti19:07:23

You don't have access to the props yet

tony.kay19:07:26

but you still have to know what thing you’re linking them into, or you can just use link queries

tony.kay19:07:55

CDM has this…`(app/current-state this)`. The cache for your dropdown options would be well-known and normalized.

zilti19:07:37

Okay, I will look into that app/current-state

lgessler19:07:14

so rummaging around in app/current-state is acceptable when you're trying to do things like determine whether a load should happen on a lifecycle/routing hook? i found myself wanting to do it yesterday but wondered if i was going to end up with a tight coupling somehow. in other frameworks i think i might have been right, but since fulcro's db is normalized i think the coupling shouldn't be as tight

tony.kay19:07:21

you’re just looking at your database. The only mistake you can make there is to use data out of the db in your UI rendering without it being in the query. Such a use will fail to refresh properly, because shouldComponentUpdate will not see the change in props

tony.kay19:07:13

so, the easy rule: if you render it: query for it. If it is for some other purpose (stateful decisions in lifecycle) then go for it.

tony.kay19:07:01

same goes for swapping on the state atom. It’s your database. Fulcro will not re-render if you swap on the atom, but it’s just data.

tony.kay19:07:17

The “formalisms” of Fulcro: mutations, loads, UI queries…those have two main purposes: 1. To establish norms that everyone understands that aim to keep your code and data consistent and understandable. 2. Provide a way for Fulcro to comprehend that something has changed that requires a UI refresh. It is right to be cautious about reaching outside of them, since lack of understanding can lead you to get unexpected results; however, the overall system is pretty simple. queries fill props, components get props, SCU short-circuits rendering if props have not changed, mutations trigger rendering refresh (which is a query followed by sending the props). Loads are “fancy mutations” put data in the database from a remote.

tony.kay19:07:41

If you twiddle around with the state atom none of that “closed loop” knows about it. Simple as that.

tony.kay19:07:41

but rendering is a pure function (your UI) of that state atom reflected through the UI query

lgessler19:07:14

wow! so i should feel more free to read from the db and maybe make some other stuff in it as long as it stays clear of fulcro's moving parts... very empowering! thanks for explaining more, @tony.kay

👍 3