0

I have lit-element based forms that are reused for editing entities. As an example consider a name form with the following fields

<mwc-textfield id="first-name" label="First name" value="${this.firstName ?? ''}"></mwc-textfield>
<mwc-textfield id="middle-name" label="Middle name" value="${this.middleName ?? ''}"></mwc-textfield>
<mwc-textfield id="last-name" label="Last name" value="${this.lastName ?? ''}"></mwc-textfield>

The user selects an entity from some list and I fill the properties firstName, middleName, lastName with the corresponding values. The user then may edit the values and press a save button which reads the values of the text fields with something like

this.firstName = this.shadowRoot.getElementById("first-name").value;

and writes them back to the database. This all works well except for one case: when the user edited the fields but then decides to not save the changes and instead load another entity, the data binding seems to be somehow broken for empty properties.

So, f.e. assume the user selected 'Fred Krueger' (firstName=Fred, middleName=null, lastName=Krueger), then typed a middle name 'Jacob' into the field but then decides to not save it and load 'Sarah Mueller' instead. Since her middle name is null, the text field should be cleared. What happens instead is that 'Jacob' remains in the field!

It seems as if a null value will not clear a text field that was edited by the user and not synchronized to the bound property. Note that properties that are not null do always update the fields!

The problem can be resolved by adding a change and keyup handler to each and every textfield in my form to synchronize the corresponding properties.

Question: Is this really the only way to do it? Why does a property with value null not update the field when it has been edited before?

(Edit: changing the binding from value to .value does not fix this!)

NicolasR
  • 2,222
  • 3
  • 23
  • 38

1 Answers1

1

This is due to the value property on the DOM being changed by the user input and Lit can't tell the DOM element needs to change. As you've noticed, using an event listener to keep the DOM value and Lit property always in sync is one way to avoid this, and this is often preferable since the Lit components will always have the latest value without needing to look it up from the DOM node.

Alternatively, using the live directive can force Lit to check against the current live value on the DOM node and properly update it.

import {live} from 'lit/directives/live.js';

<mwc-textfield id="first-name" label="First name" value="${live(this.firstName ?? '')}"></mwc-textfield>
<mwc-textfield id="middle-name" label="Middle name" value="${live(this.middleName ?? '')}"></mwc-textfield>
<mwc-textfield id="last-name" label="Last name" value="${live(this.lastName ?? '')}"></mwc-textfield>
Augustine Kim
  • 841
  • 1
  • 5
  • Thanks, that explains it. Saw the live directive some time ago but forgot about it. I am wondering why this isn't the default behavior. Does no one else have these issues? I have them all the time but maybe it's because I am reusing the forms. Do you know whether there is any critical difference in performance between the two approaches? My forms may have up to 20 text fields, radio buttons etc. Using the live directive would make the code more compact than all the event handlers. – NicolasR Jan 06 '23 at 11:44
  • 1
    The default way that Lit does change detection should be a slight performance boost that you'd be opting out of by using the `live` directive, though syncing via events for every input likely adds more overhead. For ~20 elements, perf difference won't be significant either way. As you've said, using the `live` directive would be simpler code if you're fine pulling the latest value from the DOM on save/submit. But if you want a fully controlled setup where the host always has the current state, events would be the way to go. – Augustine Kim Jan 06 '23 at 23:27