Fork me on GitHub

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)


could you show the shape of your data?


at first it was something like:

    { :field-id etc....


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


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


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


so I switched to

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


> 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?


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


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


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


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.


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


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


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


schema->form kind of thing? like + 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?


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


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


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.


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

minikomi05:04:00 I'm just validating single fields as the UI happens, so this could easily be dropped in.


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


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..


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


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?


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


here is some conversation along those lines "Converting ValidationErrors to user friendly messages"


looking forward to see it in action

❀️ 4

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.


you're generating the symbol once, then interposing


Stupid me.


haha, no problem


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



(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])


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


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.


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


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


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


Several lessons learned in short time.


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!


Big thanks! Glad I asked here πŸ™‚



πŸ‘ 4