I encountered some strange problem where a Lit Web component with Property Expression (.checked
) does not get updated. Here is a simple reproduction at Lit Playground:
import {html, css, LitElement} from 'lit';
import {customElement, property} from 'lit/decorators.js';
@customElement('simple-greeting')
export class SimpleGreeting extends LitElement {
static styles = css`p { color: blue }`;
@property()
selected = false;
render() {
debugger;
return html`<input type="checkbox"
.checked=${this.selected}
@change=${this.handleChange} />`;
}
handleChange(e) {
this.selected = e.target.checked;
this.dispatchEvent(new Event("change"));
}
}
And the HTML:
<simple-greeting></simple-greeting>
<script>
const chk = document.querySelector("simple-greeting");
chk.addEventListener("change", () => {
console.log(chk.selected);
chk.selected = false;
});
</script>
Here, every time I click on the checkbox, the value is true, false, true, false, true, false, ...
instead of true
all the time because it is supposed to revert back to false by the code. (In case anyone asks me why I do this, in real scenario, my checkbox is reverted if certain condition is not met).
When debugging, render()
is called correctly and the value for selected
is false
(which is correct). However the result DOM element still has its checked
being true half of the time.
Above screenshot: selected
is false
but the rendered checkbox ($0
is pointing to the checkbox, not the component) $0.checked
is still true
.
What is the issue here?
EDIT to add in case it helps figuring out the problem easier, if I update the property in a separate frame instead, the problem does not happen:
requestAnimationFrame(() => chk.selected = false);
As mentioned in the lit documentation
If you want to overwrite the DOM value with the bound value no matter what—use the live()
directive
import {html, css, LitElement} from 'lit';
import {customElement, property} from 'lit/decorators.js';
import {live} from 'lit/directives/live.js';
@customElement('simple-greeting')
export class SimpleGreeting extends LitElement {
static styles = css`p { color: blue }`;
@property()
selected = false;
render() {
console.log(this.selected,'Render');
return html`<input type="checkbox"
.checked=${live(this.selected)}
@change=${this.handleChange} />`;
}
handleChange(e) {
this.selected = e.target.checked;
this.dispatchEvent(new Event("change"));
}
}