Fork me on GitHub
#fulcro
<
2020-05-04
>
dvingo00:05:37

I can highly recommend guardrails. It feels like i'm living in the future when using them ha

mruzekw00:05:22

Haha, where do you use them in a pathom/fulcro app?

mruzekw00:05:39

On the components? Fns? Normalized db?

dvingo00:05:30

mostly on plain defn

dvingo00:05:10

helps catch where in a chain of fn calls some bad data is showing up

dvingo00:05:12

also heavily used in data model layer. I started having lots of fns take a map and destructure the keys they need, which allows you to flow through maps of data without having to pick apart the pieces and then call with positional params. Then spec the maps and get type checking on the keys

tony.kay01:05:33

@mruzekw guardrails was designed, in particular, for pretty much what core.typed is doing, but using spec: Make it possible to have an opt-in “type system”…but I don’t like instrument because it always throws. So, guardrails is meant to: • Be easy to type: it’s just an extra array after arglist, so it doesn’t clutter the normal syntax and can be easily ignored • Be easy to turn on/off at runtime, so the performance hit can be chosen • Be possible to tell you about possible problems without throwing exceptions. Often you just wrote the spec wrong…let it run, and fix the spec • Be possible to elide the specs so they don’t bloat the build! This one is nefarious in cljs. Every spec adds more js to the output. >def and >defn, when disabled, emit no code. dynamic languages, in general, can be very frustrating when you don’t know what the data shape should be. spec can be maddening with the syntax and lack of co-location, and throwing as checks. So, guardrails represents my own personal preference: advisory checks I can turn on while developing, and off during production.

👍 16
AJ Snow01:05:38

if I define a component of my data graph in a def can it be swapped with a transact?

mruzekw02:05:01

Thanks for that explanation, @tony.kay. Which parts of a fulcro app would you recommend using Guardrails? And would it work on fulcro macros like defcs ?

tony.kay03:05:40

defsc checks its own syntax. Remember that macros are compiler extensions, and should be treated that way: They should provide good error messages about the notation that then invent. They also exist in CLJ-land, and if you put a spec on them via fspec CLJ will already treat them as a compile-time check. So, no, there is no need for GR to apply to macros. The rest of that answer is “wherever possible”. You’ll learn the pros and cons as you go: for example, map? ends up checking all keys of the map that have specs…which can be very expensive at runtime, and can be annoying for intermediate values (see comments in CLJ community about NOT spec’ing state). It’s too much to ask…I could write a book

mruzekw03:05:31

Gotcha, alright. Thanks

tony.kay04:05:00

updated the RAD book a bit, and put a big marker in where I stopped. The Demo is still the best resource, but trying to keep the book in reasonable shape

❤️ 16
murtaza5205:05:29

I want to test the generated resolvers in the clj repl, is there an example of passing query data structure to the parser ?

Chris O’Donnell05:05:58

(parser {} [:my/key1 :my/key2])

murtaza5207:05:31

