untangled

NEW CHANNEL: #fulcro
sfrank 2017-04-17T12:53:08.061989Z

@tony.kay you were asking about reasons, why one would need to use set-query! in a form component - at the moment, I am playing around with untangled-ui form components, trying to generate forms from clojure/specs dynamically at runtime. My idea was to start with a form component that initially queries for a spec, e.g. (s/keys :req [::phone-type ...]), then parses the spec after the component was mounted and then modifies the query to add the spec'ed elements (e.g. ::phone-type). Unfortunately, it is not possible (I think), to modify the form-spec at runtime to include inputs for the new elements. I managed to work around this only by modifying the build-form function to accept an extra optional parameter form-spec to override the form-spec defined at class-level, so I can provide a new form-spec at runtime. Is there a better way to do this, so I can maybe avoid using om/set-query! and modifying build-form altogether? Any idea would be appreciated!

tony.kay 2017-04-17T16:15:25.207390Z

@sfrank Nice. So, are the specs also dynamic, or are you just wanting a way to translate a spec into a form?

tony.kay 2017-04-17T16:15:45.213450Z

because if it is the latter, I'd probably write a macro that outputs the defui from the spec instead

tony.kay 2017-04-17T16:19:50.286833Z

And then supply (as I discussed earlier) generators for the UI elements separately. That way custom rendering is still possible, but automated form generation would be as short as something like:

(defform Person ::person
   Ident
  (ident [this props] [:person/by-id (:db/id props)])
   Object
   (render [this] (render-form-bootstrap this)))

tony.kay 2017-04-17T16:20:59.307496Z

I'm assuming here that defform would keep the defui syntax (for React lifecycle, etc), but would supply IQuery and IForm itself.

tony.kay 2017-04-17T16:24:00.361503Z

Fully dynamic (sending an entity with spec from the server) would require set-query!. I can see a limited class of applications where this might be necessary: e.g. an application that let's users define their own entities with schema (ala SalesForce) where you want to autogenerate forms for those entities.

tony.kay 2017-04-17T16:26:25.404538Z

The current forms support was not designed for that use-case, but I'm sure we could figure out how to expand it. I don't personally need such support anytime in the forseeable future, but would be glad to take a well-designed contribution.

tony.kay 2017-04-17T16:27:03.416031Z

whereas the spec-based (static) form generation is something I'm very much interested in. Particularly since we could generate validation as well.

wilkerlucio 2017-04-17T16:39:45.644990Z

@sfrank I was thinking about this use case too, I find an interesting idea to generate the form from the specs, but if I was to do it I would try to make it more granular. Making it an entire form can be complicated to layout, because usually each form has a specific most efficient layout. Considering that would be interesting to have fields generated by spec, for example, if you have an int? spec you might want to use a numeric spinner, a calendar for an inst? or something, that way you could write something simpler as (form-input {::field :person/name}), this way you can reuse the specs + field implementation for that type, and it still open to send a different spec if you want some disparity in some cases

tony.kay 2017-04-17T16:48:19.801344Z

One of the more difficult problems, though, is the nested entities. As long as it doesn't have relations that go in the form. Because the spec might say a person has many phone numbers, but you may not want that to be part of your form at all, and generation of the UI for this kind of nesting may or may not be worthwhile to auto-generate. So then to make it fully general you have to have options around what parts of the spec are meant to be part of your form. Actually, you probably have to solve that problem anyhow, because you're headed towards trying to use the same spec everywhere, which means you're always going to want exclusions, because it is rare you let all of the fields of an entity be edited, and there is no easy way for the form generation logic to know the difference between a field that might need to be protected from editing/visibility. Spec doesn't talk about that aspect of data.

tony.kay 2017-04-17T16:49:48.828931Z

This is part of the reason I have not gone down the road very far. The forms support, as written, is easy to use and understand, and is very light and general purpose. Auto-generation gets you into something considerably larger.

wilkerlucio 2017-04-17T16:58:14.986446Z

@tony.kay maybe one way to get around that would just have an extra spec for the form instead of the entity, like when doing partial validation

tony.kay 2017-04-17T16:59:11.004924Z

of course, but then you have two things to maintain that get out of sync...so why not just declare the form with validation in IForm?

wilkerlucio 2017-04-17T17:00:20.028386Z

that was kind of my point when suggesting a more granular approach, have fields you can easily place from the specs

tony.kay 2017-04-17T17:00:47.037622Z

So, show me a spec for a number between 1 and 100

wilkerlucio 2017-04-17T17:01:53.059712Z

(s/def ::my-int (s/int-in 1 101))?

tony.kay 2017-04-17T17:02:02.062607Z

and this is probably showing a bit of ignorance on my part of spec...but how do Iget a good validation message for that field with i18n support?

tony.kay 2017-04-17T17:02:24.069587Z

