Angular - 切换无限嵌套的孩子

时间:2018-06-05 18:23:28

标签: angular

我有一个可以拥有未知数量的项目和无限嵌套的数组。我需要能够为每个父母添加一个按钮来切换孩子的显示,每个孩子都可以有一个按钮来切换孩子。此外,每个孩子都有一个可以切换父母的按钮。

我无法让切换工作。我现在的方式是基于数组的索引,对于每组子项,它从0开始,如果它们具有相同的索引,可能导致多个项目同时打开。

我在Stackblitz上有一个关于我的问题的例子:https://stackblitz.com/edit/angular-nested-menu-toggle?file=src%2Fapp%2Fapp.component.html

您可以通过单击“五”按钮,然后按“five.two”按钮来查看问题。你可以看到它打开“five.two”以及“two”和“two.two”,因为它们都有相同的索引。

所以我需要的是能够切换一个项目,它只影响其直接的父母或孩子,而不是同时影响其他所有项目。

这是我的阵列:

myArray = [
  {
    'title': 'one'
  },
  {
    'title': 'two',
    'children': [
      {
        'title': 'two.one'
      },
      {
        'title': 'two.two',
        'children': [
          {
            'title': 'two.two.one'
          },
          {
            'title': 'two.two.two'
          }
        ]
      },
      {
        'title': 'two.three',
        'children': [
          {
            'title': 'two.three.one'
          },
          {
            'title': 'two.three.two'
          }
        ]
      }
    ]
  },
  {
    'title': 'three',
    'children': [
      {
        'title': 'three.one'
      },
      {
        'title': 'three.two',
        'children': [
          {
            'title': 'three.two.one'
          },
          {
            'title': 'three.two.two'
          }
        ]
      },
      {
        'title': 'three.three',
        'children': [
          {
            'title': 'three.three.one'
          },
          {
            'title': 'three.three.two'
          }
        ]
      }
    ]
  },
  {
    'title': 'four'
  },
  {
    'title': 'five',
    'children': [
      {
        'title': 'five.one'
      },
      {
        'title': 'five.two'
      }
    ]
  },
  {
    'title': 'six',
    'children': [
      {
        'title': 'six.one'
      },
      {
        'title': 'six.two',
        'children': [
          {
            'title': 'six.two.one'
          },
          {
            'title': 'six.two.two'
          }
        ]
      },
      {
        'title': 'six.three',
        'children': [
          {
            'title': 'six.three.one'
          },
          {
            'title': 'six.three.two'
          }
        ]
      }
    ]
  }
];

然后我在构造函数中有一个toggle变量:

constructor() {
  this.toggle = this.myArray.map(i => false);
}

最后,我的HTML

<ul>
  <li *ngFor="let item of myArray; let i = index">
    <button (click)="toggle[i] = !toggle[i]">{{item.title}}</button>
    <div *ngIf="item.children && toggle[i]">
      <ng-container *ngTemplateOutlet="tree; context: { $implicit: item.children, idx: i }"></ng-container>
    </div>
  </li>
</ul>

<ng-template #tree let-allItems let-idx="idx">
  <ul>
    <li *ngFor="let item of allItems; let n = index">
      <button (click)="toggle[idx] = !toggle[idx]">X</button>
      <button (click)="toggle[n] = !toggle[n]">{{item.title}}</button>
      <div *ngIf="item.children && toggle[n]">
        <ng-container *ngTemplateOutlet="tree; context: { $implicit: item.children, idx: n }"></ng-container>
      </div>
    </li>
  </ul>
</ng-template>

谢谢!

3 个答案:

答案 0 :(得分:2)

一种可能的解决方案是创建普通的javascript对象:

toggle: any = {};

并利用组合索引获取树中特定项的状态,使其看起来像:

enter image description here

如您所见,所有指数都是独一无二的。

您需要做的一件事是传递正确的索引:

<ul>
  <li *ngFor="let item of myArray; let i = index">
    <button (click)="toggle[i] = !toggle[i]">{{item.title }} - {{i}}</button>
    <div *ngIf="item.children && toggle[i]">
      <ng-container *ngTemplateOutlet="tree; context: { $implicit: item.children, idx: i }">
      </ng-container>
    </div>
  </li>
</ul>

<ng-template #tree let-allItems let-idx="idx">
  <ul>
    <li *ngFor="let item of allItems; let n = index">
      <button (click)="toggle[idx] = !toggle[idx]">X - {{idx}}</button>
                              ^^^^
                           Parent index
      <button (click)="toggle[idx + '.' + n] = !toggle[idx + '.' + n]">{{item.title}} - {{ idx + '.' + n}}</button>
                              ^^^^^^^^^^^^^
                               Child index
      <div *ngIf="item.children && toggle[idx + '.' + n]">
        <ng-container 
          *ngTemplateOutlet="tree; context: { $implicit: item.children, idx: idx + '.' + n }"></ng-container>
                                                                             ^^^^^^^^^^^^^
                                                           Child index becomes parent in next iteration
      </div>
    </li>
  </ul>
</ng-template>

请注意我如何通过

生成新索引
idx + '.' + n

<强> Stackblitz Example

答案 1 :(得分:1)

创建一个接收项目作为输入的项目组件。现在每个项目只对自己负责。组件模板将是这样的:

<p>
  <span>{{item.title}}</span>
  <button *ngIf="hasChildren" type="button" class="toggleBtn" (click)="toggleChildren()">Toggle Children</button>
  <button *ngIf="hasParent" type="button" class="toggleBtn" (click)="toggleParent()">Toggle Parent</button>
</p>


<ul *ngIf="areChildrenOpen">
  <li *ngFor="let child of item.children">
    <app-item [item]="child" [hasParent]="true" (toggleClicked)="toggleChildren()"></app-item>
  </li>
</ul>

这是处理它的更清洁的方式。

<强> A full Stackblitz example

答案 2 :(得分:0)

我实现了以下递归树的基本解决方案;

技巧是在父模板中定义变量并将其用于所有子对象。如果单击父对象,然后将showChilds变量更改为反转,则所有子对象将被隐藏或显示。

<ng-container *ngTemplateOutlet="tree; context: { $implicit: myArray, idx: 1  , isChildOpen = false   }">
</ng-container>
    

    <ng-template #tree let-allItems let-idx="idx" let-showChilds="isChildOpen">
      <ul>
        <li *ngFor="let item of allItems; let n = index">
          <button (click)="showChilds = !showChilds">X - {{idx}}</button>
                             
                               Parent index
          <button (click)="showChilds = !showChilds">{{item.title}} - {{ idx + '.' + n}}</button>
                                   Child index
          <div *ngIf="item.children && showChilds">
            <ng-container 
              *ngTemplateOutlet="tree; context: { $implicit: item.children, idx: idx + '.' + n, isChildOpen:showChilds  }"></ng-container>
                                                                                 
                                                               Child index becomes parent in next iteration
          </div>
        </li>
      </ul>
    </ng-template>