(parser {} {:saless/id 1}) => {[:saless/id 1] #:saless{:id 1}} :saless/id is an invalid attribute which does not even exist in datomic, why does it return me the above, and not nil.

murtaza5207:05:00

so there should not even be a resolver which matched it

wilkerlucio07:05:38

@murtaza52 this query is invalid, EQL queries must be vectors

murtaza5207:05:12

@wilkerlucio is this a valid query (parser {:sales/id 1} [:sales/id]) ? Do params from the fulcro are they going to be in the first hash-map, will they be under a specific keyname ?

wilkerlucio07:05:02

@murtaza52 it is valid, but not doing what you expect, if you want set a initial context you can do: (parser {::p/entity (atom {:sales/id 1}} [:sales/id])

murtaza5207:05:36

@wilkerlucio thanks so the params from the UI will be in an atom under the ::p/entity key

fjolne08:05:06

@murtaza52 ::p/entity is rather an internal pathom thing, UI params are transmitted in the query itself and can be grabbed from there say, you have (df/load! [:item/id 1] Item {:params {:some-param 1}}) then the transmitted query is [({[:item/id 1] [:item/name]} {:some-param 1})] and you can grab the params inside the resolver as (-> env :ast :params) (you can also see what queries are transmitted for any load/mutation in fulcro inspect) there’s also a distinction between inputs (`:item/id`) and params (`:some-param`), the transmitted inputs are available to the 1st resolver in the 2nd arg. there are cases when you want to propagate inputs/params to the subsequent resolvers, it’s a bit more complicated but fulcro rad repo has it somewhere

fjolne08:05:09

here it is

(def query-params-to-env-plugin
  "Adds top-level load params to env, so nested parsing layers can see them."
  {::p/wrap-parser
   (fn [parser]
     (fn [env tx]
       (let [children (-> tx eql/query->ast :children)
             query-params (-> (->> children
                                   (reduce
                                     (fn [qps {:keys [type params] :as x}]
                                       (cond-> qps
                                               (and (not= :call type) (seq params)) (merge params)))
                                     {}))
                              (dissoc :pathom/context))
             env (assoc env :query-params query-params)]
         (parser env tx))))})

murtaza5210:05:56

@fjolne.yngling thanks for the details. I have defined my schema on the server and am trying to play with the resolvers that were generated by RAD. So given the parser fn returned by com.fulcrologic.rad.pathom/new-parser, I want to pass it EQL queries and params.

murtaza5210:05:04

Based on the above it could either be - (parser {:ast {:params {:sales/id 1}}} [:sales/line-items]) or (parser {:query-params {:sales/id 1}}} [:sales/line-items]) (based on the query-params-to-env-plugin)

fjolne10:05:21

@murtaza52 :sales/id is not a parameter in EQL terms, it’s an input. the call would be (parser {} [{[:sales/id 1] [:sales/line-items]}])

folcon13:05:54

Just wondering about this, I have a vector in my sim that’s appended to, it essentially internally represents a sort of inventory, so each entity has its own one, however for pathom/fulcro to work well with it, I seem to need to give the items unique id’s? Is this true? Or is there some way that I can generate a dynamic id for them?. I mean I could certainly do something like:

(defn add-inventory-ids [{id :ent/id :as ent}]
  (update ent :inventory #(vec (map-indexed (fn [i item] (assoc item :id (str id "-" i))) %))))
(let [ents [{:ent/id 1 :inventory [{:name "an item"} {:name "another item"}]}]]
  (mapv add-inventory-ids ents))

Chris O’Donnell13:05:04

I think it should only be necessary to assign a unique id to your inventory items if they're used outside the context of your inventory's :inventory (aka if they need to be normalized into a table of their own). It should be valid for your :inventory attribute to have a value of [{:name "Tomatoes"}, {:name "Onions"}].

folcon13:05:42

Hmm, I’m just wondering if it causes items to not be refreshed correctly? I keep getting react warnings about not having keys for example…

folcon13:05:10

Normally I see vector indexes being used as keys, but I’m not certain it’s happening here…

Chris O’Donnell13:05:08

That's a separate, non-fulcro issue. If you're rendering a list of react components, you need to assign a unique react key to each component so React knows how to tell which item is which. (Check out https://reactjs.org/docs/lists-and-keys.html#keys for more info.)

Chris O’Donnell13:05:31

The index isn't a great React key, since you can run into issues if you reorder your list items.

folcon14:05:38

Yep, however it worked surprisingly well previously =)… Just wondering what pattern people had for doing that here?

Chris O’Donnell14:05:58

Generally I look for a unique identifying attribute

Chris O’Donnell14:05:27

For example, if the inventory items in your inventory all have unique names, you could use the :name attribute as the react key.

folcon14:05:36

Which isn’t great in a seq like this, I might have to create some id’s then… or do a uuid?

folcon14:05:48

well no, because people can hold multiples of the same thing ;)…

Chris O’Donnell14:05:50

You could use a uuid if you have to, yeah. 👍

Chris O’Donnell14:05:48

How do you tell the difference between two inventory items with the same name? Is there value in storing them as separate list items rather than something like a tuple ["Tomatoes", 2]?

folcon14:05:24

They have other attributes that people will want to track, things like purchase price, so they can tell if selling it nets them a “profit”

folcon14:05:58

So two items that are the same, may be treated by the user as being different because they have a different providence…

Chris O’Donnell14:05:13

Sounds to me like it makes sense to assign them an id then!

folcon14:05:28

A fair point

folcon14:05:41

😃

😄 4
sova-soars-the-sora15:05:50

Multi checklist component exist?

sova-soars-the-sora15:05:26

https://github.com/fulcrologic/fulcro/blob/2.8.13/src/main/fulcro/ui/forms.cljc#L1041 There's a checkbox component, a friend asked if there was a multi-checkbox component because he'd like to use multiple boxes at once... so you could check off several options or all of them The closest I can find is the single checkbox

sova-soars-the-sora15:05:30

Pardon the terse initial message. (exists? checklist-plural) not a good way to interact with humans xD

tony.kay16:05:18

@sova Fulcro is generally not interested in providing a UI component library. There was an early attempt at providing bootstrap, but I abandoned that long ago. The React ecosystem is chock full of UI components. Use them. Fulcro provides form state support (you should be using v3, BTW, if you can), which works very well for me in various production apps: but it is interested in helping you manage the persistent state full-stack….not the representation in the UI.

sova-soars-the-sora16:05:47

Uh, okay, so there is a checklist component I want to use it like an array, does it make sense just to have a bunch of independent components?

sova-soars-the-sora16:05:57

Thanks for the reply. I dig that it's not so UI-heavy, but there is a checklist component and I'm wondering if it makes sense to try and extend this to create a singular component that can have multiple checkboxen, or just use this like a cookie-cutter / stamp.

tony.kay16:05:51

form field rendering was completely abandoned in F3. Don’t use it in 2.

tony.kay16:05:07

1. It is not platform independent (form state can be used with Native or DOM), mixing the two was a bad idea. 2. There are tons of UI component libraries…don’t maintain your own (which applies to my library…it isn’t a UI component library, so I won’t maintain one).

tony.kay16:05:26

My early efforts were misdirected. I have learned from the error of my ways. RAD is the updated approach (where I do provide sample wrappers using semantic UI React components). The library itself does not provide UI components, but makes it very easy for you to use a plugin that is RAD-aware, and extend that. rad-semantic-ui is a sample of such a plugin, but I do not personally plan to round it out any more than I need for my own business uses.

folcon16:05:48

Re RAD, now that you’ve updated the docs, would it be worthwhile reading from there or is it better to look at the demo as a starting point?

tony.kay17:05:34

the book, up to the marker, is fine.

👍 4
tony.kay17:05:46

the rest of the book is probably pretty close

tony.kay17:05:56

so, to get an idea of how things work, the book is not bad

janezj21:05:17

Hi, I think that I don't understand some of key concepts of fulcro compoments. because all my components are without initial state and I am getting react errors onchange "com.fulcrologic.fulcro.mutations:244] - ui/toggle requires component to have an ident." This component is a single instance, created from Root

(defsc PerfTest [this {:srv-test/keys [server attach]}]
  {:query         [:srv-test/server :srv-test/attach]
   :ident         (fn [] [:component/id ::perf-test])
   ;:initial-state (fn [params] {:srv-test/server "sisi:25" :srv-test/attach true})
   :initial-state {:srv-test/server "sisi:25" :srv-test/attach true}
   }
  (div :.ui.form {:style {:backgroundColor "#8ee" :padding "1em"}}
       (h3 "Performance test")
       (div :.field
            (dom/label "Attachment")
            (dom/input {:type     :checkbox
                        :onChange (fn [] (m/toggle! this :attach))
                        :checked  (boolean attach)}))
       (div :.field
            (dom/label "Server")
            (dom/select {:selected server :onChange (fn [evt] (m/set-string! this :server :event evt))}
                        (dom/option "localhost:1025")
                        (dom/option "sisi:25") ))

       (button {:onClick #(comp/transact! this [(mm/test-with-email {:server server :attach? attach})])} "Send test email")))

tony.kay21:05:24

Must be composed to Root

janezj21:05:46

(def ui-perftest (comp/factory PerfTest))

(defsc Root [this props]
;  {:query [] }
  (ui-perftest)
  )

tony.kay21:05:39

Please see videos/book. Basics of component are covered there in very much detail.

janezj22:05:00

I watched first 5 videos several times, and reread the first 4 chapters, I think that I have a blind spot for something. In your examples you have hierarchy of data model and nested components. My components are global, each with own state Independent from the root., The program I am working it is much more complex than this pieces and it works quite well on the server side. I don't understand the error message: "com.fulcrologic.fulcro.mutations:244] - ui/toggle requires component to have an ident." which ident? Is there a checklist, rules, that must be followed (like no ident on the root component).

Chris O’Donnell22:05:33

Yes, the root component cannot have an ident.

fjolne11:05:30

@UQ5QFFX54 the general rules are • compose queries • initialise state • pass props so your example would look like

(defsc PerfTest [this {:srv-test/keys [server attach]}]
  {:query         [:srv-test/server :srv-test/attach]
   :ident         (fn [] [:component/id ::perf-test])
   :initial-state {:srv-test/server "sisi:25" :srv-test/attach true}}
  ,,,)

(def ui-perftest (comp/factory PerfTest))

(defsc Root [this {:root/keys [perf-test]}]
  {:query         [{:root/perf-test (comp/get-query PerfTest)}]
   :initial-state {:root/perf-test {}}}
  (ui-perftest perf-test))

fjolne12:05:54

unless you’re using new fancy floating roots support, all your components should be composed in a connected tree starting from the root and components get data from passed props: queries are means for normalisation and client-server interaction, they don’t provide data per se

janezj15:05:03

Thank you, it works. You really saved my day.

🎉 4