- Angular 8 教程
- Angular 8 - 首页
- Angular 8 - 简介
- Angular 8 - 安装
- 创建第一个应用程序
- Angular 8 - 架构
- Angular 组件和模板
- Angular 8 - 数据绑定
- Angular 8 - 指令
- Angular 8 - 管道
- Angular 8 - 响应式编程
- 服务和依赖注入
- Angular 8 - Http 客户端编程
- Angular 8 - Angular Material
- 路由和导航
- Angular 8 - 动画
- Angular 8 - 表单
- Angular 8 - 表单验证
- 身份验证和授权
- Angular 8 - Web Workers
- Service Workers 和 PWA
- Angular 8 - 服务器端渲染
- Angular 8 - 国际化 (i18n)
- Angular 8 - 可访问性
- Angular 8 - CLI 命令
- Angular 8 - 测试
- Angular 8 - Ivy 编译器
- Angular 8 - 使用 Bazel 构建
- Angular 8 - 向后兼容性
- Angular 8 - 工作示例
- Angular 9 - 新特性?
- Angular 8 有用资源
- Angular 8 - 快速指南
- Angular 8 - 有用资源
- Angular 8 - 讨论
Angular 8 - 指令
Angular 8 指令是与应用程序交互的 DOM 元素。通常,指令是一个TypeScript函数。当此函数执行时,Angular编译器会在 DOM 元素内检查它。Angular 指令以ng-开头,其中ng代表 Angular,并使用@directive装饰器扩展 HTML 标签。
指令允许将逻辑包含在 Angular 模板中。Angular 指令可以分为三类,如下所示:
属性指令
用于为现有 HTML 元素添加新属性,以更改其外观和行为。
<HTMLTag [attrDirective]='value' />
例如:
<p [showToolTip]='Tips' />
这里,showToolTip 指的是一个示例指令,当它用于 HTML 元素时,会在用户悬停 HTML 元素时显示提示。
结构指令
用于在当前 HTML 文档中添加或删除 DOM 元素。
<HTMLTag [structuralDirective]='value' />
例如:
<div *ngIf="isNeeded"> Only render if the *isNeeded* value has true value. </div>
这里,ngIf 是一个内置指令,用于在当前 HTML 文档中添加或删除 HTML 元素。Angular 提供了许多内置指令,我们将在后面的章节中学习。
基于组件的指令
组件可以用作指令。每个组件都有Input和Output选项,用于在组件及其父 HTML 元素之间传递数据。
<component-selector-name [input-reference]="input-value"> ... </component-selector-name>
例如:
<list-item [items]="fruits"> ... </list-item>
这里,list-item 是一个组件,items 是输入选项。我们将在后面的章节中学习如何创建组件和高级用法。
在进入本主题之前,让我们在 Angular 8 中创建一个示例应用程序(directive-app)来巩固所学知识。
打开命令提示符,使用以下命令创建一个新的 Angular 应用程序:
cd /go/to/workspace ng new directive-app cd directive-app
使用 Angular CLI 创建一个test组件,如下所示:
ng generate component test
以上命令创建一个新的组件,输出如下:
CREATE src/app/test/test.component.scss (0 bytes) CREATE src/app/test/test.component.html (19 bytes) CREATE src/app/test/test.component.spec.ts (614 bytes) CREATE src/app/test/test.component.ts (262 bytes) UPDATE src/app/app.module.ts (545 bytes)
使用以下命令运行应用程序:
ng serve
DOM 概述
让我们简要了解一下 DOM 模型。DOM 用于定义访问文档的标准。通常,HTML DOM 模型被构建为对象的树。它是一个访问 html 元素的标准对象模型。
我们可以在 Angular 8 中出于以下原因使用 DOM 模型:
- 我们可以轻松地使用 DOM 元素导航文档结构。
- 我们可以轻松地添加 html 元素。
- 我们可以轻松地更新元素及其内容。
结构指令
结构指令通过添加或删除元素来更改DOM的结构。它用 * 符号表示,并具有三个预定义指令NgIf、NgFor和NgSwitch。让我们逐一简要了解一下。
NgIf 指令
NgIf 指令用于根据条件为真或假来显示或隐藏应用程序中的数据。我们可以将其添加到模板中的任何标签。
让我们在我们的directive-app应用程序中尝试ngIf指令。
在test.component.html中添加以下标签。
<p>test works!</p> <div *ngIf="true">Display data</div>
在你的app.component.html文件中添加测试组件,如下所示:
<app-test></app-test>
使用以下命令启动你的服务器(如果尚未启动):
ng serve
现在,运行你的应用程序,你将看到以下响应:
如果将条件设置为ngIf=“false”,则内容将被隐藏。
ngIfElse 指令
ngIfElse类似于ngIf,不同之处在于它还提供了在失败情况下渲染内容的选项。
让我们通过一个示例了解ngIfElse的工作原理。
在test.component.ts文件中添加以下代码。
export class TestComponent implements OnInit { isLogIn : boolean = false; isLogOut : boolean = true; }
在test.component.html文件中添加以下代码:
<p>ngIfElse example!</p> <div *ngIf="isLogIn; else isLogOut"> Hello you are logged in </div> <ng-template #isLogOut> You're logged out.. </ng-template>
最后,使用以下命令启动你的应用程序(如果尚未启动):
ng serve
现在,运行你的应用程序,你将看到以下响应:
这里:
isLogOut
值为true,因此它进入else块并渲染ng-template。我们将在本章后面学习ng-template。ngFor 指令
ngFor 用于重复项目列表中的部分元素。
让我们通过一个示例了解 ngFor 的工作原理。
在 test.component.ts 文件中添加如下所示的列表:
list = [1,2,3,4,5];
在test.component.html中添加ngFor指令,如下所示:
<h2>ngFor directive</h2> <ul> <li *ngFor="let l of list"> {{l}} </li> </ul>
这里,let 关键字创建一个局部变量,它可以在模板中的任何位置引用。let l 创建一个模板局部变量来获取列表元素。
最后,使用以下命令启动你的应用程序(如果尚未启动):
ng serve
现在,运行你的应用程序,你将看到以下响应:
trackBy
有时,对于大型列表,ngFor的性能较低。例如,当在列表中添加新项目或删除任何项目时,可能会触发多个 DOM 操作。要迭代大型对象集合,我们使用trackBy。
它用于跟踪何时添加或删除元素。它由 trackBy 方法执行。它有两个参数:index 和 element。Index 用于唯一标识每个元素。简单的示例定义如下。
让我们通过一个示例了解 trackBy 与ngFor一起工作的方式。
在test.component.ts文件中添加以下代码。
export class TestComponent { studentArr: any[] = [ { "id": 1, "name": "student1" }, { "id": 2, "name": "student2" }, { "id": 3, "name": "student3" }, { "id": 4, "name": "student4" } ]; trackByData(index:number, studentArr:any): number { return studentArr.id; }
这里:
我们创建了:
trackByData()
方法以基于 id 的唯一方式访问每个学生元素。在test.component.html文件中添加以下代码,以便在 ngFor 中定义 trackBy 方法。
<ul> <li *ngFor="let std of studentArr; trackBy: trackByData"> {{std.name}} </li> </ul>
最后,使用以下命令启动你的应用程序(如果尚未启动):
ng serve
现在,运行你的应用程序,你将看到以下响应:
这里,应用程序将打印学生姓名。现在,应用程序使用学生 ID 而不是对象引用来跟踪学生对象。因此,DOM 元素不会受到影响。
NgSwitch 指令
NgSWitch用于检查多个条件,并使 DOM 结构保持简单易懂。
让我们在我们的directive-app应用程序中尝试ngSwitch指令。
在test.component.ts文件中添加以下代码。
export class TestComponent implements OnInit { logInName = 'admin'; }
在 test.component.html 文件中添加以下代码:
<h2>ngSwitch directive</h2> <ul [ngSwitch]="logInName"> <li *ngSwitchCase="'user'"> <p>User is logged in..</p> </li> <li *ngSwitchCase="'admin'"> <p>admin is logged in</p> </li> <li *ngSwitchDefault> <p>Please choose login name</p> </li> </ul>
最后,使用以下命令启动你的应用程序(如果尚未启动):
ng serve
现在,运行你的应用程序,你将看到以下响应:
这里,我们定义了logInName为admin。因此,它匹配第二个 SwitchCase 并打印上面与 admin 相关的消息。
属性指令
属性指令执行 DOM 元素或组件的外观或行为。一些示例包括 NgStyle、NgClass 和 NgModel。而 NgModel 是在上一章中解释的双向属性数据绑定。
ngStyle
ngStyle指令用于添加动态样式。以下示例用于将蓝色应用于段落。
让我们在我们的directive-app应用程序中尝试ngStyle指令。
在test.component.html文件中添加以下内容。
<p [ngStyle]="{'color': 'blue', 'font-size': '14px'}"> paragraph style is applied using ngStyle </p>
使用以下命令启动你的应用程序(如果尚未启动):
ng serve
现在,运行你的应用程序,你将看到以下响应:
ngClass
ngClass用于在 HTML 元素中添加或删除 CSS 类。
让我们在我们的directive-app应用程序中尝试ngClass指令。
使用以下命令创建一个类User
ng g class User
你将看到以下响应:
CREATE src/app/user.spec.ts (146 bytes) CREATE src/app/user.ts (22 bytes)
转到src/app/user.ts文件并添加以下代码:
export class User { userId : number; userName : string; }
这里,我们在User类中创建了两个属性userId和userName。
打开test.component.ts文件并添加以下更改:
import { User } from '../user'; export class TestComponent implements OnInit { users: User[] = [ { "userId": 1, "userName": 'User1' }, { "userId": 2, "userName": 'User2' }, ]; }
这里,我们声明了一个局部变量 users 并用 2 个用户对象初始化它。
打开test.component.css文件并添加以下代码
.highlight { color: red; }
打开你的test.component.html文件并添加以下代码:
<div class="container"> <br/> <div *ngFor="let user of users" [ngClass]="{ 'highlight':user.userName === 'User1' }"> {{ user.userName }} </div> </div>
这里:
我们应用了ngClass到User1,因此它将突出显示User1。
最后,使用以下命令启动你的应用程序(如果尚未启动):
ng serve
现在,运行你的应用程序,你将看到以下响应:
自定义指令
Angular 提供了使用用户定义的指令扩展 angular 指令的选项,这称为自定义指令。让我们在本节中学习如何创建自定义指令。
让我们尝试在我们的directive-app应用程序中创建自定义指令。
Angular CLI 提供以下命令来创建自定义指令。
ng generate directive customstyle
执行此命令后,你将看到以下响应:
CREATE src/app/customstyle.directive.spec.ts (244 bytes) CREATE src/app/customstyle.directive.ts (151 bytes) UPDATE src/app/app.module.ts (1115 bytes)
打开app.module.ts。指令将通过declarations元数据在AppModule中配置。
import { CustomstyleDirective } from './customstyle.directive'; @NgModule({ declarations: [ AppComponent, TestComponent, CustomstyleDirective ] })
打开customstyle.directive.ts文件并添加以下代码:
import { Directive, ElementRef } from '@angular/core'; @Directive({ selector: '[appCustomstyle]' }) export class CustomstyleDirective { constructor(el: ElementRef) { el.nativeElement.style.fontSize = '24px'; } }
这里,constructor方法使用CustomStyleDirective获取元素作为el。然后,它访问 el 的样式并使用 CSS 属性将其字体大小设置为24px。
最后,使用以下命令启动你的应用程序(如果尚未启动):
ng serve
现在,运行你的应用程序,你将看到以下响应:
ng-template
ng-template用于创建动态且可重用的模板。它是一个虚拟元素。如果你使用ng-template编译代码,则它会在 DOM 中转换为注释。
例如:
让我们在test.component.html页面中添加以下代码。
<h3>ng-template</h3> <ng-template>ng-template tag is a virtual element</ng-template>
如果你运行应用程序,它将只打印h3元素。检查你的页面源代码,模板显示在注释部分,因为它是一个虚拟元素,所以它不会呈现任何内容。我们需要将ng-template与 Angular 指令一起使用。
通常,指令会发出与其关联的 HTML 标签。有时,我们不需要标签,只需要内容。例如,在下面的示例中,将发出 li。
<li *ngFor="let item in list">{{ item }}</li>
我们可以使用ng-template安全地跳过li标签。
ng-template 与结构指令
ng-template应始终用于ngIf、ngFor或ngSwitch指令内部以呈现结果。
让我们假设一个简单的代码。
<ng-template [ngIf]=true> <div><h2>ng-template works!</h2></div> </ng-template>
这里,如果ngIf条件为真,它将打印 div 元素内的信息。类似地,你也可以使用ngFor和ngSwitch指令。
NgForOf 指令
ngForOf 也是一个结构指令,用于渲染集合中的项目。下面的示例用于演示ng-template 内部的 ngForOf 指令。
import { Component, OnInit } from '@angular/core'; @Component({ selector: 'app-test', template: ` <div> <ng-template ngFor let-item [ngForOf]="Fruits" let-i="index"> <p>{{i}}</p> </ng-template> </div>` , styleUrls: ['./test.component.css'] }) export class TestComponent implements OnInit { Fruits = ["mango","apple","orange","grapes"]; ngOnInit() { } }
如果运行应用程序,它将显示每个元素的索引,如下所示:
0 1 2 3
组件指令
组件指令基于组件。实际上,每个组件都可以用作指令。组件提供 @Input 和 @Output 装饰器,用于在父组件和子组件之间发送和接收信息。
让我们尝试在我们的 directive-app 应用程序中使用组件作为指令。
使用以下命令创建一个新的 ChildComponent:
ng generate component child CREATE src/app/child/child.component.html (20 bytes) CREATE src/app/child/child.component.spec.ts (621 bytes) CREATE src/app/child/child.component.ts (265 bytes) CREATE src/app/child/child.component.css (0 bytes) UPDATE src/app/app.module.ts (466 bytes)
打开 child.component.ts 并添加以下代码:
@Input() userName: string;
在这里,我们为 ChildComponent 设置一个输入属性。
打开 child.component.html 并添加以下代码:
<p>child works!</p> <p>Hi {{ userName }}</p>
在这里,我们使用 userName 值来欢迎用户。
打开 test.component.ts 并添加以下代码:
name: string = 'Peter';
打开 test.component.html 并添加以下代码:
<h1>Test component</h1> <app-child [userName]="name"><app-child>
在这里,我们使用 AppComponent 作为带有输入属性的指令在 TestComponent 内。
最后,使用以下命令启动你的应用程序(如果尚未启动):
ng serve
现在,运行你的应用程序,你将看到以下响应:
[](images/directive-app/component_as_directive.PNG)
工作示例
让我们在我们的 ExpenseManager 应用程序中添加一个新组件来列出支出条目。
打开命令提示符并转到项目根文件夹。
cd /go/to/expense-manager
启动应用程序。
ng serve
使用以下命令创建一个新的组件 ExpenseEntryListComponent:
ng generate component ExpenseEntryList
输出
输出如下:
CREATE src/app/expense-entry-list/expense-entry-list.component.html (33 bytes) CREATE src/app/expense-entry-list/expense-entry-list.component.spec.ts (700 bytes) CREATE src/app/expense-entry-list/expense-entry-list.component.ts (315 bytes) CREATE src/app/expense-entry-list/expense-entry-list.component.css (0 bytes) UPDATE src/app/app.module.ts (548 bytes)
在这里,该命令创建了 ExpenseEntryList 组件并在 AppModule 中更新了必要的代码。
将 ExpenseEntry 导入到 ExpenseEntryListComponent 组件中 (src/app/expense-entry-list/expense-entry-list.component)
import { ExpenseEntry } from '../expense-entry';
添加一个方法 getExpenseEntries() 以在 ExpenseEntryListComponent (src/app/expense-entry-list/expense-entry-list.component) 中返回支出条目的列表(模拟项目)。
getExpenseEntries() : ExpenseEntry[] { let mockExpenseEntries : ExpenseEntry[] = [ { id: 1, item: "Pizza", amount: Math.floor((Math.random() * 10) + 1), category: "Food", location: "Mcdonald", spendOn: new Date(2020, 4, Math.floor((Math.random() * 30) + 1), 10, 10, 10), createdOn: new Date(2020, 4, Math.floor((Math.random() * 30) + 1), 10, 10, 10) }, { id: 1, item: "Pizza", amount: Math.floor((Math.random() * 10) + 1), category: "Food", location: "KFC", spendOn: new Date(2020, 4, Math.floor((Math.random() * 30) + 1), 10, 10, 10), createdOn: new Date(2020, 4, Math.floor((Math.random() * 30) + 1), 10, 10, 10) }, { id: 1, item: "Pizza", amount: Math.floor((Math.random() * 10) + 1), category: "Food", location: "Mcdonald", spendOn: new Date(2020, 4, Math.floor((Math.random() * 30) + 1), 10, 10, 10), createdOn: new Date(2020, 4, Math.floor((Math.random() * 30) + 1), 10, 10, 10) }, { id: 1, item: "Pizza", amount: Math.floor((Math.random() * 10) + 1), category: "Food", location: "KFC", spendOn: new Date(2020, 4, Math.floor((Math.random() * 30) + 1), 10, 10, 10), createdOn: new Date(2020, 4, Math.floor((Math.random() * 30) + 1), 10, 10, 10) }, { id: 1, item: "Pizza", amount: Math.floor((Math.random() * 10) + 1), category: "Food", location: "KFC", spendOn: new Date(2020, 4, Math.floor((Math.random() * 30) + 1), 10, 10, 10), createdOn: new Date(2020, 4, Math.floor((Math.random() * 30) + 1), 10, 10, 10) }, ]; return mockExpenseEntries; }
声明一个局部变量 expenseEntries 并加载如下所示的模拟支出条目列表:
title: string; expenseEntries: ExpenseEntry[]; constructor() { } ngOnInit() { this.title = "Expense Entry List"; this.expenseEntries = this.getExpenseEntries(); }
打开模板文件 (src/app/expense-entry-list/expense-entry-list.component.html) 并以表格形式显示模拟条目。
<!-- Page Content --> <div class="container"> <div class="row"> <div class="col-lg-12 text-center" style="padding-top: 20px;"> <div class="container" style="padding-left: 0px; padding-right: 0px;"> <div class="row"> <div class="col-sm" style="text-align: left;"> {{ title }} </div> <div class="col-sm" style="text-align: right;"> <button type="button" class="btn btn-primary">Edit</button> </div> </div> </div> <div class="container box" style="margin-top: 10px;"> <table class="table table-striped"> <thead> <tr> <th>Item</th> <th>Amount</th> <th>Category</th> <th>Location</th> <th>Spent On</th> </tr> </thead> <tbody> <tr *ngFor="let entry of expenseEntries"> <th scope="row">{{ entry.item }}</th> <th>{{ entry.amount }}</th> <td>{{ entry.category }}</td> <td>{{ entry.location }}</td> <td>{{ entry.spendOn | date: 'short' }}</td> </tr> </tbody> </table> </div> </div> </div> </div>
这里:
使用了 Bootstrap 表格。table 和 table-striped 将根据 Bootstrap 样式标准设置表格样式。
使用 ngFor 遍历 expenseEntries 并生成表格行。
打开 AppComponent 模板 src/app/app.component.html 并包含 ExpenseEntryListComponent 并删除 ExpenseEntryComponent,如下所示:
... <app-expense-entry-list></app-expense-entry-list>
最后,应用程序的输出如下所示。