[英]Dynamically ADDING and REMOVING Components in Angular
The current official docs only shows how to dynamically change components within an <ng-template>
tag.当前的官方文档仅展示了如何动态更改
<ng-template>
标签内的组件。 https://angular.io/guide/dynamic-component-loader https://angular.io/guide/dynamic-component-loader
What I want to achieve is, let's say I have 3 components: header
, section
, and footer
with the following selectors:我想要实现的是,假设我有 3 个组件:
header
、 section
和footer
以及以下选择器:
<app-header>
<app-section>
<app-footer>
And then there are 6 buttons that will add or remove each component: Add Header
, Add Section
, and Add Footer
然后有 6 个按钮可以添加或删除每个组件:
Add Header
、 Add Section
和Add Footer
and when I click Add Header
, the page will add <app-header>
to the page that renders it, so the page will contain:当我单击
Add Header
,页面会将<app-header>
添加到呈现它的页面,因此该页面将包含:
<app-header>
And then if I click Add Section
twice, the page will now contain:然后,如果我单击“
Add Section
两次,该页面现在将包含:
<app-header>
<app-section>
<app-section>
And if I click Add Footer
, the page will now contain all these components:如果我单击
Add Footer
,页面现在将包含所有这些组件:
<app-header>
<app-section>
<app-section>
<app-footer>
Is it possible to achieve this in Angular?是否有可能在 Angular 中实现这一点? Note that
ngFor
is not the solution I'm looking for, as it only allows to add the same components, not different components to a page.请注意,
ngFor
不是我正在寻找的解决方案,因为它只允许向页面添加相同的组件,而不是不同的组件。
EDIT: ngIf
and ngFor
is not the solution I'm looking for as the templates are already predetermined.编辑:
ngIf
和ngFor
不是我正在寻找的解决方案,因为模板已经预先确定。 What I am looking for is something like a stack of component
s or an array
of component
s where we can add, remove, and change any index of the array
easily.我所寻找的是类似的堆栈
component
S或一个array
的component
S其中,我们可以添加,删除和更改的任何索引array
容易。
EDIT 2: To make it more clear, let's have another example of why ngFor
does not work.编辑 2:为了更清楚,让我们举另一个例子来说明为什么
ngFor
不起作用。 Let's say we have the following components:假设我们有以下组件:
<app-header>
<app-introduction>
<app-camera>
<app-editor>
<app-footer>
Now here comes a new component, <app-description>
, which the user wants to insert in between and <app-editor>
.现在出现了一个新组件
<app-description>
,用户希望将其插入到<app-editor>
。 ngFor
works only if there is one same component that I want to loop over and over. ngFor
仅在我想一遍又一遍地循环一个相同的组件ngFor
起作用。 But for different components, ngFor
fails here.但是对于不同的组件,
ngFor
在这里失败了。
What you're trying to achieve can be done by creating components dynamically using the ComponentFactoryResolver
and then injecting them into a ViewContainerRef
.您可以通过使用
ComponentFactoryResolver
动态创建组件然后将它们注入ViewContainerRef
来实现您想要实现的目标。 One way to do this dynamically is by passing the class of the component as an argument of your function that will create and inject the component.动态执行此操作的一种方法是将组件的类作为将创建和注入组件的函数的参数传递。
See example below:请参阅下面的示例:
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);
}
}
}
Notes:笔记:
If you want to make it easier to remove the components later on, you can keep track of them in a local variable, see this.components
.如果您希望稍后更轻松地删除组件,您可以在局部变量中跟踪它们,请参阅
this.components
。 Alternatively you can loop over all the elements inside the ViewContainerRef
.或者,您可以遍历
ViewContainerRef
内的所有元素。
You have to register your component as an entry component.您必须将您的组件注册为入口组件。 In your module definition register your component as an entryComponent (
entryComponents: [DraggableComponent]
).在您的模块定义中,将您的组件注册为 entryComponent (
entryComponents: [DraggableComponent]
)。
Running example: https://plnkr.co/edit/mrXtE1ICw5yeIUke7wl5运行示例: https : //plnkr.co/edit/mrXtE1ICw5yeIUke7wl5
For more information: https://angular.io/guide/dynamic-component-loader更多信息: https : //angular.io/guide/dynamic-component-loader
I have created a demo to show the dynamic add and remove process.我创建了一个演示来展示动态添加和删除过程。 Parent component creates the child components dynamically and removes them.
父组件动态创建子组件并删除它们。
Parent Component父组件
// .ts
export class ParentComponent {
@ViewChild("viewContainerRef", { read: ViewContainerRef })
VCR: ViewContainerRef;
child_unique_key: number = 0;
componentsReferences = Array<ComponentRef<ChildComponent>>()
constructor(private CFR: ComponentFactoryResolver) {}
createComponent() {
let componentFactory = this.CFR.resolveComponentFactory(ChildComponent);
let childComponentRef = this.VCR.createComponent(componentFactory);
let childComponent = childComponentRef.instance;
childComponent.unique_key = ++this.child_unique_key;
childComponent.parentRef = this;
// add reference for newly created component
this.componentsReferences.push(childComponentRef);
}
remove(key: number) {
if (this.VCR.length < 1) return;
let componentRef = this.componentsReferences.filter(
x => x.instance.unique_key == key
)[0];
let vcrIndex: number = this.VCR.indexOf(componentRef as any);
// removing component from container
this.VCR.remove(vcrIndex);
// removing component from the list
this.componentsReferences = this.componentsReferences.filter(
x => x.instance.unique_key !== key
);
}
}
// .html
<button type="button" (click)="createComponent()">
I am Parent, Create Child
</button>
<div>
<ng-template #viewContainerRef></ng-template>
</div>
Child Component子组件
// .ts
export class ChildComponent {
public unique_key: number;
public parentRef: ParentComponent;
constructor() {
}
remove_me() {
console.log(this.unique_key)
this.parentRef.remove(this.unique_key)
}
}
// .html
<button (click)="remove_me()">I am a Child {{unique_key}}, click to Remove</button>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.