lit/lit-element

Boolean PropertyAttributes best way to handle propProp="false" as false

MichaelPeter opened this issue · 7 comments

Hello community,

I would like to handle this:

<mycomp isactive="false" />

as false value, it is only false if the attribute is not set.

I saw the quote: https://lit-element.polymer-project.org/guide/properties

For Booleans, when the attribute is:

    non-null, set the property to true.
    null or undefined, set the property to false.

So the best way is to write a custom converter for the value? Or is there a better way?

Also, the sample also has a bug, I talk about the prop1, prop2, prop3 sample here:
https://lit-element.polymer-project.org/guide/properties

if I press the changeAttributes button, prop3 always stays true, no matter if the value is prop3="true" or prop3="false"

event if I set
this.setAttribute('prop3', null);
or this.setAttribute('prop3', undefined);

the value stays true.

In HTML boolean attribute are "truthy" based on their presence not their value.

<div disabled></div> <!-- this div is disabled -->
<div disabled="false"></div> <!-- this div is also disabled -->

The way to set a boolean attribute to "false" is to remove it:

this.removeAttribute('prop3');

This is what LitElement does if you set the prop3 property to false:

this.prop3 = false;
await this.updateComplete; // Wait for the LitElement to update
this.hasAttribute('prop3'); // false

https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes#Boolean_Attributes

My Problem is just that I use the Blazor Framework on top of my webcomponents and there binding works like this:

<my-link isactive="@_isActive"></my-link>

@code
{
        bool _isActive = true;
        public void MakeInactive()
        {
              _isActive=false;
        }
}

Removing attribute on false requires lots of not nice workarround code.

Example here: https://stackoverflow.com/questions/58621769/conditional-attributes-that-is-not-boolean-in-blazor

So even if _isActive would be false the link would still be active, since the attribute is truthy...
Also if I write in my examples

<my-link isactive="true"></my-link>

The first thing everybody tries to do is set isactive="false" and wonder why it is not working...
This would be nasty bugs nobody would find quickly.

So I will need to write for all my booleans custom converter.

We could offer a convert for this case, but the attributes would not be boolean in the HTML sense. They would be more akin to enum attributes.

Thanks for the quick answer :)

like a BoolEnum with Values True and False? Could you show a sample how this could be done?

So far this was my solution:
Is there a better way?

    static get properties() {
        return {
            itemid: { type: String },
            label: { type: String },
            href: { type: String },
            isactive: { converter: getBoolConverter() }
        };

export function getBoolConverter() {
    return {
        fromAttribute: (value, type) => {
            if (value && value != 'false') {
                return true;
            }
            else {
                return false;
            }
        },
        toAttribute: (value, type) => {
            if (value) {
                return 'true';
            } else {
                return 'false';
            }
        }
    };
}

What you've done seems pretty reasonable. Needing custom logic around handling attributes is exactly why we exposed the converter in the first place. Good job figuring that out.

@sorvell I've seen this come up a few times, and HTML does have a couple of enum-values attributes, including ones that accept literal "true" and "false" strings. I wonder if we should offer an EnumConverter in a module?

What you've done seems pretty reasonable. Needing custom logic around handling attributes is exactly why we exposed the converter in the first place. Good job figuring that out.

Must say the documentation is quite good if you actually ready it - in detail ;) @justinfagnani @sorvell