Fork me on GitHub
#fulcro
<
2020-08-17
>
bbss04:08:24

@souenzzo thanks for the effort, very helpful resource

Tyler Nisonoff05:08:14

Anyone have advice for using client state such as current-user in an RAD form (`defsc-form`)? Lets say I have an entity Item with attribute item/owner that I want to automatically set to the logged in user when the form is saved - I tried messing with fo/query-inclusion but couldn’t seem to get anything added to the form’s query. Or is the right way to do this to handle this with save-middleware ?

Helins13:08:08

Isn't it kind of expensive to generate an uuid for every transaction ? Does it mean that heavy local mutations (say, complex animations) would be better served by swap! ing on the state atom and manually scheduling a render ?

tony.kay14:08:29

Also, a UUID is not computationally expensive…it isn’t cryptography

tony.kay14:08:16

I can generate about 300,000/sec in js on Chrome.

tony.kay14:08:20

@adam678 there are all sorts of things you’d do in a complex animation…i.e. you’d probably use component local state, not an async transaction. Fulcro is about full-stack data model, not gaming.

tony.kay14:08:21

I worked on a prototype of something using three.js where I wrote a separate reconciler (three controls a canvas) for sync’ing data model with UI…Fulcro was used for doing the forms and full-stack data interactions, and loading a normalized data model representing the objects in the scene, but was not involved in the 3d rendering. You do what is right for the project in question. No tool can solve all problems.

Helins14:08:00

@tony.kay Yep, right tool for the right problem. It's just I am still wrapping my head around the API. I am recording data (let's say 20 updates per second), this data figures both in a rather complex canvas animation and part of it is in the DOM as well... Somewhat non-trivial, I am trying to figure out how to organize this. Thank you for your fast answers, I will probably hang out here for a little while yet! And thank you for building all this, it is quite a beast.

tony.kay14:08:50

Right. Fulcro is very pluggable. You can replace the tx processing system, the rendering routine…hell, you could make it render via ncurses on a UNIX ascii terminal on a 300 baud modem if you really wanted to (i.e. no React).

tony.kay14:08:30

Some of the bits are there to make reasoning in a distributed system a bit easier…and some are legacy compatibility

👍 1
Michael W14:08:31

@tony.kay Any chance you could put an example of adding a new type that isn't derived automatically to the fulcro-rad-demo? Or add some documentation? I am trying to create a couple new types for :url and :link (for counting clicks) but I am not very good with clojure yet so I am not making much headway. A simple example would probably give me enough to build on. Awesome project, thanks so much for creating it. I am really liking how simple it can be to iterate on app ideas.

tony.kay15:08:42

@michael819 did you look at the fulcro-rad-semantic-ui source? the all_controls namespace…also, I thought the demo had something…what do you mean by adding a new type? You mean full-stack db-wise? I would do URL and LINK as renderers of a string type.

tony.kay15:08:01

hm…guess not…here’s a sample from my prod app:

(rad-app/install-ui-controls! @app-atom
    (-> rad-sui/all-controls
      (assoc-in [:com.fulcrologic.rad.control/type->style->control :instant :starting-date] date-controls/midnight-on-date-control)
      (assoc-in [:com.fulcrologic.rad.control/type->style->control :instant :ending-date] date-controls/midnight-next-date-control)
      (assoc-in [:com.fulcrologic.rad.control/type->style->control :date-range :default] date-controls/date-range-control)
      (assoc-in [::form/type->style->control :string :ucv/ssn] render-ssn-field)
      (assoc-in [::form/type->style->control :string :ucv/tysys-account-credential] render-tysys-account-credential-field)
      (assoc-in [::form/type->style->control :string :ucv/phone-number] render-phone-number-field)
      (assoc-in [::form/type->style->control :string :ucv/color] render-color-field)
      (update-in [::form/type->style->control :decimal] merge {:USD            rad-numerics/render-currency-field
                                                               :ucv/percentage rad-numerics/render-percent-field})))

tony.kay15:08:02

so something like (assoc-in [::form/type->style->control :string :your-ns/link] render-link) would be for a form control that can edit links

tony.kay15:08:12

it’s all just a big open map

tony.kay15:08:34

see rad-sui/all-controls for the different spots you can add stuff in the SUI render plugin

tony.kay15:08:06

then you select those with field-style

Michael W15:08:10

@tony.kay I see it now, I did not look in the fulcro-rad-semantic-ui repo. It looks like the namespace is com.fulcrologic.rad.rendering.semantic-ui.semantic-ui-controls

tony.kay15:08:14

