Vue: Ability to Recursively Define the Schema of Object Prop Values

Created on 12 Jan 2019  ·  12Comments  ·  Source: vuejs/vue

What problem does this feature solve?

The inability to pass nested objects as props to a component - even when declaring default properties in the binded component, the binded nested object properties do not change the default values and do not grant reactivity.

  • The current way offeres quite a wierd, unnatural and inconsistent way of registering some of the binded object properties as props while the others are registered as data or computed - for the sole reason that they are nested in a property object - which makes no sense and adds up additional redundant boilerplate.

Use Case
In my specific use-case I do not want to flatten the object before binding, I have experimented with doing so. But in my use-case I am building a Vue Compnent and I would like to allow the consumer-developer of the component to bind a sole config object that is divided to inner nested objects for example in my use-case a star rating mechanisem, its style property with its inner styles should be registeed like so --> config.style.color.

Before

Binding a config object like so:

data() {
    return {
      config: {
        rating: 4.7,
        isIndicatorActive: true,
        starStyle: {
          fullStarColor: "#ed8a19",
          emptyStarColor: "#737373",
          starWidth: 100,
          starHeight: 100
        }
      }
    };
  },

And in the selector: <stars-rating v-bind="config"></stars-rating>

Then I register the properties in the <stars-rating> component like so:

 props: {
   // regular same hirearchy level props
    rating: {
      type: Number,
      default: 4.3
    },
    isIndicatorActive: {
      type: Boolean,
      default: true
    }
  },
// 2nd hirearchy level props inside style object
fullStarColor: {
    type: String,
    default: "#ed8a19"
},
emptyStarColor: {
    type: String,
    default: "#737373"
},
starWidth: {
    type: Number,
    default: 100
},
starHeight: {
    type: Number,
    default: 100
}

So far, by trying to accomplish the above, all four 2nd hirearchy level props are not reactive and do not override deafult values.

After

By running this code on the props before they are attached to the child component, all props are
flattened recursievly and should work with current reactivity logic:

flattenProps(props) {
    let flatProps = {};

   for (let i in props) {
        if (!ob,hasOwnProperty(i)) continue;

        if(typeof props[i] == 'object') {
            let flatObj = this.flattenProps(props[i]);
            for(let x in flatObj) {
               if (!flatObj.hasOwnProperty(x)) continue;

               flatProps[i + 'i' + x] = flatObj[x];
            }
        } else {
            flatProps[i] = props[i];
        }
    }
    return flatProps;
}

I need to see the constraints and everything, but this is the general logic I would like to implement to flatten the props object before "mounted" or attached to the component

End User
Any vue developer building dynamic components for other develoeprs to use like me & any vue users who want minimal boilerplate and a simple easy-to-use configuration for various components they consume.

  • I tried to build a vue star rating component that is fully open-source and for the use of anyone, I then encountered the need to style the stars in it, and in the future its tooltip, and more. I would like to have a single style object as a property of the config object binded to this component.

What does the proposed API look like?

I plan to add a sort of middleware function that will analyze the binded object that is passed to v-bind, this function would do the following:

  • Check for a nested property that is typeof 'object'
  • If finding such a type - the function will then concat the object name with each inner property name and place the designated value as new key/value pairs in the 1st hirearchial level thus, "flattening" the binded object.
  • To make it more efficient this function would be recursive - and handle any levels of nested objects

All 12 comments

@Justineo Please provide feedback of how I can be clearer about what I wrote :) I would like to make it more to the point.

@Justineo Provided gudiance of where exactly to add this logic in vue core, assuming it is accepted - I would love to develop a solution for this Feature Request and contribute to the community :)

Actual code examples - before and after - to demonstrate the problem and solution would be helpful.

So far my picture of the issue is quite fuzzy

@LinusBorg Just updated it with code example of the before situation, and after logic I plan to implement - let me know if its clearer

I am not sure where exactly in the vue core this logic should be attached though, would be glad to recieve guidance and adjust the relevant logic if you see fit.

Regarding validation. Keep it mind it's in dev mode only and you can already do it with the validator function.
For default values, so what you want is to merge objects to have default nested values?

@posva I want the nested values to be reactive although registered as shown in above before config - now after binding they are not reactive.

And regarding the validator function I am not quite sure what do you mean could you reference the code?

@LinusBorg @posva Could you direct me to where in the repository this logic is handled - props binded to a vue component and the handler function that recieves the props and executes some logic on them before creating the component?

I think I get what you want, for the most part. And to me it feels like it's doing to much behind the scenes and the behaviour is very different to how Vue normally behaves.

  • What ifI just want to pass a nested object?
  • Why is it important that the parent can pass props on this particular way (one style object)?
  • couldn't you flatten the object with a helper in the parent? That would be explicit and not conflict with existing behaviour of nested objects.

Why don't you just transform the config to the desired format before passing it to the child? I don't see any reason this should be a special API provided by the framework.

@LinusBorg

  • I think the best way to introduce such a feature would be to allow the user to easily enable/disable therefore solving the first issue you raised.
  • The motivation is to allow minimum-configuration and boilerplate for developers - the best way I see to do so is by passing a single configuration object with its nested configurations. A good example for that would be a more complex component like a multiline graph with various levels of nested properties (e.g Highcharts for example you have even 3 and 4 levels of nested object properties in the schemas of the graphs)
  • I could of course flatten it before binding with some helper function. But that is:
    a) Not a robust solution
    b) requires more boilerplate to be written from the component consumer
    c) Less user-friendly e.g - it is not plug-n-play, its more plug and now improt this helper and use it to pass your config object...

@yyx990803 In my use case I am building a vue star rating component for the free usage of various Vue developers - requiring them to transform the config object to the desired format instead of just allowing them to pass their config object dynamically without any further boilerplate is not a good approach in my opinion.

  • As a developer consuming other componets myself, I use Highcharts at many times and what I appreciate so much there is the simplicity & elegancy of the config/options object with various levels of nesting, it's very verbose and dynamic and really gives an amazing user experience as a developer to work with this approach.
  • A component built for free use for othe Vue developers should aspire to be as easy to use, simple, elegant and be plug-n-play. Once as a consumer of that component I have to map the data I want to bind before binding it because these are the framework limitations - well that doesn't feel "right". It's further:
  • Can cause incosistencies
  • Unnatrual & feels like a workaround
  • Less verbose and robust(as mentioned before)
  • not plug-n-play rather - requires more from the consumer of the component to just use it.

We introduced our RFC process yesterday: https://github.com/vuejs/rfcs

Your feature request marks all ticks that require an RFC now. So I'll close this issue and kindly ask you to open a PR for this proposition to go through the formal process if you feel this feature adds enough value for our users to be discussed there.

You can and should of course also link to this issue when writing the RFC.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

RashadSaleh picture RashadSaleh  ·  51Comments

Akryum picture Akryum  ·  34Comments

karevn picture karevn  ·  42Comments

okjesse picture okjesse  ·  49Comments

rpkilby picture rpkilby  ·  50Comments