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 提供了许多内置指令,我们将在后面的章节中学习。

基于组件的指令

组件可以用作指令。 每个组件都有 InputOutput 选项在组件与其父 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、NgForNgSwitch。 我们简单一一了解一下。

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

现在,运行您的应用程序,您可以看到以下响应 −

NgServe

如果设置条件ngIf="false",则内容将被隐藏。

ngIfElse指令

ngIfElsengIf 类似,但它也提供了在失败情况下呈现内容的选项。

让我们通过示例来了解 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

现在,运行您的应用程序,您可以看到以下响应 −

NgApplication

这里,

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

现在,运行您的应用程序,您可以看到以下响应 −

Ngdirective

trackBy

有时,对于大型列表,ngFor 性能较低。 例如,当添加新项目或删除列表中的任何项目时可能会触发多个 DOM 操作。 为了迭代大型对象集合,我们使用 trackBy

它用于跟踪元素何时添加或删除。 它是通过 trackBy 方法执行的。 它有两个参数索引和元素。 索引用于唯一地标识每个元素。 下面定义了简单的示例。

让我们通过一个示例来了解 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 而不是对象引用来跟踪 student 学生对象。 因此,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

现在,运行您的应用程序,您可以看到以下响应 −

NgSwitch

在这里,我们将 logInName 定义为 admin。 因此,它匹配第二个 SwitchCase 并打印上面的管理相关消息。

属性指令

属性指令执行 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

现在,运行您的应用程序,您可以看到以下响应 −

ngStyle

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 类中创建了两个属性 userIduserName

打开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>

这里,

我们已经为User1应用了ngClass,因此它将突出显示User1

最后,使用以下命令启动您的应用程序(如果尚未完成) −

ng serve

现在,运行您的应用程序,您可以看到以下响应 −

ngClass

自定义指令

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'; 
   } 
}

这里,构造函数方法使用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、ngForngSwitch 指令内使用以呈现结果。

让我们假设简单的代码。

<ng-template [ngIf]=true> 
   <div><h2>ng-template works!</h2></div> 
</ng-template>

这里,如果ngIf条件成立,它将打印div元素内的数据。 同样,您也可以使用 ngForngSwitch 指令。

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>

在这里,我们在 TestComponent 中使用 AppComponent 作为具有输入属性的指令。

最后,使用以下命令启动您的应用程序(如果尚未完成) −

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。 tabletable-striped 将根据 Boostrap 样式标准设置表格样式。

  • 使用ngFor循环expenseEntries并生成表行。

打开AppComponent模板,src/app/app.component.html并包含ExpenseEntryListComponent并删除ExpenseEntryComponent,如下所示 −

... 
<app-expense-entry-list></app-expense-entry-list>

最后,应用程序的输出如下所示。

AppComponent