I think I have to write a spec parser (or spec result parser)

wilkerlucio 2017-04-17T17:02:25.069955Z

you probably have to write an extra layer from it

wilkerlucio 2017-04-17T17:02:48.077447Z

I had worked on a "sub-layer" of spec for doing coercions

tony.kay 2017-04-17T17:02:52.078475Z

right, but if I declare it as a form field element, that layer is all right there. Easily visible. Easy to write. No extra code.

wilkerlucio 2017-04-17T17:03:00.080959Z

where I try to use spec as most as I can, and add missing stuff on top of it

tony.kay 2017-04-17T17:03:11.084432Z

yeah...I'm just playing devil's advocate 😉

wilkerlucio 2017-04-17T17:03:45.095563Z

I guess for forms/validation you gotta do something on that direction, try to re-use the information you have, and add extra stuff on top, trying to minimize the total number of names of you have

tony.kay 2017-04-17T17:04:39.112634Z

I'm just seeing it as a wider problem of: specs can be completely arbitrary. You can use lambdas to "validate". Parsing a spec to generate the right form field with validation is a hard problem in general

tony.kay 2017-04-17T17:05:55.136577Z

I personally would rather see specs used for their intended purpose: verification that things are sane. Using them in code generation seems a slippery slope to complexity

wilkerlucio 2017-04-17T17:05:59.138016Z

so you try to from the spec to reduce it into the most basic names you can (maybe have one for number, other for ranged numbers, other for strings, this grow as much as your domain needs), then when you have those basic forms you can derive from it, and for arbitrary things you might need arbitraty error message definitions, the challenge is try to avoid those, because they will decrease your reuse

wilkerlucio 2017-04-17T17:07:10.159667Z

but I agree with one, not a simple problem and there is a high risk of end up just being a lot of more complexity

wilkerlucio 2017-04-17T17:07:33.167074Z

so I would treat this as a very experimental thing to do 🙂

wilkerlucio 2017-04-17T17:08:30.184558Z

what's tempting about it is the fact that the key is giving you information, and its nice when we are able to re-use something that's already there

tony.kay 2017-04-17T17:08:32.185171Z

and would keep this in mind: https://clojure.org/about/spec#_informational_vs_implementational

👍 1
tony.kay 2017-04-17T17:09:06.195847Z

I think that is ultimately the problem here: it's easy to slip into using a spec to inform an implementation detail.

wilkerlucio 2017-04-17T17:09:53.210463Z

I guess as long as you don't mix up your layers (don't add something directly on your spec to change something about the form), it's all good

sfrank 2017-04-17T17:13:52.283811Z

Thanks for the answers, I have already learnt a lot! I am trying to build forms for specs that are provided at runtime, so the macro solution does not work for me.

sfrank 2017-04-17T17:15:38.317173Z

Regarding the question whether or not specs should be used for validation only or also for generation: there has been a dicussion on stackoverflow: http://stackoverflow.com/questions/41657101/metaprogramming-with-clojure-spec-values

tony.kay 2017-04-17T17:16:31.333688Z

If my database supported schema, I'd be more tempted to declare legal values on fields (possibly as specs) but derive the forms from those simple field validations combined with the schema itself.

tony.kay 2017-04-17T17:17:17.348125Z

I don't know....it's a bigger problem than a few minutes of noodling can solve 🙂

sfrank 2017-04-17T17:17:28.351842Z

Sure

tony.kay 2017-04-17T17:19:52.396946Z

well, in any case: it sounds like you have a good use-case for set-query!

sfrank 2017-04-17T17:21:31.428461Z

OK, but then I have the problem to modify my form spec to match the new query. Is there any future plan to allow re-defining the form spec at runtime?

tony.kay 2017-04-17T17:29:56.588640Z

that sounds good, yes. We could make the first param of form-spec be this instead of the class. which you'd have to assume would be the class in the static use-case

tony.kay 2017-04-17T17:30:26.598912Z

Or have build-form just allow you to pass the form spec when you call it.

sfrank 2017-04-17T17:31:01.610149Z

I tried the latter solution and that worked for me so far

tony.kay 2017-04-17T17:31:55.628006Z

yeah, the declarative nature really is just for static forms...the dynamic case is better with what you're doing

sfrank 2017-04-17T17:32:17.635293Z

You would also need to allow an optional form-spec argument for get-form-spec (which should be named get-form, I think)

tony.kay 2017-04-17T18:16:22.522110Z

I think making build-form just look to see if it is a class that implements IForm or a simple map would work. That's all a form spec is: a map

tony.kay 2017-04-17T18:16:44.529145Z

(build-form Person) vs. (build-form some-spec)

tony.kay 2017-04-17T18:17:13.538353Z

oh...hm. Still need the component for the ident, I guess