首页 > 解决方案 > 递归组件数据引用问题

问题描述

我正在尝试构建一个类别选择器组件,该组件将类别树作为第一手的输入,如下所示:

 mockData: Category[] = [
    { id: 1, name: 'main cat1', children: [
       { id: 3, name: 'sub cat1', children: []},
       { id: 4, name: 'sub kat2', children: []}
    ]},
    { id: 2, name: 'main cat2', children: [
      { id: 5, name: 'sub cat5', children: []},
    ]},
  ]

模板:该组件以ngFor. 如果选择了一个类别,它会手动将带有#tpl 标记的模板插入到#vc 视图容器中。这是我假设这将解决问题的尝试。

<mat-chip-list aria-label="Category selection" [multiple]="false">
    <mat-chip *ngFor="let chip of data"
              [selected]="chip.id === selectedCategory?.id"
              (click)="clickChip($event, chip)">{{ chip.name }}</mat-chip>
</mat-chip-list>
<ng-container #vc></ng-container>


<ng-template #tpl>
  <span class="divider"> / </span>
  <category [data]="copyOFChildren"
            (selected)="emitSelected($event)"
  ></category>
</ng-template>

组件:

export class CategorySelectorComponent implements AfterViewInit, OnInit {

  @Input() data: Category[];
  @Output() selected: EventEmitter<Category> = new EventEmitter();

  selectedCategory: Category;
  copyOFChildren: Category[];

  @ViewChild("vc", {read: ViewContainerRef, static: true}) vc: ViewContainerRef;
  @ViewChild('tpl', {static:true}) tpl: TemplateRef<any>;
  childViewRef: ViewRef;

  ngAfterViewInit(){
    this.childViewRef = this.tpl.createEmbeddedView(null);
  }

  clickChip(event, category) {
    this.removeChildView();

    this.selectedCategory = (this.selectedCategory && this.selectedCategory.id === category.id) ? null : {...category};
    this.selected.emit(this.selectedCategory);
    this.insertChildView();
  }

  emitSelected(category: Category | null):void {
    if (category) {
      this.selected.emit(category)
    } else {
      this.selected.emit(this.selectedCategory)
    }
  }

  insertChildView(){
    if (this.selectedCategory && this.selectedCategory.children.length) {
      this.copyOFChildren = [ ...this.selectedCategory.children ];
      this.vc.insert(this.childViewRef);
    }
  }

  removeChildView(){
    this.copyOFChildren = undefined;
    this.vc.detach();
    console.log('detach')
  }
}

当我执行以下操作时会出现问题:

  1. 选择'main cat1'然后选择它的子类别(sub cat 1)

  2. 选择另一个主要类别“main cat2”,然后切换回“main cat1”

  3. 结果:“main cat1”被突出显示,但之前已选择(步骤 1。)“sub cat1”也被突出显示

输出正确,日志显示选择了“main cat1”

所以,问题是“sub cat1”芯片在被选中时仍然突出显示。只应突出显示“main cat1”。

似乎插入的组件以某种方式保存了它的数据。我无法弄清楚如何。

堆栈闪电战

标签: angular

解决方案


同时找到了解决办法。

问题是我使递归元素的组件保持活动状态,因此它保留在内存中,因为它存在于模板中,如下所示:

<ng-template #tpl>
  <span class="divider"> / </span>
  <category [data]="copyOFChildren"
            (selected)="emitSelected($event)"
  ></category>
</ng-template>

解决方案:<ng-template>我没有使用ComponentFactoryResolver手动创建子组件并将输入和输出绑定到它。

查看演示:StackBlitz


推荐阅读