RAD itself does not define how a renderer works…so that will always be render-plugin-specific, though RAD does define the concept of styles…but still it’s all open maps, so a plugin could easily veer off that path

tony.kay15:08:25

well, I guess there are some “entry point” definitions in there for getting to the renderer…just saying, the nested structure is amenable

Michael W15:08:36

@tony.kay Thanks again for a great product, I hope to get good enough I can contribute. This is exactly what I needed to create what I had in mind.

👍 1
tony.kay15:08:39

It is a lot more fun to work with the higher-level data than do all the low-level messing around all the time.

mischov17:08:33

I've been following along with a series of blog posts on using Fulcro and for authentication they needed to add an asynchronously fetched value to the headers that their remote always sends. They basically ended up solving the problem by copying wrap-fulcro-request and updating it to use a go block inside of the returned function and then copying fulcro-http-remote and using a go block inside of the transmit! function too. I was wondering if there was a more idiomatic way to do something asynchronously in a remote's middleware? For reference, blog post can be found here: https://chrisodonnell.dev/posts/giftlist/api_auth/

tony.kay19:08:53

unfortunately no. My assumption when I designed that was that async stuff would happen outside of the middleware (e.g. you’d do auth, a cookie would get set, and you’d just “have it” by the time middleware was involved). Same with token exchanges…seems like putting the value in a top-level atom and just accessing it from middleware is sufficient. You either have it or you don’t; however, if you’re trying to do something where the middleware itself makes some kind of side-band async request all bets are off for prebuilt stuff…I mean, you could copy the source of the existing remote and augment it with core async and expect middleware to return channels…but I’m not interested.

tony.kay19:08:22

so, I support the blog post sol’n, basically 😄

tony.kay19:08:53

it would be trivial for someone to make an OOS remote for Fulcro that adds extra features…after all, a remote is just a map

mischov19:08:31

Cool, thanks.

souenzzo20:08:09

When I want to return 2 components after a mutation I do that

(defsc MergeReturn [_ _]
  {:query [{:>/a (comp/get-query A)}
           {:>/b (comp/get-query B)}]})

(m/defmutation operation
  [_]
  (remote [env] (m/returning env MergeReturn)))
This MergeReturn still a valid solution or there is a better way to do that?

tony.kay21:08:24

To anyone using Fulcro Spec test helpers: I just release 3.1.6, which improves: • Mocking errors. Recently that had regressed to just say ExceptionInfo, which was worse than useless. Now reports the exact nature of the mocking failure. • Exception stack traces: Are not printed inline with the test failure, and are filtered/pruned according to two dynamic vars you can control (for terminal reporter in CLJ/REPL). See https://github.com/fulcrologic/fulcro-spec/blob/develop/docs/index.adoc#controlling-stack-trace-output

🎉 2
Tyler Nisonoff21:08:58

The RAD book states: “The subforms themselves can act as standalone forms, but will not be running their own state machine unless you route directly to them.” However, When I try to route to a form that is a subform of another via form/create! this MySubForm , it takes me to the parent form (I guess this isn’t routing directly to it :man-shrugging: ) — anyone know how to route directly to one? Im trying to create the ability to create nested relations, but edit a specific subcomponent. For example, if an Account has many Portfolios, which has Many Projects, I want to be able to add new portfolios from an account form, but if I want to edit a Portfolio, I want to be able to zoom into a PortfolioForm I can do this with separate forms fwiw

tony.kay22:08:38

So you can create one that way, but how do you expect it to be “linked” to the parent??? Not sure why routing is not working for it, unless you didn’t give it a route prefix.

tony.kay22:08:27

At the moment, RAD, by itself, cannot handle you creating some child outside of a parent context. To do that you’d have to probably support some virtual reverse reference attribute that could stand for the parent’s ID, and fix that up on save in save middleware on the server.

tony.kay22:08:24

e.g. :portfolio/account which is not reified in DB, but which is an attribute on the form, which in turn you pass in as an argument to create’s :initial-state

Tyler Nisonoff19:08:45

One thing I noticed about setting :initial-state is this becomes a "before" value in the delta saved to the db. for non-to-many fields, this is fine, as to-one-txn , as if there is a non-nil after in the delta, it will be used directly however in to-many-txn , we look at the set/difference of the before and after and save that, so I guess its dangerous to use :initial-state for default values of to-many fields? Or am I misreading the logic

tony.kay19:08:34

I’ve lost context…so no idea what you’re getting at. To-many in initial state works fine in normal apps. If you’re doing something strange, then, well, you have to figure it out

Tyler Nisonoff19:08:44

