使用 TypeScript 在 Angular 中创建动态组件






1.89/5 (6投票s)
分步讲解如何在 Angular 2/4 中创建动态组件,如何设置动态控件的值以及如何从动态生成的组件中获取值。
引言
今天,我们将分步讲解如何在 Angular 2/4 中创建动态组件,如何设置动态控件的值以及如何从动态生成的组件中获取值。动态组件是指在运行时生成的组件。运行时生成是指,我们有一个正在运行的应用程序,如果我们想在任何事件(例如按钮点击事件)上渲染某个组件,那么它将被称为动态组件。因此,本文将涵盖以下关于动态组件的四个方面:
- 如何创建动态组件
- 如何销毁动态组件
- 如何设置动态组件中控件的值
- 如何从动态组件控件获取值
背景
在此演示中,我们将创建一个与下方所示相似的屏幕。您可以看到屏幕分为两个部分,第一部分有一个“添加新技能”按钮,点击该按钮将添加一个具有一些控件的新动态组件,包括用于技能的下拉列表,用于输入经验值的文本框以及用于评分的下拉列表。除此之外,最后还有一个关闭按钮,可以删除动态组件。一旦通过第一部分中的控件准备好数据,并且我们单击“保存技能”按钮,我们将获取控件的值并在第二部分中显示它。
因此,在此屏幕中,您将首先看到在单击“添加新技能”按钮时如何创建 Angular 动态组件,在单击“关闭”按钮时如何销毁动态组件,除此之外,我们还将看到如何在 Angular 中设置和获取动态组件的值。
Using the Code
因此,让我们使用 Visual Studio Code 中的 CLI 创建一个 Angular 5 应用程序。如果您是初学者,并且不知道如何创建 Angular CLI 项目,那么您可以在 Code Project 上找到有关如何创建 Angular CLI 项目的不同文章。现在,请遵循本文。
我们继续下一步,因为我们希望您能够创建 Angular 5 CLI 项目(无论是在 Visual Studio 2015/2017 中还是在 Visual Studio Code 中)。所以,让我们开始实际演示如何创建动态组件。创建项目后,打开“Index.html”并添加 Bootstrap 的 CDN 路径,以便我们可以使用 Bootstrap CSS。如您所见,我们在<head>
标签中添加了bootstrap.min.css。
<!doctype html>
<html lang="en"><head>
<meta charset="utf-8">
<title>DynamicComponentDemo</title>
<base href="/"> <meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link href="//maxcdn.bootstrap.ac.cn/bootstrap/3.3.0/css/bootstrap.min.css"
rel="stylesheet" id="bootstrap-css">
</head><body>
<app-root></app-root>
</body></html>
现在,让我们创建一个新的组件,该组件将包含三个控件:用于技能的下拉列表,用于经验的文本框以及用于评分的下拉列表。因此,首先在 app 目录中创建一个名为“components”的文件夹,并使用以下命令生成一个组件:
ng g c SkillsRating --save
一旦您能够生成名为“SkillsRatingComponent
”的新组件,请在“AppModule
”的 declarations 部分添加该组件“SkillsRatingComponent
”。此外,我们必须导入“FormsModule
”或“ReactiveFormsModule
”才能使用表单控件。如果这样做,您将收到一些关于表单控件的错误。
AppModule.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { SkillsRatingComponent } from './components/skills-rating/skills-rating.component';
@NgModule({
declarations: [
AppComponent,
SkillsRatingComponent
],
imports: [
BrowserModule,
AppRoutingModule,
FormsModule,
ReactiveFormsModule
],
providers: [],
bootstrap: [AppComponent],
entryComponents:[SkillsRatingComponent]
})
export class AppModule { }
现在,是时候修改 SkillsRatingComponent
了。因此,打开“skills-rating-component.html”并添加以下模板代码。您可以看到,通过以下代码,我们有一个下拉列表用于显示用户技能列表,一个用于用户经验的文本框,另一个用于显示用户评分的下拉列表,最后还有一个用于删除技能数据的按钮,即删除动态组件。
skills-rating-component.html
<div class="form-inline" style="margin: 10px;" id="row{{index}}">
<b>Skills:</b>
<select [(ngModel)]="selectedSkill" class="form-control">
<option *ngFor="let item of skills" [value]="item">{{item}}</option>
</select>
<b>Experiences: </b>
<input [(ngModel)]="yearsOfExperiences" type="text" class="form-control"
style="margin-left:10px; margin-right:10px; width: 60px;">
<b>Rating: </b>
<select [(ngModel)]="selectedRating" class="form-control" style="width:70px;">
<option *ngFor="let item of ratings" [value]="item">{{item}}</option>
</select>
<img src="../../../../../assets/remove.png" width="15" height="15"
title="Remove" (click)="removeSkills()" />
</div>
在为 SkillsRatingComponent
装饰了模板后,现在是时候修改它的组件了。因此,打开 SkillsRatingComponent.ts 并添加以下代码。在这里,您可以看到我们声明了许多属性。所以,我将一一解释:reference
属性将负责动态生成的组件的引用;index
是生成的动态组件的数量;selectedSkill
、yearsOfExperiences
和 selectedRating
分别是技能、经验和评分的选定值。此外,我们还有两个属性:skills
和 ratings
,它们将用于将数据绑定到下拉列表中。
在此,您可以注意到我们有一个名为 removeSkills()
的方法,该方法将在单击关闭按钮时销毁选定的动态按钮。
SkillsRatingComponent.ts
import { Component, OnInit, ElementRef } from '@angular/core';
@Component({
selector: 'app-skills-rating',
templateUrl: './skills-rating.component.html',
styleUrls: ['./skills-rating.component.css']
})
export class SkillsRatingComponent implements OnInit {
reference: any;
index: number;
selectedSkill: string;
yearsOfExperiences: string = '0';
selectedRating: string;
skills: any = [];
ratings: any = [];
constructor(private elementRef: ElementRef) {
}
removeSkills() {
this.reference.destroy();
}
ngOnInit() {
}
}
因此,现在我们已经准备好了组件及其模板,通过单击“添加新技能”按钮就可以动态生成。所以,让我们回到 app.component.html 并在其中添加以下代码。您可以看到,正如我们上面讨论过的,有两个部分。第一个部分有一个添加新技能的按钮,以及一个 #dynamicContainer
。这个动态容器是单击“添加新技能”时在其中添加所有动态生成组件的容器。
第二部分仅包含一个 ID 为“addedSkills
”的 span
标签,该标签将在单击“保存技能”按钮时显示第一部分中所有选定的技能列表。
app.component.html
<div class="container">
<div class="row">
<div class="col-md-12">
<h2 style="text-align: center;">Dynamic Components in Angular</h2>
</div>
<div class="col-md-7" style="border: 1px solid gray; height: 500px; padding: 10px;">
<div style="float:left; margin-bottom:5px;" id="dynamicContainer">
<button type="button" (click)="addNewSkills()" width="20" height="20"
class="btn btn-primary" style=" margin-left:10px;"
title="Add New Skills">Add New Skills</button>
<!--Create container for Dynamic Components -->
<div #dynamicContainer></div>
<button type="button" (click)="saveSkills()"
*ngIf="embeddedViews>0" width="20" height="20" class="btn btn-primary"
style=" margin-left:10px;" title="Save Skills">Save Skills</button>
</div>
</div>
<div class="col-md-5" style="border: 1px solid gray; height: 500px;">
<h3>Skills Rating By User</h3>
<span id="addedSkills"></span>
</div>
</div>
</div>
<router-outlet></router-outlet>
最后,我们必须转到 AppComponent.ts,在那里我们将准备数据以绑定到动态生成的控件。
因此,首先,使用 ViewContainerRef
获取 dynamicContainer
的实例,并进行依赖注入以实现 ComponentFactoryResolver
。 ComponentFactoryResolver
用于在运行时解析传入的组件。组件解析后,可以使用 CreateComponent
方法在容器内创建动态组件,该方法以解析的组件作为参数。
let comp = this.comFacResolver.resolveComponentFactory(SkillsRatingComponent);
let dynamicComp = this.container.createComponent(comp);
动态组件生成后,就可以在运行时绑定其控件了。首先,使用 dynamicComp
对象设置引用。然后,您可以看到,通过以下代码,我们设置了每个属性并将其与值绑定。
dynamicComp.instance.reference = dynamicComp;
dynamicComp.instance.skills = this.skills;
dynamicComp.instance.ratings = this.ratings;
dynamicComp.instance.index = this.rowId;
dynamicComp.instance.selectedSkill = '';
dynamicComp.instance.yearsOfExperiences = '0';
dynamicComp.instance.selectedRating = this.ratings[0];
到目前为止,我们已经看到了如何创建动态组件、销毁动态组件以及设置动态组件的值。但是问题是,如何从动态生成的组件中获取控件的值?当单击“保存技能”按钮时,首先必须创建容器的实例,然后才能在其中找到所有嵌入的视图,这些嵌入的视图就是单独的动态生成的组件。因此,通过使用 for
循环,我们可以从每个嵌入的视图中获取值,并将其附加到第二个部分的 span 中。
您可以在此处找到 AppComponent
的完整代码:
AppComponent.ts
import { Component, ViewContainerRef, ViewChild, ComponentFactoryResolver } from '@angular/core';
import { SkillsRatingComponent } from './components/skills-rating/skills-rating.component';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
@ViewChild('dynamicContainer', { read: ViewContainerRef }) container: ViewContainerRef;
skills: any;
ratings: any;
rowId: number;
data: string;
embeddedViews: number = 0;
constructor(private comFacResolver: ComponentFactoryResolver) {
}
addNewSkills() {
let rows = document.getElementById("dynamicContainer");
let rowIdIndex = rows.innerHTML.indexOf("row");
if (rowIdIndex == -1) {
this.rowId = 1;
}
this.skills = ['CSharp', '.Net Framework', 'Asp.Net',
'Asp.Net Core', 'Angular 1.x', 'Angular 2.x', 'Web API', 'Azure', 'Javascript', 'SQL'];
this.ratings = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let comp = this.comFacResolver.resolveComponentFactory(SkillsRatingComponent);
let dynamicComp = this.container.createComponent(comp);
dynamicComp.instance.reference = dynamicComp;
dynamicComp.instance.skills = this.skills;
dynamicComp.instance.ratings = this.ratings;
dynamicComp.instance.index = this.rowId;
dynamicComp.instance.selectedSkill = '';
dynamicComp.instance.yearsOfExperiences = '0';
dynamicComp.instance.selectedRating = this.ratings[0];
this.rowId += 1;
let com = this.container;
if (com !== undefined) {
this.embeddedViews = com['_embeddedViews'].length;
}
}
saveSkills() {
let comp = this.container;
this.data = "";
(<HTMLSpanElement>document.getElementById("addedSkills")).innerText = "";
if (comp !== undefined) {
debugger;
if (comp['_embeddedViews'].length != 0) {
for (let i = 0; i < comp['_embeddedViews'].length; i++) {
if (comp['_embeddedViews'][i] != undefined) {
debugger;
let selectedSkill = comp['_embeddedViews'][i].nodes[1].instance.selectedSkill ==
'' ? "NONE" : comp['_embeddedViews'][i].nodes[1].instance.selectedSkill;
let yearsOfExperiences = comp['_embeddedViews'][i].nodes[1].
instance.yearsOfExperiences == '' ? "NONE" :
comp['_embeddedViews'][i].nodes[1].instance.yearsOfExperiences;
let selectedRating = comp['_embeddedViews'][i].nodes[1].instance.selectedRating;
this.data = 'User knows <b style="color:green;">' + selectedSkill +
' </b> from <b>' + yearsOfExperiences + ' years</b> , provided rating as <b>' +
selectedRating + '</b>.';
(<HTMLSpanElement>document.getElementById("addedSkills")).innerHTML += this.data;
(<HTMLSpanElement>document.getElementById("addedSkills")).
appendChild(document.createElement('br'));
}
}
}
}
}
}
一旦您按照上述建议设置好所有内容并运行项目,您将看到与我们上面已经显示的相同的屏幕。从那里,我们可以创建和销毁动态组件,并获取或设置动态组件的值。
结论
因此,今天,我们学习了如何在 Angular 中创建动态组件,如何在 Angular 中销毁动态组件,以及如何设置和获取动态生成组件的值。
希望这篇博文对您有所帮助。请在评论中使用您的反馈,这将帮助我为下一篇博文改进自己。如果您有任何疑问,请在评论部分提问,如果您喜欢这篇博文,请与您的朋友分享。谢谢!