Skip to content

Support destructuring assignment of component properties from a special attribute #8780

Closed
@richardtallent

Description

@richardtallent

What problem does this feature solve?

Like function definitions, available component props can grow over time, leading to calls like:

<my-comp
    :size="3"
    :mode="active ? 'foo' : 'bar'"
    :show-icon="true"
    color="blue"
    label="Foo"
    icon-src="smile.svg"
/>

For an individual component in a layout with literal or simple attribute values, this is fine -- it's clear in intent, the unused props have suitable defaults (or warn if required), etc. However, when such a component is used in a v-for loop, it can often turn into a bunch of yak-shaving, where the developer is basically destructuring the iterated item by hand into individual attribute bindings:

<my-comp v-for="item in items" :key="item.id"
    mode="item.mode"
    :size="item.size"
    :show-icon="item.showIcon"
    :color="blue"
    :label="item.label"
    :icon-src="item.iconSrc"
/>

This is tiresome, error-prone (forgetting the colon for interpreted values, swapped values, bad Pascal-to-kebab conversions, etc.), and verbose. (I made two errors above, by the way... :))

The current alternative is for developers to use a parameter of type Object as a "catch-all" for properties so they can easily create and maintain their component instances.

As an alternative, I propose that a special "magic" attribute be designated (maybe "props"?). When this attribute is bound to an object, Vue will unpack the properties of the object and assign them to their respective (same-named) props.

What does the proposed API look like?

With this proposed special attribute, these two examples would be equivalent to the ones above:

<my-comp :props="{
    mode: active ? 'foo' : 'bar',
    size: 3,
    showIcon: true,
    color: 'blue',
    label: 'Foo',
    iconSrc: 'smile.svg' }"
/>

<my-comp v-for="item in items" :key="item.id" :props="item" />

As you can see, while using this form for literal attribute values on an individual component would be silly, it's a vast improvement where the desired attributes of the component already match an available object. Using the ES6 spread operator, it would even be easy to "tweak" the object to add a few more attributes. For example, these would be equivalent:

<my-comp
    v-for="item in items"
    :key="item.id"
    :props="item"
    :is-active="item.id === currentItem" />

<my-comp
    v-for="item in items"
    :key="item.id"
    :props="{...item, isActive: item.id === currentItem}" />

If you want to use this form but your incoming state object doesn't match the component's interface, no problem, just use a computed value to map from the state to the component's properties. The beauty there is, if you have similar situations in other places in your application, you can make it a mix-in, encapsulating the logic. And if the component interface, the backing data, or the mapping between them needs to change, you only need to adjust one place in the code, not the markup on every page where you used the component.

Notes:

  1. Where there is a collision of an explicitly-defined attribute and a key on the props value, I propose the attribute should override the object. Any keys of the props value not matching an attribute name would be ignored.

  2. This would require no changes to the components themselves... they would be unaware of the props attribute, they would only see their incoming properties.

  3. Since I don't use JSX, I don't know if this option would be useful, terrible, or neutral for those developers.

  4. There is precedent for "magic" (non-directive) attributes -- key, various slot-related attributes, etc. If there is serious concern about collisions of props with actual components in the wild using a prop called "props," I can imagine two solutions: (1) bypass this functionality when a component has a prop named "props", or (2) choose another candidate less likely to create compatibility issues -- perhaps "v-props".

  5. I suspect this would not just be syntactic sugar in the template compiler, since the compiler wouldn't know which keys to expect.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions