动态添加和删除Angular中的组件

Mar*_*one 31 html typescript angular

目前的官方文档仅显示如何动态更改标记的组件<ng-template>.https://angular.io/guide/dynamic-component-loader

我想实现的是,让我们说我有3个部分组成:header,section,并footer具有以下选择:

<app-header>
<app-section>
<app-footer>
Run Code Online (Sandbox Code Playgroud)

再有6个按钮,这将增加或删除每个组件:Add Header,Add Section,和Add Footer

当我点击时Add Header,页面将添加<app-header>到呈现它的页面,因此页面将包含:

<app-header>
Run Code Online (Sandbox Code Playgroud)

然后如果我点击Add Section两次,该页面现在将包含:

<app-header>
<app-section>
<app-section>
Run Code Online (Sandbox Code Playgroud)

如果我点击Add Footer,页面将包含所有这些组件:

<app-header>
<app-section>
<app-section>
<app-footer>
Run Code Online (Sandbox Code Playgroud)

是否有可能在Angular中实现这一目标?请注意,这ngFor不是我正在寻找的解决方案,因为它只允许向页面添加相同的组件,而不是不同的组件.

编辑:ngIf和ngFor不是我正在寻找的解决方案,因为模板已经预先确定.我正在寻找的东西就像一堆组件或一组组件,我们可以轻松地添加,删除和更改数组的任何索引.

编辑2:为了使它更清楚,让我们有另一个例子说明为什么ngFor不起作用.假设我们有以下组件:

<app-header>
<app-introduction>
<app-camera>
<app-editor>
<app-footer>
Run Code Online (Sandbox Code Playgroud)

现在,这里有一个新组件,ngIf用户希望在其间插入ngFor.ngFor只有在我想要循环遍历的同一个组件时才有效.但是对于不同的组件,ngFor在这里失败了.

Ash*_*dem 42

您尝试实现的目标可以通过使用动态创建组件ComponentFactoryResolver然后将它们注入到组件中来完成ViewContainerRef.动态执行此操作的一种方法是将组件的类作为函数的参数传递,该函数将创建并注入组件.

见下面的例子:

import {
  Component,
  ComponentFactoryResolver, Type,
  ViewChild,
  ViewContainerRef
} from '@angular/core';

// Example component (can be any component e.g. app-header app-section)
import { DraggableComponent } from './components/draggable/draggable.component';

@Component({
  selector: 'app-root',
  template: `
    <!-- Pass the component class as an argument to add and remove based on the component class -->
    <button (click)="addComponent(draggableComponentClass)">Add</button>
    <button (click)="removeComponent(draggableComponentClass)">Remove</button>

    <div>
      <!-- Use ng-template to ensure that the generated components end up in the right place -->
      <ng-template #container>

      </ng-template>
    </div>

  `
})
export class AppComponent {
  @ViewChild('container', {read: ViewContainerRef}) container: ViewContainerRef;

  // Keep track of list of generated components for removal purposes
  components = [];

  // Expose class so that it can be used in the template
  draggableComponentClass = DraggableComponent;

  constructor(private componentFactoryResolver: ComponentFactoryResolver) {
  }

  addComponent(componentClass: Type<any>) {
    // Create component dynamically inside the ng-template
    const componentFactory = this.componentFactoryResolver.resolveComponentFactory(componentClass);
    const component = this.container.createComponent(componentFactory);

    // Push the component so that we can keep track of which components are created
    this.components.push(component);
  }

  removeComponent(componentClass: Type<any>) {
    // Find the component
    const component = this.components.find((component) => component.instance instanceof componentClass);
    const componentIndex = this.components.indexOf(component);

    if (componentIndex !== -1) {
      // Remove component from both view and array
      this.container.remove(this.container.indexOf(component));
      this.components.splice(componentIndex, 1);
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

笔记:

  1. 如果您希望以后更容易删除组件,可以在局部变量中跟踪它们,请参阅this.components.或者,您可以循环遍历内部的所有元素ViewContainerRef.

  2. 您必须将组件注册为条目组件.在模块定义中,将组件注册为entryComponent(entryComponents: [DraggableComponent]).

运行示例:https: //plnkr.co/edit/mrXtE1ICw5yeIUke7wl5

有关更多信息,请访问:https: //angular.io/guide/dynamic-component-loader

  • 我在答案中添加了一个plunker.请参阅[https://plnkr.co/edit/mrXtE1ICw5yeIUke7wl5](https://plnkr.co/edit/mrXtE1ICw5yeIUke7wl5).希望这可以帮助. (4认同)
  • 这里还有一个关于此的教程:https://courses.ultimateangular.com/courses/angular-pro/lectures/2170084 (2认同)

Was*_*siF 12

我创建了一个演示来显示动态添加和删除过程.父组件动态创建子组件并将其删除.

点击进行演示

父组件

import { ComponentRef, ComponentFactoryResolver, ViewContainerRef, ViewChild, Component } from "@angular/core";

@Component({
    selector: 'parent',
    template: `
    <button type="button" (click)="createComponent()">
        Create Child
    </button>
    <div>
        <ng-template #viewContainerRef></ng-template>
    </div>
  `
})
export class ParentComponent implements myinterface {

    @ViewChild('viewContainerRef', { read: ViewContainerRef }) VCR: ViewContainerRef;

    //manually indexing the child components for better removal
    //although there is by-default indexing but it is being avoid for now
    //so index is a unique property here to identify each component individually.
    index: number = 0;

    // to store references of dynamically created components
    componentsReferences = [];

    constructor(private CFR: ComponentFactoryResolver) {
    }

    createComponent() {

        let componentFactory = this.CFR.resolveComponentFactory(ChildComponent);
        let componentRef: ComponentRef<ChildComponent> = this.VCR.createComponent(componentFactory);
        let currentComponent = componentRef.instance;

        currentComponent.selfRef = currentComponent;
        currentComponent.index = ++this.index;

        // prividing parent Component reference to get access to parent class methods
        currentComponent.compInteraction = this;

        // add reference for newly created component
        this.componentsReferences.push(componentRef);
    }

    remove(index: number) {

        if (this.VCR.length < 1)
            return;

        let componentRef = this.componentsReferences.filter(x => x.instance.index == index)[0];
        let component: ChildComponent = <ChildComponent>componentRef.instance;

        let vcrIndex: number = this.VCR.indexOf(componentRef)

        // removing component from container
        this.VCR.remove(vcrIndex);

        this.componentsReferences = this.componentsReferences.filter(x => x.instance.index !== index);
    }
}
Run Code Online (Sandbox Code Playgroud)

子组件

@Component({
    selector: 'child',
    template: `
    <div>
    <h1 (click)="removeMe(index)">I am a Child, click to Remove</h1>
    </div>
    `
})
export class ChildComponent {

    public index: number;
    public selfRef: ChildComponent;

    //interface for Parent-Child interaction
    public compInteraction: myinterface;

    constructor() {
    }

    removeMe(index) {
        this.compInteraction.remove(index)
    }
}

// Interface
export interface myinterface {
    remove(index: number);
}
Run Code Online (Sandbox Code Playgroud)

添加对app.module.ts的引用

@NgModule({
  declarations: [

    ParentComponent,
    ChildComponent

  ],
  imports: [

    //if using routing then add like so
    RouterModule.forRoot([
      { path: '', component: ParentComponent }
    ]),

  ],
  entryComponents: [

    ChildComponent,  

  ],
Run Code Online (Sandbox Code Playgroud)

  • 我让它工作:https://stackblitz.com/edit/angular-ivy-k6nqph。我在删除方法中更改了 let vcrIndex: number = this.VCR.indexOf(componentRef) 与 let vcrIndex: number = this.VCR.indexOf(componentRef.hostView); (5认同)
  • 你可以用`.find(...)`替换`.filter(x =&gt; x.instance.index == index)[0]` (2认同)