0

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>
    );
  }
Victor
  • 1
  • 1
  • Please add an executable StackOverflow Snippet to your post. It will help readers execute your code with one click. And help create answers with one click. See [How to add a StackOverflow snippet](https://meta.stackoverflow.com/questions/269753/feedback-requested-runnable-code-snippets-in-questions-and-answers) – Danny '365CSI' Engelman Oct 15 '22 at 08:00
  • Sorry for the late response. I'm having a lot of difficulties with this because Stencil isn't supported here. – Victor Oct 16 '22 at 17:14
  • Sorry I didn't know stencil wasn't native javascript – Danny '365CSI' Engelman Oct 16 '22 at 18:09
  • Solved. Broke this up into functions and took MenuItem as an argument. Updated post. – Victor Oct 17 '22 at 12:59

0 Answers0