I am using Lit-HTML ( NOT Lit-Element ) to build a html form. The form is very simple, it has 2 dropdowns: Population dropdown and Animal dropdown.
The animal dropdown options depends on the selection of the Population dropdown. See the getAnimalDS() logic.
Model Definition & Utility function:
const model = {
Population: null,
Animal: null,
}
const dataSources = {
Populations: [null, "p1", "p2"],
Animals_1: [null, "a1", "a2"],
Animals_2: [null, "a3", "a4", "a5"],
}
function getAnimalDS(population) {
switch (population) {
case 'p1':
return dataSources.Animals_1;
case 'p2':
return dataSources.Animals_2;
default:
return [];
}
}
The main function to build LIT-HTML template:
function buildSection1(model) {
return html`
<div class="mb-3">
<label for="Population" class="form-label">Population</label>
<select id="Population" name="Population" class="form-control"
.value="${model.Population}" @change=${e => {
model.Population = e.target.value;
model.Animal = null; // Reset Animal
renderSection1(); // Re-render the form
}}>
${dataSources.Populations.map((item) => html`
<option value="${item}" ?selected="${model.Population === item}">${item}</option>
`)}
</select>
</div>
<div class="mb-3">
<label for="Animal" class="form-label">Animal</label>
<select id="Animal" name="Animal" class="form-control"
.value="${model.Animal}" @change=${e => model.Animal = e.target.value}>
${getAnimalDS(model.Population).map((item) => html`
<option value="${item}" ?selected="${model.Animal === item}">${item}</option>
`)}
</select>
</div>
`;
}
TESTING:
If I select a population on the Population dropdown, I see the options on the Animal get changed correctly -> Awesome.
STEPS to see issue:
#1: Select option "p1" on the Population dropdown, I see 3 options: "", 'a1', 'a2' on the Animal dropdown -- Good
#2: Next, I select 'a2' on the Animal dropdown ( selected index = 2) -> Good
#3: Now, select option "p2" on the Population dropdown, I see 4 options: "", 'a3', 'a4', 'a5' on the Animal dropdown -> Good
#4: At this point, I expect to see 'EMPTY' line (selected index = -1) on the Animal dropdown , because on @change of the Population, I did reset model.Animal to null.
---- BUT the Animal dropdown shows option 'a4' ( selected index = 2 )
---- It means LIT-HTML uses the previous selected index = 2 at step #2.
Any comments? Thanks!
The reason for this behavior is because lit-html is not aware that the value for the 2nd <select id="Animal">
element has changed, because the @change
event handler changes model.Animal
to 'a2'
but does not call on render()
again. Since lit-html thinks the value is still null
, it doesn't bother updating the DOM when re-rendering.
Solution 1: You can fix this by adding renderSection1()
call there as well like:
html`
...
<select id="Animal" name="Animal" class="form-control"
.value="${model.Animal}"
@change=${e => {
model.Animal = e.target.value;
renderSection1();
}}>
...
</select>`
which makes lit-html aware that the value has been updated. So when the first dropdown changes the value back to null
it knows it's different again and update the DOM.
Solution 2: Alternatively, you can also make lit-html always check the value against the DOM value by using the live()
directive:
import {live} from 'lit-html/directives/live.js';
html`
...
<select id="Animal" name="Animal" class="form-control"
.value="${live(model.Animal)}" // always compare against DOM
@change=${e => model.Animal = e.target.value}>
...
</select>`