Fork me on GitHub
#untangled
<
2017-05-30
>
fz00:05:43

How can I chain mutation functions in a load post-mutation? https://clojurians.slack.com/archives/C06DT2YSY/p1496100336889588

tony.kay00:05:05

chaining: I don’t recommend that…kinda messy. Rather, write a support function that accepts a state map for all of your state manipulations, then it is easy to chain them:

fz16:05:18

@tony.kay FYI use case, in case it's helpful for future designs — essentially I want my post mutation to do 'foo and 'bar, but 'bar itself is also a mutation that can be triggered from elsewhere in the UI. For example, after loading data fields on a record, I want to expand a detail view and select a particular tab; selecting a particular tab is a mutation that can be triggered by the user via the UI as well.

tony.kay17:05:33

was there a question in there? It sounds like my response from 5:49pm yesterday (PST) applies just fine.

fz21:05:33

It does. No question here, was thinking about it and realized it might be useful in general to share my use case 🙂

tony.kay00:05:30

(swap! state (fn [s] (-> s  (do-a) (do-b) (do-c))))

tony.kay00:05:56

and your simple mutations are just (defmutation do-thing [args] (action [{:keys [state]}] (swap! state do-thing)))

tony.kay00:05:28

but technically, there is no problem with pulling an action out of a call to mutate. It is just a multimethod.

tony.kay00:05:48

(defn open-menu-impl [state-map id] (assoc-in state-map [:menu/by-id id :open?] true)

(defmutation open-menu [{:keys [id]}]
  (action [{:keys [state]}] (swap! state open-menu-impl id)))

fz01:05:13

Makes sense — thank you

mitchelkuijpers14:05:16

Has anyone extended untangled-ui forms support in a way that you can have a field which has a query? I have a field that needs to load options from a remote place and then show them and then you can create or select an existing. I tried to make a field and now I am creating a subform But then I have the problem that the subforms ident will change based on what the user selects.. Not sure if that is a problem btw.

mitchelkuijpers14:05:49

btw I think that making this a subform is the wrong solution for what I am trying to do

tony.kay17:05:15

@mitchelkuijpers Yeah, it isn’t a subform. What you want is a control that has it’s query, a load that fills in that control, and a callback that can tell you when you’ve interacted (selected). Then you can hook the callback up to fill in the form field (just modify the value in app state).

tony.kay17:05:52

and probably pass in the current value via computed (since you won’t be querying for the current selection in the control).

tony.kay17:05:40

So, you have UI control that takes the possible selections (via query), a selected value (via computed), and has a callback to tell you when it is selected. The parent (form) does the transact to update the actual value.

tony.kay17:05:25

and since forms are just stacked on top of regular state, that just means updating that field’s value. Of course, you’ll want that field to be properly declared (for submission, dirty check, etc.)

tony.kay17:05:39

but the control details are just orthogonal.

tony.kay17:05:19

@timovanderkamp just for my info: are you planning on making those extensions to om-css?

timovanderkamp17:05:36

@tony.kay yes, but first i will continue playing with this version to see if more has to be fixed

tony.kay17:05:16

that’s always a good approach. see where things are broken usually leads to better design

mitchelkuijpers18:05:53

@tony.kay I was a bit scared of just modifying the app state. But I will try this out. Good to know that I am on the right track

mitchelkuijpers18:05:06

The hardest part is getting the current value since it is an ident and I cannot query the form state (I think)

timovanderkamp19:05:50

I was also thinking.. wouldnt implementing a $ prefix for preventing localization make the Global protocol unnecessary?

tony.kay19:05:49

@mitchelkuijpers You have to query the form state (e.g. your field value)

tony.kay19:05:16

@timovanderkamp Hm. I was thinking that it would mainly be top-level components that would contribute to the global. Something like a Button should not need to, but you might need the nesting selectors.

tony.kay19:05:27

So, I’d say both are nice to have

mitchelkuijpers19:05:03

Ah so just query the untangled.ui.forms/form-key

tony.kay19:05:10

oh, not what I meant 🙂

tony.kay19:05:31

yes, you do query that, but the current value is just your local current value…you still treat the form-data as opaque

tony.kay19:05:37

it is the “pristine” copy

tony.kay19:05:50

along with field decl config

mitchelkuijpers19:05:51

So maybe just copy the current value from the ident when it's get selected

tony.kay19:05:15

um. let me try an example…forgive me if I don’t get the naming right, I don’t have it all in my head:

mitchelkuijpers19:05:47

No problem, I have been breaking my head a bit to long about this so I might be a bit slow today

tony.kay19:05:23

(defui SelectorComponent
  ... query for your selector, which will act as UI for form field. This is NOT a form field ...
  ... ident ...
  (render [this] 
    (let [{:keys [current-value onChange] (om/get-computed this)
          {:keys [selections]} (om/props this)]
    ... render your selector, with current-value, and selections. Call onChange when a change happens))

tony.kay19:05:42

That would be the selector thingy. It is not part of the form…just a UI component with query, ident, and some computed stuff

tony.kay19:05:01

Then, your form. Let’s say you’re selecting tire-size. At some point you’ll need to load the tire sizes for the selections of the above component, but that is orthogonal to the form…just for the selector thingy

tony.kay19:05:23

(defui TireForm
   ... declare form fields, including :tire-size ...
   ... query (include :tire-size and other form stuff, ALSO include a join {:tire-selector (om/get-query SlectorComponent)}) >..
   ... ident as usual ...
   (render [this]
      ; let, extracting tire-size and tire-selector
      (ui-selector (om/computed tire-selector {:current-value tire-size}))
      ; other form stuff))

tony.kay19:05:57

so you see you’re feeding the selector the current value through computed

tony.kay19:05:07

so the selector can be parallel to your form

tony.kay19:05:26

You can just mutate the tire-size as you would in any component

tony.kay19:05:44

Now, you could abstract that all into functions (one that returns the query bit, one that calls the rendering with proper options) and add that in the form-field multi-method.

mitchelkuijpers19:05:43

So basically just create a component which handles the options and the current value, and then on change updated the correct part of the form-state so I still get the pristine and other goodies

tony.kay19:05:10

yeah, this is how you’d implement a form field extension that also needs to query for data

tony.kay19:05:39

:tire-selector isn’t part of the form, per-se, it is just rendering assistance

mitchelkuijpers19:05:42

Yeah the biggest problem was that I need to add a query, form-spec item and a certain render. But maybe I can just extend the multimethod which I also did for our own text-field component and then just create a function for the needed queries based on the form-spec

mitchelkuijpers19:05:57

Big + of creating a data structure for the form

mitchelkuijpers19:05:08

Thank you @tony.kay this helps a lot. I was banging my head against a wall

tony.kay19:05:28

no problem.

urbank22:05:05

Can you think of an example when you would use rather vanilla omnext (build some custom framework around it) instead of untangled?