I have an LitElement based web component, that renders a mdw-dialog with a custom element inside that uses a property from the parent component. When user click an a button, the dialog is shown that renders a couple of input fields. This all works great, but I want the property to be reset to an empty object whenever the button is clicked, before the dialog is shown and though I can easily do so - it does not appear to have an effect on the dialog elements.
Parent element
export class PageApplications extends LitElement {
static get properties() {
return {
application: { type: Object }
}
}
constructor() {
super();
this.application = {};
}
onMouseOver(event) {
this.shadowRoot.querySelector('mwc-fab').extended = ('mouseenter' === event.type);
}
openDialog() {
console.log('PageApplication.openDialog:', this.application);
this.application = {};
let dialog = this.shadowRoot.querySelector('mwc-dialog');
dialog.requestUpdate;
dialog.open = true;
}
closedDialog(event) {
console.log('closedDialog:', this.application)
if ('ok' === event.detail.action) {
// NOP
}
}
render() {
console.log('PageApplication.render', this.application);
return html`
<style type="text/css">
mwc-fab {
position: fixed;
right: 1rem;
}
mwc-list {
background-color: #FFF;
}
</style>
<mwc-fab showIconAtEnd icon="add_circle" label="New Application"
@mouseenter="${this.onMouseOver}"
@mouseout="${this.onMouseOver}"
@click="${this.openDialog}"
>
</mwc-fab>
<mwc-dialog heading="Create Application"
@closed="${this.closedDialog}">
<cnvy-create-application
.application="${this.application}"
></cnvy-create-application>
<mwc-button
slot="primaryAction"
dialogAction="ok"
>Create</mwc-button>
<mwc-button
slot="secondaryAction"
dialogAction="cancel"
>Cancel</mwc-button>
</mwc-dialog>
<h2>Applications</h2>
as you can see the application property is bound to element cnvy-create-application
export class ConvoyCreateApplication extends LitElement {
constructor() {
super();
}
static get properties() {
return {
application: { type: Object }
}
}
render() {
console.log('ConvoyCreateApplication.render', this.application);
return html`
<style>
mwc-formfield {
display: block;
}
</style>
<mwc-formfield>
<mwc-textfield
placeholder="Name"
helper="Name of application"
.value="${this.application.name}"
@change="${e => this.application.name = e.target.value}"
></mwc-textfield>
</mwc-formfield>
<mwc-formfield>
<mwc-textarea
rows="2"
placeholder="Description"
helper="Short description"
.value="${this.application.description}"
@change="${e => this.application.description = e.target.value}"
></mwc-textfield>
</mwc-formfield>
`;
}
}
customElements.define('cnvy-create-application', ConvoyCreateApplication);
Problem is - the first time I click the button, the dialog is shown as expected and each textfield says "undefined", which is probably ok as props are not defined in empty object. I enter value respectively "a" and "b" and click ok in the dialog and console prints
closedDialog: {name: "a", description: "b"}
as expected. If I then click button again, the "application" property should be reset to empty object but UI shows textfields with "a" and "b", and if I change "b" to "d", only "d" is sent back
closedDialog: {description: "d"}
meaning the application object was infact reset to empty, BUT this is not what is shown ... so, how do I trigger the update? I've tried various "update", "requestUpdate" .. and such lifecycle methods but to no avail ..
mwc-textfield
internally uses a native input
and passes the value
property binding to it. This is one of the cases where a property is updated from multiple sources (from .value="${this.application.name}"
and from the DOM when editing the text). In this scenario lit-html
may not know how to update the value and can cause a misalignment between the data and what's actually shown. To solve this you can use the live
directive: it
checks binding value against the live DOM value, instead of the previously bound value, when determining whether to update the value.
import {live} from 'lit-html/directives/live';
// ...
render() {
return html`
<mwc-textfield
.value=${live(this.application.name)}
^^^^
></mwc-textfield>
`;
}
In openDialog()
you have probably missed the parenthesis in dialog.requestUpdate˅;
although there's no need to call it: this.application = {};
is sufficient to trigger an update.
To prevent the textfields from showing undefined
you can either provide a fallback value in the binding
<mwc-textfield
.value=${live(this.application.name || '')}
></mwc-textfield>
or initialize the entire data structure with empty values:
constructor() {
this.application = {
a: '',
b: '',
// ...
};
}