I'm trying to build an interactive tree-like component using Lit, and I'm running into some issues when trying to remove tree nodes. Here's the implementation of the component:
import { html, LitElement } from 'https://cdn.jsdelivr.net/gh/lit/dist@2/all/lit-all.min.js';
let counter = 0;
export class TreeNode extends LitElement {
static properties = {
label: { type: String },
children: { state: true }
};
constructor() {
super();
this.children = [];
}
render() {
return html`
<div>
${this.label}
<button @click=${this.onAddButtonClick}>add child</button>
<button @click=${this.onRemoveButtonClick}>remove</button>
</div>
<ul>
${this.children.map(child => html`
<li>
<tree-node label=${child.label} @remove=${(ev) => { ev.stopPropagation(); this.removeChild(child.id); }}></tree-node>
</li>
`)}
</ul>
`;
}
onAddButtonClick() {
const id = counter++;
this.children = [...this.children, { id, label: `Node #${id}` }];
}
onRemoveButtonClick() {
this.dispatchEvent(new Event('remove', { bubbles: true, composed: true }));
}
removeChild(id) {
this.children = this.children.filter(child => child.id !== id);
}
}
customElements.define('tree-node', TreeNode);
export class TreeView extends LitElement {
render() {
return html`<tree-node label="root"></tree-node>`;
}
}
customElements.define('tree-view', TreeView);
And a working example is here: https://codepen.io/petrbroz/pen/wvyzzPp
Now, let's say I have the following tree structure:
- Node A
- Node B
- Node C
- Node D
- Node E
- Node F
Now, if I try to remove Node B
, its children are not removed, but instead they become children of Node F
. Why is that? Am I doing something wrong in the implementation? I've been able to work around this by manually passing the children to nested nodes as shown below, but I'd like to understand why the original implementation isn't working. Thanks!
import { html, LitElement } from 'https://cdn.jsdelivr.net/gh/lit/dist@2/all/lit-all.min.js';
let counter = 0;
export class TreeNode extends LitElement {
static properties = {
label: { type: String },
children: { type: Array }
};
constructor() {
super();
this.children = [];
}
render() {
return html`
<div>
${this.label}
<button @click=${this.onAddButtonClick}>add child</button>
<button @click=${this.onRemoveButtonClick}>remove</button>
</div>
<ul>
${this.children.map(child => html`
<li>
<tree-node label=${child.label} .children=${child.children} @remove=${(ev) => { ev.stopPropagation(); this.removeChild(child.id); }}></tree-node>
</li>
`)}
</ul>
`;
}
onAddButtonClick() {
const id = counter++;
this.children = [...this.children, { id, label: `Node #${id}`, children: [] }];
}
onRemoveButtonClick() {
this.dispatchEvent(new Event('remove', { bubbles: true, composed: true }));
}
removeChild(id) {
this.children = this.children.filter(child => child.id !== id);
}
}
customElements.define('tree-node', TreeNode);
export class TreeView extends LitElement {
render() {
return html`<tree-node label="root"></tree-node>`;
}
}
customElements.define('tree-view', TreeView);