Skip to content

Better boolean attribute behavior #12342

@jfbrennan

Description

@jfbrennan

What problem does this feature solve?

I understand there is already bias against this topic, but I'd ask for a chance to revisit it in more detail.

After the necessary removal of proprietary enumerated attributes behavior, the boolean attributes behavior has been made less intuitive, more difficult, and error-prone.

Less intuitive because HTML attributes take string values, so it was widely understood a boolean value of false would result in no attribute at all, e.g. <div :attr="false"> would result in <div>. This was ideal because the code for controlling these attributes naturally produced a boolean value, i.e. it Just Worked. Attributes that need strings "true" and "false" were easily provided with bool.toString(), which made sense because HTML attributes only take strings. More on these attributes below.

Also, Vue now matches native setAttribute('attr', false) which results in attr="false", so you might expect Vue to give null equal treatment, e.g. setAttribute('attr', null) results in attr="null", but it doesn't.

More difficult because developers now have to work against JavaScript to ensure their data, template expressions, methods, and/or computed properties result in an unnatural null value. It may seem as simple as :attr="bool || null" but any computed or method with logic branches the dev now has to go the extra mile to ensure null and not false is going to be the final output of the branching. Also, this logic :attr="bool || null" just makes my brain go Huh? If false give me a null? Never written an expression like that before, would hate to have to scatter that all over my templates.

More error-prone because the odds of a value being a boolean are very high and the ubiquitous boolean is now a liability in templates. One way to mitigate this new risk is to make all things that could be false be null instead. This avoids the possibility of another dev using something that isn't boolean attribute safe. A terrible pattern, but if it means reducing risk devs may go that route. Gross.

Okay, on to attributes that take "true" and "false" strings...

It seems like what's happened is Vue has replaced some old special behavior (i.e. enumerated attributes) for new special behavior based on assumptions about the importance of a relatively small number of attributes that only take the strings "true" or "false". In short, "true" or "false" was like a decoy that resulted in introducing odd behavior.

Let me explain with some examples:

  • Sure, attributes like aria-required need a string value of "true" or "false", however...
  • Attributes like contenteditable are likely given string values by the developer because "true" and "false" are just two of several possible strings the developer can set on that attribute. Devs can write code like :contenteditable="plainText ? 'plaintext-only' : 'false'", so it's strange for Vue to go out of its way to cast booleans to strings for the developer to support this case.
  • Same story with aria-invalid. It supports the strings "grammar", "spelling", "true", or "false". The developer will supply one of the four strings, not one of two strings or a boolean. Yes, someone could write the latter, but you see where I'm going.
  • Same again for aria-haspopup, which supports 7 different string values, including true and false.
  • And then there's aria-expanded and aria-pressed, which support "true", "false", and "undefined" (pressed also supports a fourth string "mixed"). But if I give Vue undefined it doesn't cast it to a string like it does for false it removes that attribute, which made sense in Vue 2 but not now. Vue requires the developer to supply the string "undefined", but casts false for them. Vue does this because it mistakenly believes there's something special about attributes that take "true" or "false" strings. This is not great and it's made even more weird because setAttribute('attr', undefined) normally results in attr="undefined".

I think true and false string values can be awkward to reason about at first, so I can emphasize, but developers are now put in an even more awkward situation trying to ensure false becomes null as well as navigating what Vue will and won't do for them in regards to casting strings for attributes.

The fix
Developers are required to provide whatever string their attributes need, including the not special "true" and "false" strings, while Vue reserves the power of false (and null or undefined) to remove attributes.

What does the proposed API look like?

These three values remove the attribute (uses removeAttribute('attr')) 
<div :attr="false"> => <div>
<div :attr="null"> => <div>
<div :attr="undefined"> => <div>

True adds an attribute with no value, i.e. a boolean attribute  (using setAttribute('attr', '') not setAttribute('attr', true))
<div :attr="true"> => <div attr>

Everything else (same as setAttribute('attr', val))
<div :attr="'true'"> => <div attr="true">
<div :attr="'false'"> => <div attr="false">
<div :attr="'null'"> => <div attr="null">
<div :attr="'undefined'"> => <div attr="undefined">
<div :attr="0"> => <div attr="0">
<div :attr="''"> => <div attr="''">
<div :attr="'hi'"> => <div attr="'hi'">

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