Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Attr fallthrough behavior #92

Closed
wants to merge 8 commits into from
Closed

Attr fallthrough behavior #92

wants to merge 8 commits into from

Conversation

yyx990803
Copy link
Member

This is a follow up of #26, but with much less breakage.

  • Make the attrs fallthrough behavior more consistent;
  • Make it easier to pass all extraneous attrs to child elements / components.

Rendered

@yyx990803 yyx990803 added breaking change This RFC contains breaking changes or deprecations of old API. core 3.x This RFC only targets 3.0 and above labels Nov 6, 2019
@tmorehouse
Copy link

For class and style bindings, and inheritAttrs is false (or implied false), how would one make the style/class binding apply to the root element, but not the inner element?

<template>
  <div :class="$attrs.class" :style="$attrs.style">
    <!-- How does one easily prevent style and class from also being applied here? -->
    <div v-bind:"$attrs">
       Foobar
    </div>
  </div>
</template>

Perhaps there needs to be similar flags for inheritStyle and inheritClass?

@Meekohi
Copy link

Meekohi commented Nov 6, 2019

For class and style bindings, and inheritAttrs is false (or implied false), how would one make the style/class binding apply to the root element, but not the inner element?

Specifically, you may often wish to apply all event listeners to a child element... maybe a simple helper that plucks off the listeners from $attrs would cover most cases? v-bind:"listenerProps($attrs)"?

@yyx990803
Copy link
Member Author

@tmorehouse you can create a computed property based on $attrs, in which you can pluck off anything you don't want to pass down to the inner element.

@yyx990803 yyx990803 mentioned this pull request Nov 6, 2019
@tmorehouse
Copy link

@yyx990803 so something like this could be done to omit style/class:

export default {
  props: {
    foo: { type: Boolean, default: false }
  },
  computed: {
     computedAttrs() {
       return { ...this.$attrs, style: null, class: null }
     }
  }
}

Although is this.$attrs reactive (to trigger the update of the computed prop)?

Similarly, if one wanted the v-on listeners (which are now top level onXXX properties in $attrs), one would need to use a bit more convoluted code to eliminate the listeners (or extract the listeners) so they can be targetted to another element:

export default: {
  computed: {
    computedListeners() {
      return Object.keys(this.$attrs).reduce((obj, key) => {
        if (/^on[A-Z]/.test(key)) {
          obj[key] = this.$attrs[key]
        }
        return obj
      }, {})
    },
    computedAttrs() {
      return Object.keys(this.$attrs).reduce((obj, key) => {
        if (!/^on[A-Z]/.test(key)) {
          obj[key] = this.$attrs[key]
        }
        return obj
      }, {})
    }
  }
}

Perhaps exposing a few utility methods like omit(obj, keysArr) and pluck(obj, keysArr) might be useful.

@yyx990803
Copy link
Member Author

yyx990803 commented Nov 6, 2019

@tmorehouse it does remind me that $attrs is not reactive and a simple method call is probably more consistent. (Maybe we should make it reactive? It's a matter of cost vs. benefits)

As for helpers like omit and pluck - these are generic JS utilities and there are already plenty of existing libraries out there, and it doesn't really take much effort to write yourself (they can be easily tailored to your specific needs and reused across your project). Vue will only provide helpers that are Vue-specific.

@michaeldrotar
Copy link

michaeldrotar commented Nov 6, 2019

this probably isn't the best approach, and I haven't Vue'd in almost a year so I'm sure I have some syntax wrong.. but could you do something like this?

<!-- search-component.vue -->
<template>
  <form v-bind="$attrs.form" @submit="search">
    <input type="text" v-bind="$attrs.input">
    <button type="submit" v-bind="$attrs.button">
       Search
    </button>
  </form>
</template>

Usage:

<search-component
  input="{ name: 'foo', value: searchText }"
  form="{ onSubmit: fancySearch }"
/>

@leopiccionia
Copy link

@michaeldrotar I think it should be <search-component :input={ ... } :form="{ ... }/>, to bind an object instead of a string. But I think that registering a prop, in this case, would be more idiomatic than using $attrs.

@cjpearson
Copy link

Does this also affect v-slot props? Would listeners be exposed as onXXX props there?

@yyx990803
Copy link
Member Author

@cjpearson yes.

@yyx990803
Copy link
Member Author

This RFC is now in final comments stage. An RFC in final comments stage means that:

The core team has reviewed the feedback and reached consensus about the general direction of the RFC and believe that this RFC is a worthwhile addition to the framework.
Final comments stage does not mean the RFC's design details are final - we may still tweak the details as we implement it and discover new technical insights or constraints. It may even be further adjusted based on user feedback after it lands in an alpha/beta release.
If no major objections with solid supporting arguments have been presented after a week, the RFC will be merged and become an active RFC.

@yyx990803 yyx990803 added the final comments This RFC is in final comments period label Nov 29, 2019
@tmorehouse
Copy link

tmorehouse commented Nov 30, 2019

I still like the idea of $listeners which pre-separates events from attributes to make it easier for plucking out event handlers instead of creating wildcard RegExp's to find anything that matches /^on[A-Z]+/

Perhaps instead of $attrs having everything, maybe a new property $all would have everything, and then $attrs is any non event, and $listeners has only events.

This would follow more closely the template convention of @xxx for events, and :yyy="expression" (or yyy="string") for attributes/props. unless instead event handlers will be placed on elements/components as :on-foo-event="handler" (requiring the v-bind so that a handler reference can be passed)

@yyx990803
Copy link
Member Author

This proposal has been replaced by #137.

@yyx990803 yyx990803 closed this Feb 28, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
3.x This RFC only targets 3.0 and above breaking change This RFC contains breaking changes or deprecations of old API. core final comments This RFC is in final comments period
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants