Arbitrary nested dynamic forms (still unclear: mutating object props - bad or good/ok)

props
components
forms

#1

Hi, in https://codesandbox.io/s/0y7jw79ljn and https://github.com/drahkrub/props-as-data I created a little demo for arbitrary nested dynamic forms.
grafik
Of course such forms can be much more complex, it’s just a demo.
The idea is that some root component holds all the data and passes parts of the data as “objects props” down the component hierarchy. Each sub component can mutate its object prop which in return alters the state of the root component.

As said in https://vuejs.org/v2/guide/components-props.html#One-Way-Data-Flow

Note that objects and arrays in JavaScript are passed by reference, so if the prop is an array or object, mutating the object or array itself inside the child component will affect parent state.

I’ve read a lot about this stuff, here (e.g. Is mutating object props bad practice?), on SO and in the official docs - but it’s still unclear for me, if this is ok or not (or if it can be problematic in future versions of vue.js).

It would be very nice, if a clear statement from higher authorities would be given.

If there is a different/better way to handle arbitrary nested dynamic forms, I would be very interested!

Cheers!


Is mutating object props bad practice?
#2

I would say mutating a prop is bad practice. You don’t know where the value is coming from. The source of the object might not expect it to be mutated. If the value comes from Vuex, you will get a warning since you’re not using a mutation.

As a tip, have you looked at provide/inject? See https://vuejs.org/v2/api/#provide-inject. Although it states that by design, provide/inject is not reactive, any reactive object passed down remains reactive. This might be suitable for your use case as well, and doesn’t have the downside of prop mutation.

Provide/inject has 2 additional advantages over props in this case: 1) you don’t have to pass down the prop through the hierarchy, this is done for you, and 2) you can nest components that know nothing about the data you want to pass down.


#3

Hi, thanks for your answer! Yes, I know about provide/inject but I do not think it’s useful/fitting for this requirement/problem, even in this simple example. The root component would have to provide the complete data. I think the sub components would have to provide their ‘injected’ data, too - otherwise I can’t imagine how the ‘injects’ of deeper components should look like (because of their static nature).


#4

I have observed changes in the Tree View example which might have been made in response to the discussion of the Is mutating object props bad practice? question. See the note added to my answer. Sadly, the fiddle change is far from a

clear statement from higher authorities

.


#5

Sad thing. But thanks for the info! We should try to ping some higher authorities…


#6

While I probably count as such a “higher authoritity” (horrible title, btw.), I won’t do do any sort of clear statement. What I will say is:

  1. It’s recommended to generally not do this in most situations as one way data flow usually is better for code understandability and debugability.
  2. But if in your case, the pros (ease of implementation) for you outweigh the cons (breaking a “recommended practice”) - do it. See how it plays out for you. If it’s really advantagous is highly dependent on the actual reuirements of the component you are building, which are not necessarily reflected in the simplified example you presented.

You will learn nothing from me saying: “I clearly state: This is bad!”. You will only get true insights from experience and trying out (or breaking) the things some people recommend on actual projects.


#7

Thanks for your statement! And sorry for the horrible title - it was the only one that comes to my mind due to my restricted english vocabulary. :slight_smile:

Right now my implementation works fine, but I’m worried that it might not be the case in future versions of vue.js - therefore my wish for a clear statement.

To justify my approach: I have to build really complex and in particular dynamic CRUD forms. There is always a base entity which is (or can be) connected to several other entities in arbitrary relations (one-to-many, many-to-many, …), therefore the json transfered from (and later on back to) the server is deeply nested too.

I know that flattening the data is a common approach, but

  • not all entities are real entities in the sense, that they have their own IDs (in the database), e.g. see [stackoverflow]

  • form editing is finished by pressing a save button (no live editing where changes are sent to the server as soon as they take place) and when the save button is pressed, there should be only one POST request which sends the json back to the server. And on the server (written in Java backed by Spring and Hibernate) the json is mapped back to the base entity (perhaps changing the base entity and/or connected entities, creating new relations to other existing or newly created entities and so on) and the complete stuff is stored within one (database) transaction to always ensure the consistency of the stored data.

In ancient times I’ve done this on the client with JSPs using crazy jQuery magic to make the forms dynamic (creating new input elements, deleting existing ones, …). It was a horrible mess fiddling with the DOM, calculating the correct indices and names of the form elements.

With vue the implementation (as initially described) is super simple and clean. The form can be broken down in components which can be reused everywhere. Making the form dynamic is simply done by changing the data, the form elements do not even need to have names!

I do not see a way to achieve such a simple and clean implementation without the possibility of mutating object props.

Moreover I talked so some react guys and they told me, that a comparably simple implementation is not possible in react (at least not out of the box).

For that reason I really would like to consider it as a strength of vue doing it this way.