Skip to content

Latest commit

 

History

History
151 lines (110 loc) · 8.23 KB

0024-attribute-coercion-behavior.md

File metadata and controls

151 lines (110 loc) · 8.23 KB
  • Start Date: 2020-01-30
  • Target Major Version: 3.x
  • Reference Issues: #8731 #8735 #9397 #11053
  • Implementation PR:

Summary

  • Drop the internal concept of enumerated attributes and treat those attributes the same as normal non-boolean attributes.
  • No longer removes attribute if value is boolean false. Instead, it's set as attr="false" instead. To remove the attribute, use null or undefined.

Motivation

In 2.x, we have the following strategies for coercing v-bind values:

In 2.x we modeled the concept of “enumerated attributes” as if they can only accept 'true' or 'false', which is technically flawed. This also make them behave differently from other non-boolean attributes which led to confusion. The following table describes how Vue coerce “enumerated attributes” differently with normal non-boolean attributes:

Binding expr. foo normal draggable enumerated
:attr="null" / draggable="false"
:attr="undefined" / /
:attr="true" foo="true" draggable="true"
:attr="false" / draggable="false"
:attr="0" foo="0" draggable="true"
attr="" foo="" draggable="true"
attr="foo" foo="foo" draggable="true"
attr foo="" draggable="true"

We can see from the table above, current implementation coerces true to 'true' but removes the attribute if it's false. This also led to inconsistency and required users to manually coerce boolean values to string in very common use cases like aria-* attributes like aria-selected, aria-hidden, etc.

Detailed design

  • We intend to drop this internal concept of “enumerated attributes” and treat them as normal non-boolean HTML attributes.

    This solves the inconsistency between normal non-boolean attributes and “enumerated attributes”. It also made it possible to use value other than 'true' and 'false', or even keywords yet to come, for attributes like contenteditable.

  • For non-boolean attributes, stop removing them if they are false and coerce them to 'false' instead.

    This solves the inconsistency between true and false and makes outputing aria-* attributes easier.

The following table describes the new behavior:

Binding expr. foo normal draggable enumerated
:attr="null" / /
:attr="undefined" / /
:attr="true" foo="true" draggable="true"
:attr="false" foo="false" draggable="false"
:attr="0" foo="0" draggable="0"
attr="" foo="" draggable=""
attr="foo" foo="foo" draggable="foo"
attr foo="" draggable=""

†: changed

Coercion for boolean attributes is kept untouched.

Drawbacks

This proposal is introducing the following breaking changes:

  • For “enumerated attributes”:

    • null will remove the attribute instead of producing 'false'.
    • Numbers and string values other than 'true' and 'false' won't be coerced to 'true' any more. (The old behavior shouldn't have been working anyway.)
  • For all non-boolean attributes, false values won't be removed any more and will be coerced to 'false'.

The most major breakage here is that users should stop relying on false values to remove attributes, instead they are required to use null or undefined to do that. As boolean attributes are not affected, this change mostly affects enumerated attributes where 'false' value and absence of the attribute result in different element state. eg. aria-checked. It may also affect CSS selectors like [foo].

Alternatives

N/A

Adoption strategy

It's unlikely that a codemod can help in this case. We shall provide detailed information in our migration guide and document the coercion behavior in 3.x docs.

Enumerated attributes

The absence of an enumerated attribute and attr="false" may produce different IDL attribute values (which will reflect the actual state), described as follows:

Absent enumerated attr IDL attr & value
contenteditable contentEditable'inherit'
draggable draggablefalse
spellcheck spellchecktrue

To keep the old behavior work, and as we will be coercing false to 'false', in 3.x Vue developers need to make v-bind expression resolve to false or 'false' for contenteditable and spellcheck.

In 2.x, invalid values were coerced to 'true' for enumerated attributes. This was usually unintended and unlikely to be relied upon on a large scale. In 3.x true or 'true' should be explicitly specified.

Coercing false to 'false' instead of removing the attribute

In 3.x, null or undefined should be used to explicitly remove an attribute.

Comparison between 2.x & 3.x behavior

Attribute v-bind value 2.x v-bind value 3.x HTML output
2.x “Enumerated attrs”
i.e. contenteditable, draggable and spellcheck.
undefined, false undefined, null removed
true, 'true', '', 1, 'foo' true, 'true' "true"
null, 'false' false, 'false' "false"
Other non-boolean attrs
eg. aria-checked, tabindex, alt, etc.
undefined, null, false undefined, null removed
'false' false, 'false' "false"

Unresolved questions

N/A