UPDATE: Solved by separating rendering into different functions and passing it the MenuItem:
renderItem(item: TreeMenuItem) {
console.log('render item:', item.label);
return (
<div>
<li>
{item.children ? <button onClick={this.toggleDisplayChildren}>{this.displayChildren ? 'v' : '>'}</button> : ''}
{this.showCheckbox ? <input type="checkbox" checked={item.selected} onClick={() => this.selectItem(item)} /> : ''}
{item.label}
</li>
{this.renderChildren(item)}
</div>
);
}
renderChildren(item: TreeMenuItem) {
return item.children && this.displayChildren
? item.children.map(child => {
child.parent = item;
return this.renderItem(child);
})
: '';
}
render() {
return <Host>{this.renderItem(this.item)}</Host>;
}
I'm having some difficulties figuring this out. I have an object with nested children and I am getting it rendered correctly. My issue lies in that I'm looking to add functionality so that when I select a parent, it will automatically extend all children elements as well(and grandchildren, etc). I am currently only able to do this for immediate children, but not any children of the children, etc.
I apologize for the verbose code. I tried to trim it down as much as possible while still showcasing the relevant parts. I am hoping to be able to achieve this without having a displayChild
boolean property in the object itself, but rather use:
@Prop({ mutable: true }) displayChildren = false;
toggleDisplayChildren = () => {
this.displayChildren = !this.displayChildren;
};
My issue is that when I try to do it this way(in a forEach loop), the this
keyword always refers to the parent calling the children, not the individual children.
TL;DR: When displayChildren = true
for a parent element, I would like for every child element of that parent that also has children under it to also displayChildren = true
.
Side note: I'm also having an issue where the checkbox is not updating automatically when item.selected
is being set. It only updates when displayChildren
is changed.
selectItem() {
this.item.selected = !this.item.selected;
this.isParent() ? this.selectParent() : this.selectChild();
this.item = { ...this.item };
}
private selectParent() {
if (!this.isParent()) return;
if (this.item.indeterminate) this.item.indeterminate = false;
if (!this.displayChildren) this.displayChildren = true;
console.log('Selecting parent.');
if (this.item.children.length === 1) {
this.item.children[0].selected = this.item.selected;
console.log('Only one child, so setting same as parent.');
} else {
this.item.children.map(child => {
child.selected = true;
console.log(`${child.label} selected: ${child.selected}`);
if (child.children) {
console.log(`${child.label} has children, so do them too.`);
}
});
}
}
items: TreeMenuItem[] = [
{
label: 'Plants',
value: 'plants',
children: [
{
label: 'Mono-Cotyledons',
value: 'monocotyledons',
children: [
{
label: 'Coconut',
value: 'coconut',
selected: false,
indeterminate: false,
disabled: false,
},
{ label: 'Banana', value: 'banana', selected: false, indeterminate: false, disabled: false },
],
selected: false,
indeterminate: false,
disabled: false,
},
{
label: 'Di-Cotyledons',
value: 'dicotyledons',
children: [{ label: 'Mango', value: 'mango', selected: false, indeterminate: false, disabled: false }],
selected: false,
indeterminate: false,
disabled: false,
},
],
selected: false,
indeterminate: false,
disabled: false,
},
];
I am rendering it as such:
render() {
return (
<div>
<li>
{this.item.children ? <button onClick={this.toggleDisplayChildren}>{this.displayChildren ? 'v' : '>'}</button> : ''}
{this.showCheckbox ? <input type="checkbox" checked={this.item.selected} onClick={() => this.selectItem()} style={{ color: 'red' }} /> : ''}
{this.item.label}
</li>
{this.item.children && this.displayChildren
? this.item.children.map(child => {
child.parent = this.item;
return <menu-item item={child} class="child-item"></menu-item>;
})
: ''}
</div>
);
}