I am trying to use the conditional attribute notation with an async function, something like that :
<span ?special="${this.asyncMethodThatReturnsTrueOrFalse()}">...</span>
However It seems that lit-html will always evaluate this expression as true even if the method returns false (minimal example).
Thanks for the reproduction @vdegenne!
At the moment only NodeParts handle promises and other types, AttributePart, PropertyPart, EventPart and BooleanAttributePart take the value as-is without special handling for promises, iterables, nodes etc.
In my opinion that shouldn't change. For boolean attributes, changing the behaviour would give unexpected consequences when trying to set an attribute depending on whether a value is truthy or not. That'll work for any value, except for promises (or rather: any value with a then property) because for those we'll look at the truthiness of the value they eventually get fulfilled with.
It's of course true that one can always write
<span ?special=${this.somePromise != null}>
which will always work with or without the change. When I see a template with
<span ?special=${this.something}>
I interpret that as
if (this.something) {
spanEl.setAttribute('special', '');
} else {
spanEl.removeAttribute('special');
}
so in my mind the behaviour described in the OP is very much expected behaviour.
To support promises in the (boolean) attribute part we could easily update the until directive to support all part types and not just NodeParts. Right now this directive makes use of the fact that NodePart supports promises out of the box, but we could update until to subscribe to the promise itself.
This feels more correct to me for two reasons:
BooleanAttributePart automatically handles promises, I cannot declare whether I want the attribute to be present or not while the promise is unresolved.@bgotink The reason why I used an asynchronous method here was because I needed the template to fully render. this.asyncMethodThatReturnsTrueOrFalse() is a method in a LitElement element. This method waits the dom to fully render and returns true or false based on an index :
{
async isCardInactive(index) {
await this.updateComplete;
const card = this.shadowRoot.querySelectorAll('x-card')[index];
await card.updateComplete;
return !card._active;
}
render() {
return html`
<style>button[inactive] { opacity: .4 }</style>
${repeat(this.cardsIds, (cardId, index) => html`
<button ?inactive=${isCardInactive(index)}>${cardId}</button>
`}
${repeat(this.cardsIds, cardId => html`<x-card .cardId=${cardId}></x-card>`)}
`;
}
}
It's my bad that I didn't specify the context. But I agree this behavior was very much expected. This issue was more like a feature request as it would save the writing of an extra method that requires dom manipulation.
It would most definitely be a nice feature to have! I'm just arguing that I'd modify the until directive to support attributes (and boolean attributes) so we can do
<button ?inactive=${until(this.isCardInactive(index), false)}>${cardId}</button>
Closed by #555
Most helpful comment
It would most definitely be a nice feature to have! I'm just arguing that I'd modify the
untildirective to support attributes (and boolean attributes) so we can do