Fork me on GitHub
#reagent
<
2018-04-27
>
minikomi04:04:29

is nesting atoms a code smell? trying to create a nested form with flexible fields - can add or remove fields, and each field has a value state.. using a single atom & cursors seemed like a good fit, but it made it difficult in another way (`track` - ing the value to auto-generate errors, couldn't think of a good way to reference the nested value)

javi05:04:26

could you show the shape of your data?

minikomi05:04:53

at first it was something like:

(r/atom
{:fields
  [ 
    { :field-id etc....
     value
    }
  ]
})

minikomi05:04:34

where fields can also be flexible - and hold a similar nested structure

minikomi05:04:09

when I was creating the layout, I'd walk the tree and get a cursor for each field

minikomi05:04:44

but.. you'd have to check for an error when rendering.. I wanted the error to also update alongside updating the value

minikomi05:04:00

so I switched to

{:fields
  [ 
    { :field-id etc....
     :value (r/atom ~~)
     :err (track value atom -> validate -> maybe error)
    }
  ]
})

javi05:04:31

> but.. you'd have to check for an error when rendering.. what kind of error? where does it come from? also, do you mean that whatever gets rendered in the end depends on that error?

minikomi05:04:05

yes, each field also has validation attached - when the value is updated it gets validated eg. too long, by regex, whatever

minikomi05:04:37

i was originally nesting that information with the original data layout, and then calling it within the render function

minikomi05:04:50

rather, I'd like it to be called when the value is updated, and just the result passed down

javi05:04:46

i would keep the data that specifies the layout separate from the validation of the fields... are you using re-frame? it would be easy to setup some validation with interceptors on teh events that the form sends.

minikomi05:04:05

i'm developing it outside of re-frame so it can be dropped into larger projects. I've found that I don't like keeping temporary form values in the main app db; don't like sending events on every form update

minikomi05:04:21

the idea is you have a form schema, and some previous values.. the library will generate a "live" version of the values which can be: * rendered as fields which update the data using the library * serialized when all nested validation passes, return error if not * validated on serverside using the same schema

minikomi05:04:14

still playing around with what the "live" version of the data will look like. . a single big atom, or a bunch of nested atoms..

javi05:04:50

schema->form kind of thing? like https://mozilla-services.github.io/react-jsonschema-form/? + serverside nice.. so for me there is the schema and the data The schema is a map that contains data types and validations etc.. a spec of the possible shapes of the data. The data from the form is in a single atom that gets updated on changes and (optionally) validated using the spec in the schema wouldn't this be a good fit for clojure spec?

minikomi05:04:18

yep similar to that, but also with nested fields, compound fields, and flexible fields

minikomi05:04:54

flexible fields have set types you can add -- eg. image, markdown, email -- and they can be re-ordered

minikomi05:04:54

compound fields are sets of fields which can be added to a flex field and validated together eg. a banner link would be a url / image url.. if the url isn't blank but the image is blank, that would be a compound error.

minikomi05:04:45

I've been using funcool.struct for validation, spec might alternatively be part of it.. not sure yet.

minikomi05:04:00

https://github.com/alexanderkiel/phrase I'm just validating single fields as the UI happens, so this could easily be dropped in.

javi05:04:58

sounds good, I would definitely have a map or if using spec a spec for the form schema that defines the shape of the form and its elements. this is static and never changes, if something is optional or can contain a flexible number of items, that would be part of the definition of this schema. the validation functions can be here as well as they further define the possible shapes the data can have, and the data/state of the form in a single atom whose that follows the schema and when it is updated, on a what or on swap!, check the path to what is being updated and run the validaton function for that path...? ... or if using spec, just check that the data conforms to the spec

minikomi05:04:48

yep, sounds like what i have - the form schema generates the live data.. just westling with how to do simultaneous value and error updates... a magic fn which does both, a large single atom watched which re-validates everything, nested atoms with nested tracks.. somehow pass a chan down which can take an update event w/ a path.. a global chan i can send an event to..

minikomi05:04:51

I'm not to slick with spec to be honest.. so I've avoided involving it so far

javi06:04:07

same here, love spec but still integrating it in my mindset > just westling with how to do simultaneous value and error updates... what do you mean by that. also, could you use cursors and when updating the data, get the validation function that resides at the same path in the schema and validate?

minikomi06:04:33

I mean, the error really is just a projection of the value.. so, I'd rather not update it manually, but rather, have it be automatically generated from the value

javi06:04:19

here is some conversation along those lines "Converting ValidationErrors to user friendly messages" https://github.com/plumatic/schema/issues/56

javi06:04:50

looking forward to see it in action

❀️ 4
sho06:04:03

Can someone help me? I used gensym to generate keys for a list of items as below.

(->> blog-lists
     (map #(identity `[:div {:key ~(:blog-id %)} ~(:blog-content %)])
     (interpose [:hr {:key (str (gensym))}]))
And the Chrome console gave me the following error message.
Warning: Encountered two children with the same key, `G__7`. Keys should be unique so that components maintain their identity across updates. Non-unique keys may cause children to be duplicated and/or omitted β€” the behavior is unsupported and could change in a future version.
    in div (created by shomiyamoto.views.blog_entries)
First of all, what should I do when I want to give identity-less components like [:hr] unique keys so React doesn't give me warnings.

minikomi07:04:35

you're generating the symbol once, then interposing

sho07:04:01

Stupid me.

minikomi07:04:06

haha, no problem

sho07:04:25

Thanks. I think I can figure out what I should do in a case like this.

minikomi07:04:44

maybe..

(for [{:keys [blog-id blog-content] :as b} blog-lists]
  (when-not (= (first blog-lists) b)
    ^{:key [(:blog-id %) :hr]} [:hr])
  ^{:key (:blog-id %)}
  [:div blog-content])

minikomi07:04:44

or forget the [:hr] and use css for borders with a :first-child disabling the top border

sho07:04:32

That's probably the best solution! Without your suggestion, I would have wasted my time somehow insisting to use gensym, this time with mapcat, which should work but not as idiomatic as your solution.

minikomi07:04:24

map-indexed is also a good one to look at if you need indicies for all sub-elements

minikomi08:04:14

(map-indexed
 (fn [idx {:keys [blog-id blog-content]}]
   (when-not (= idx 0)
     ^{:key [(:blog-id %) :hr]} [:hr])
   ^{:key (:blog-id %)}
   [:div blog-content])
 blog-lists)

sho08:04:43

Got it. No point of generating random keys. Just need enumeration and separate first and rest.

sho08:04:12

Several lessons learned in short time.

minikomi08:04:47

right - it's also more explicit that way, since you point out in the code that the first (or 0 indexed) is being treated specially. good if you have to come back to code later!

sho08:04:36

Big thanks! Glad I asked here πŸ™‚

minikomi08:04:45

γ©γ†γ„γŸγ—γΎγ—γ¦γ€œ

πŸ‘ 4