Skip to content

Latest commit

 

History

History
127 lines (87 loc) · 3.96 KB

0003-dynamic-directive-arguments.md

File metadata and controls

127 lines (87 loc) · 3.96 KB
  • Start Date: 2019-01-16
  • Target Major Version: (2.x / 3.x)
  • Reference Issues: #2, #3
  • Implementation PR: vuejs/vue#9373

Summary

Support dynamic values in directive arguments.

Basic example

<div v-bind:[key]="value"></div>
<div v-on:[event]="handler"></div>

Motivation

Due to directive arguments being static, currently users would have to resort to argument-less object bindings in order to leverage dynamic keys:

<div v-bind="{ [key]: value }"></div>
<div v-on="{ [event]: handler }"></div>

However, this has a few issues:

  • It's a lesser known technique that relies on knowledge of the object-based syntax for v-bind/v-on and the existence of computed property keys in JavaScript.

  • It generates less efficient code: an ad-hoc object is allocated and if there are other static bindings on the same element, it has to be dynamically iterated and mixed into the existing data object. The code looks roughly like this:

    return h('div', {
      on: Object.assign({
        click: onClick
      }, {
        [event]: handler
      })
    })

    Where as with dynamic arguments we can directly generate:

    return h('div', {
      on: {
        click: onClick,
        [event]: handler
      }
    })

In addition, v-slot doesn't have an equivalent object syntax, since it's value is used for declaring the slot scope variable. So without the dynamic argument, the v-slot syntax will not be able to support dynamic slot names. Although this is probably a very rare use case, it would be a pain having to rewrite an entire template into render function just because of this single limitation.

Detailed design

<!-- v-bind with dynamic key -->
<div v-bind:[key]="value"></div>

<!-- v-bind shorthand with dynamic key -->
<div :[key]="value"></div>

<!-- v-on with dynamic event -->
<div v-on:[event]="handler"></div>

<!-- v-on shorthand with dynamic event -->
<div @[event]="handler"></div>

<!-- v-slot with dynamic name -->
<foo>
  <template v-slot:[name]>
    Hello
  </template>
</foo>

<!-- v-slot shorthand with dynamic name -->
<!-- pending #3 -->
<foo>
  <template #[name]>
    Default slot
  </template>
</foo>

Handling of null as Special Value

Dynamic argument values are expected to be strings. However, it would be convenient if we allow null as a special value that explicitly indicates that the binding should be removed. Any other non-string values are likely mistakes and will trigger a warning.

null as a special value only applies to v-bind and v-on, but not v-slot. This is because v-slot is not a binding and cannot be removed. Custom directives have the liberty of deciding how to handle non-string arguments, but are expected to follow the convention when it applies.

Drawbacks / Considerations

Constraints on expressions

Theoretically this opens up the directive argument to arbitrarily complex JavaScript expressions, but html attribute names cannot contain spaces and quotes, so in some cases the user may get tripped up with something like:

<div :[key + 'foo']="value"></div>

Which does not work as expected. A workaround would be:

<div :[`${key}foo`]="value"></div>

That said, complex dynamic key bindings should probably be pre-transformed in JavaScript via a computed property.

Update:: it is possible to detect such usage and provide proper warnings in the parser (by checking for arguments that are missing the closing bracket).

Custom Directives

Allowing dynamic arguments for all directives means custom directive implementations now also need to account for potential argument changes in addition to value changes.

This also requires the addition of binding.oldArgs to the custom directive binding context.

Alternatives

N/A

Adoption strategy

This is non-breaking and should be straightforward to introduce with appropriate documentation updates.

Unresolved questions

N/A