My task involves displaying a list of items using lit components. Each item in the list consists of a known name
and an asynchronously fetched value
.
Situation Overview:
- A generic component named
simple-list
is required to render any pairs ofname
andvalue
- There is also a specific configuration component called
foo-list
which passes data to thesimple-list
component for rendering - The time taken to fetch each
value
can vary, and their responses arrive at different times - Upon initial loading of the
foo-list
component, allnames
should display with their correspondingvalue
set as"Loading..."
- As each
value
response is received, the relevant text in the list should update to show the retrieved value.
I am facing challenges in getting the component to re-render for each update. Below is my code for both the simple-list
and foo-list
components.
- The
simple-list
component accepts a data object and re-renders its list each time a new object is provided. - The
foo-list
component initially sends all names with temporary values tosimple-list
, retrieves values for each name, and updates the data object every time a value is obtained. - I suspect that calling
this.requestUpdate
within an async anonymous method might not work, but I'm unsure how best to structure this scenario. - If there's a more efficient way to pass data or instructions into the
simple-list
component, I would appreciate any suggestions.
How can I ensure that getDataAndUpdate
displays both the initial value and the updated values once the promises are resolved?
// Modified-Simple-List.ts
import { html, LitElement } from "lit";
import { customElement, property } from "lit/decorators.js";
export type SimpleListItem = { name: string, value: string };
@customElement("simple-list")
export class SimpleList extends LitElement {
@property({ type: Array, attribute: false })
items: SimpleListItem[] = [];
update(changedProperties: Map<string, unknown>) {
super.update(changedProperties);
if (changedProperties.has("items")) {
this.requestUpdate();
}
}
render() {
return html`<ul>${this.items.map((item) => html`<li>${item.name}: ${item.value}</li>`)}</ul>`;
}
}
// Custom-Foo-List.ts
import { html, LitElement } from "lit";
import { customElement, property } from "lit/decorators.js";
import "./Modified-Simple-List";
import { SimpleListItem } from "./Modified-Simple-List";
async function getValue(name: string): Promise<string> {
return new Promise((resolve) => {
setTimeout(() => {
resolve(`Value for ${name}`);
}, Math.random() * 1000);
});
}
@customElement("foo-list")
export class MyFoo extends LitElement {
@property({ type: Array })
names: string[] = [];
@property({ type: Array, attribute: false })
data: SimpleListItem[] = [];
constructor() {
super();
this.getDataAndUpdate();
}
update(changedProperties: Map<string, unknown>) {
super.update(changedProperties);
if (changedProperties.has("names")) {
this.getDataAndUpdate();
}
}
async getDataAndUpdate() {
this.data = [];
for (let i = 0; i < this.names.length; i++) {
const value = "loading...";
const name = this.names[i];
(async () => {
this.data[i] = { name , value: await getValue(name) };
this.requestUpdate();
})();
this.data.push({ name, value });
}
this.requestUpdate();
}
render() {
return html` <simple-list .items=${this.data}></simple-list> `;
}
}