the original context was setting default values for a new form It seems that if you set initial state on a from.create for a to-many field, it will be filtered out before saving to the db. Will figure out a workaround

Tyler Nisonoff20:08:05

*default values set from context from outside the form (i.e. the component that triggers the form.create!)

tony.kay20:08:38

new form should not use initial-state

tony.kay20:08:59

use UISM to control form logic flow. pre-merge is a reasonable broad-stroke way to do it as well

👍 1
tony.kay20:08:54

initial state is about exactly one thing: The initial value your app state will contain for the very first animation frame displayed by your app on mount.

tony.kay20:08:17

Full stack operation is a sequence of follow-on operations. The only form that you might pre-configure in initial state would be something like a login form, where it isn’t really a full-stack persisted thing…it’s just a form…but in that case, you don’t really need fs support, since there’s never a need to undo/save. Maybe a “Contact Us” page? Yeah, initial state for forms is mostly an anti-pattern.

tony.kay20:08:26

forms have a lifecycle, and at certain predictable times, they are initialized/loaded/saved

tony.kay20:08:27

but initialized with what: content from server, new content, etc. More than one possible entry to “initial”

Tyler Nisonoff20:08:54

got it, makes sense, thanks for the response

tony.kay22:08:14

Then when you save, add in save middleware that would look for that attribute and fix up account to point to that new (or existing) portfolio

tony.kay22:08:27

and if you want edit to work, you’ll have to add a resolver that can “figure out” :portfolio/account when given a portfolio id

Tyler Nisonoff22:08:35

Got it — I’m trying to figure out how to handle forms with “implicit” fields with fixed values set from previous context — for example, I want to create a “New Portolfio” form that allows you to create a new portfolio for your user — my best way of doing this with RAD is to create a form on the account layer, have the only field to be edited be :account/portfolios I originally tried to have a reified :portfolio/account field, instead of :account/portfolios but I couldn’t figure out how to pass this to the form (asked about this above https://clojurians.slack.com/archives/C68M60S4F/p1597641494045800) > e.g. `:portfolio/account` which is not reified in DB, but which is an attribute on the form, which in turn you pass in as an argument to create’s `:initial-state` (edited) Ah that makes sense, it could be a non-reified attribute that I pull up.. If I did that, then my :portfolio/account approach would probably be viable

Tyler Nisonoff22:08:22

(form/defsc-form AccountPortfolioForm [this props]
  {fo/id           account/id
   fo/attributes   [account/portfolios]
   fo/route-prefix "account-portfolios"
   fo/title        "Edit Portfolios"
   fo/subforms     {:account/portfolios {fo/ui          PortfolioForm
                                         fo/title       "Portfolios"
                                         fo/can-delete? (fn [_ _] true)
                                         fo/can-add?    (fn [_ _] true)
                                         }}

Tyler Nisonoff22:08:05

Thats one way I was achieving this sort of view

Tyler Nisonoff22:08:01

> Not sure why routing is not working for it, unless you didn’t give it a route prefix. The routing would take me to /account/create/<new-temp-id>/portfolios/create/<new-temp-id> My expectation was it’d take me to /portfolios/create/<new-temp-id>

tony.kay23:08:40

but did you give Portfolio it’s own fo/route-prefix?

tony.kay23:08:37

and when you call form/create did you pass it Portfolio? Was the route prefix unique?

tony.kay23:08:02

You can pass :initial-state in the options to form create to pass in default values for something you’re making that is new

Tyler Nisonoff23:08:48

> but did you give Portfolio it’s own `fo/route-prefix`? yeah, that’s how Fuclro is somehow appending /portfolios/ (or whatever I change the route-prefix to) in the route > and when you call `form/create` did you pass it Portfolio?  Was the route prefix unique?

(ui-dropdown-item {:onClick (fn [] (form/create! this PortfolioForm ))} "New")
Yeah, i changed the route-prefix, and it links to the subform route param /account/create/<new-temp-id>/<whatever route-prefix-i-choose>/create/ Its not blocking me, as I can create a shared options map and pass that to to a new form with a unique symbol / route-prefix, was just curious if I was doing something obviously wrong.

tony.kay23:08:08

Did you add the Portfolio thing to the ROUTER? Otherwise it is always a child of the account

tony.kay23:08:47

routing is a recursive search…if it isn’t at the routing layer you want, then it will end up in a nested route 😜

tony.kay23:08:32

D.R. should probably give a warning for that, since there is no router in-between, so the routing isn’t actually going to work right when you do that

Tyler Nisonoff23:08:37

yeah, its in the router