学习 Angular 教程 - 第三部分





5.00/5 (5投票s)
使用 Angular 路由和 Angular 验证实现 SPA。
目录
- 实验 7:使用 Angular 路由实现 SPA
- 实验 8:使用 Angular 表单实现验证
- 良好验证结构的要求
- 在此实验中,我们将实现哪种验证?
- 验证放在哪里?
- 步骤 1:导入 Angular 验证器所需的组件
- 步骤 2:使用 FormBuilder 创建 FormGroup
- 步骤 3:添加简单验证
- 步骤 4:添加复合验证
- 包含验证的完整模型代码
- 步骤 5:在 CustomerModule 中引用“ReactiveFormsModule”。
- 步骤 6:将 formGroup 应用于 HTML 表单
- 步骤 7:将验证应用于 HTML 控件
- 步骤 8:检查验证是否正常
- 步骤 9:检查单个验证
- 步骤 10:独立元素
- 应用了验证的 Customer UI 的完整代码
- 运行并查看您的验证效果
- 脏、原始、已触摸和未触摸
- 下一篇文章有什么内容?
- 历史
其余文章的链接
- 在第一部分中,我们探讨了 Node、TypeScript、模块加载器/打包器和 VS Code。
- 在第二篇文章中,我们创建了一个简单的基础 Angular 应用程序,其中包含一个屏幕,并介绍了一些重要概念,如组件和模块。
- 在第三篇文章中,我们研究了如何实现 SPA 和验证。
- 在第四篇文章中,我们理解了如何进行 HTTP 调用以及如何使用 Input 和 Output 创建自定义 Angular 组件。
- 在第五部分中,我们涵盖了两个实验——一个是如何在 Angular 中使用 Jquery,另一个是延迟路由。
- 在第六部分中,我们再次涵盖了两个实验——管道和使用提供者的依赖注入。
在本文中,我们将了解如何使用 Angular 路由机制创建 SPA。
实验 7:使用 Angular 路由实现 SPA
单页应用程序基础
如今,单页应用程序 (SPA) 已成为创建网站的风格。在 SPA 中,我们仅加载我们需要的内容,而不多加载其他任何内容。
右侧是一个简单的网站,顶部有 Logo 和标题,左侧有菜单链接,底部有页脚。 因此,当用户第一次访问网站时,主页的所有部分都将被加载。但是,当用户单击“供应商”链接时,仅加载“供应商”页面,而不会再次加载 Logo、标题和页脚。当用户单击“客户”链接时,仅加载“客户”页面,而不会加载所有其他部分。 Angular 路由可帮助我们实现相同的目标。 | ![]() |
步骤 1:创建主页
由于一切都围绕主页展开,因此第一个合乎逻辑的步骤是创建“MasterPage
”。
在此主页中,我们将为 Logo、标题、菜单、页脚、版权等创建占位符。这些部分仅在用户首次浏览网站时加载一次。之后,仅按需加载所需的页面。
以下是包含所有占位符部分的代码示例。您也可以在此代码中看到,我们保留了一个“DIV
”标签部分,我们将在此部分按需加载页面。
以下是“MasterPage
”的整体主要部分。请注意“dynamicscreen
”名称的“DIV
”部分,我们打算在此处动态加载屏幕。稍后我们将填充这些部分。
<table border="1" width="448">
<tr>
<td>Logo</td>
<td width="257">Header</td>
</tr>
<tr>
<td>Left Menu</td>
<td width="257">
<div id=”dynamicscreen”>
Dynamic screen will be loaded here
</div>
</td>
</tr>
<tr>
<td>Footer</td>
<td width="257">Copyright</td>
</tr>
</table>
步骤 2:创建供应商页面和欢迎页面
让我们再创建两个 HTML UI,一个“Supplier
”页面和一个“Welcome
”页面。在这两个 HTML 页面中,我们没有做太多事情,只有问候语。
以下是供应商页面的文本。
这是供应商页面
以下是欢迎页面的文本。
欢迎访问本网站
步骤 3:重命名 Index.html 中的占位符
正如在第 1 部分中所解释的,“Index.html”是启动页面,它使用 systemjs 引导所有其他页面。在前一课中,“Index.html”内部加载了“Customer.html”页面。但现在我们有了主页,所以 index 页面内部会加载“MasterPage.html”。
为了使其更有意义,让我们将“customer-ui
”标签重命名为“main-ui
”。在此“main-ui
”部分中,我们将加载主页,当最终用户单击主页左侧菜单链接时,将加载供应商、客户和欢迎页面。
所以,如果观察流程,首先加载 index.html,然后在“ | ![]() |
步骤 4:从 CustomerComponent 中移除选择器
现在,index.html 中首先加载的页面将是 Masterpage
而不是 Customer
页面。因此,我们需要从“CustomerComponent.ts”中移除选择器。此选择器将在后面的部分移至主页组件。
“CustomerComponent.ts”的最终代码如下所示。
import {Component} from "@angular/core"
//importing the customer model
import {Customer} from '../Model/Customer'
@Component({
templateUrl: "../UI/Customer.html"
})
export class CustomerComponent {
//this is binding
CurrentCustomer:Customer = new Customer();
}
步骤 5:为 Master、Supplier 和 Welcome 页面创建组件
每个启用 Angular 的 UI 都应该有一个组件代码文件。我们已经创建了三个用户界面,因此我们需要三个组件代码文件。 在组件文件夹中,我们将创建三个组件 TS 文件“MasterPageComponent.ts”、“SupplierComponent.ts”和“WelcomeComponent.ts”。 您可以将组件代码文件可视化为 Angular UI 的代码后。 | ![]() |
首先,让我们从“MasterPage.html”组件开始,我们将其命名为“MasterPageComponent.ts”。此主页将在初始引导过程中加载到“Index.html”中。您可以看到在此组件中,我们放置了选择器,这将是唯一具有选择器的组件。
import {Component} from "@angular/core"
@Component({
selector: "main-ui",
templateUrl: "../UI/MasterPage.html"
})
export class MasterPageComponent {
}
以下是“Supplier.html”的组件代码。
import {Component} from "@angular/core"
@Component({
templateUrl: "../UI/Supplier.html"
})
export class SupplierComponent {
}
以下是“Welcome.html”的组件代码。Supplier
和 Welcome
组件都没有选择器,只有主页组件有,因为它将是加载到 index 页面的启动 UI。
import {Component} from "@angular/core"
@Component({
templateUrl: "../UI/Welcome.html"
})
export class WelcomeComponent {
}
步骤 6:创建路由常量集合
一旦主页在 index 页面中加载,最终用户将单击主页链接浏览到供应商页面、客户页面等。现在,为了让用户能够正确浏览,我们需要定义导航路径。这些路径将在后续步骤的“href
”标签中指定。
当浏览这些路径时,它将调用组件,组件将加载 UI。下表包含三列。第一列指定路径模式,第二列指定浏览这些路径时将调用的组件,最后一列指定将加载的 UI。
路径/URL | 组件 (Component) | 将加载的 UI |
/ | WelcomeComponent.ts | Welcome.html |
/Customer | CustomerComponent.ts | Customer.html |
/Supplier | SupplierComponent.ts | Supplier.html |
路径和组件条目需要定义在一个简单的字面量集合中,如下面的代码所示。您可以看到“ApplicationRoutes
”是一个简单的集合,我们在其中定义了路径和将被调用的组件。这些条目是根据顶部指定的表制作的。
import {Component} from '@angular/core';
import {CustomerComponent} from '../Component/CustomerComponent';
import {SupplierComponent} from "../Component/SupplierComponent";
import {WelcomeComponent} from "../Component/WelcomeComponent";
export const ApplicationRoutes = [
{ path: 'Customer', component: CustomerComponent },
{ path: 'Supplier', component: SupplierComponent },
{ path: '', component:WelcomeComponent }
];
作为最佳实践,我们将所有上述代码定义在一个单独的“routing”文件夹和“routing.ts”文件中。
步骤 7:定义 routerLink 和 router-outlet
在“第 6 步”的集合中定义的导航(路由)需要在我们尝试在网站内部导航时引用。例如,在主页中,我们定义了左侧菜单超链接。
所以,我们不需要使用 HTML 的“href
”标签,而是需要使用“[routerLink]
”。
<a href=”Supplier.html”>Supplier</a>
我们需要使用“[routerLink]
”,而“[routerLink]
”的值将是上一步定义的路由集合中指定的路径。例如,在“ApplicationRoutes
”集合中,我们为 Supplier
路径做了一个条目,我们需要在锚定标签中指定路径,如下面的代码所示。
<a [routerLink]="['Supplier']">Supplier</a>
当最终用户单击主页左侧的链接时,页面(供应商页面、客户页面和欢迎页面)将加载到“div
”标签内。为此,我们需要定义“router-outlet
”占位符。在此占位符内,页面将动态加载和卸载。
<div id=”dynamicscreen”>
<router-outlet></router-outlet>
</div>
因此,如果我们用“router-link
”和“router-outlet
”更新“第 1 步”中定义的主页,我们将得到如下代码。
<table border="1">
<tr>
<td><img src="http://www.questpond.com/img/logo.jpg" alt="Alternate Text" />
</td>
<td>Header</td></tr><tr>
<td>Left Menu<br /><br /><br />
<a [routerLink]="['Supplier']">Supplier</a> <br /><br />
<a [routerLink]="['Customer']">Customer</a></td><td>
<div id=”dynamicscreen”>
<router-outlet></router-outlet>
</div>
</td>
</tr>
<tr>
<td>Footer</td><td></td>
</tr>
</table>
步骤 8:在主模块中加载路由
为了启用“ApplicationRoutes
”中定义的路由集合路径,我们需要将其加载到“MainModuleLibrary
”中,如下面的代码所示。“RouterModule.forRoot
”有助于在模块级别加载应用程序路由。
一旦在模块级别加载,它将可用于该模块内加载的所有组件进行导航。
@NgModule({
imports: [RouterModule.forRoot(ApplicationRoutes),
BrowserModule,
FormsModule],
declarations: [CustomerComponent,MasterPageComponent,SupplierComponent],
bootstrap: [MasterPageComponent]
})
export class MainModuleLibrary { }
包含路由的完整代码如下所示。
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import {FormsModule} from "@angular/forms"
import { CustomerComponent } from '../Component/CustomerComponent';
import { SupplierComponent } from '../Component/SupplierComponent';
import { MasterPageComponent } from '../Component/MasterPageComponent';
import { RouterModule } from '@angular/router';
import { ApplicationRoutes } from '../Routing/Routing';
@NgModule({
imports: [RouterModule.forRoot(ApplicationRoutes),
BrowserModule,
FormsModule],
declarations: [CustomerComponent,MasterPageComponent,SupplierComponent],
bootstrap: [MasterPageComponent]
})
export class MainModuleLibrary { }
步骤 9:定义 APP BASE HREF
Router 模块需要一个根路径。换句话说,“Supplier
”路由将变为“companyname/Supplier
”,“Customer
”路由将变为“companyname/Customer
”,依此类推。如果您不提供路由,您将遇到如下错误。
因此,在您的“Index.html”中,我们需要添加 HTML BASE HREF 标签,如下面的突出显示代码所示。此时,我们不提供任何根目录。
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta charset="utf-8" />
</head>
<base href="./">
<!—Other code has been removed for clarity -->
步骤 10:查看输出
现在运行网站,尝试浏览到 UI 文件夹,您应该会看到下面的动画视频输出。您可以看到 Logo 只加载了一次,之后当用户单击供应商链接时,客户链接的图像不会重复加载。
步骤 11:修复“无法匹配任何路由”错误
如果您按“F12”并在 Chrome 浏览器的控制台部分查看,您会看到如下错误。您能猜出错误是什么吗?
您当前的 Angular 应用程序已启用路由。因此,浏览的每个 URL 都会在路由集合中查找。 所以这就是为什么会抛出上述错误。 | ![]() |
为了解决这个问题,为“UI
”路径再添加一个条目,并将其指向“WelcomeComponent
”。
export const ApplicationRoutes = [
{ path: 'Customer', component: CustomerComponent },
{ path: 'Supplier', component: SupplierComponent },
{ path: '', component:WelcomeComponent },
{ path: 'UI', component:WelcomeComponent }
];
理解流程
- 最终用户加载 index.html 页面。
- Index.html 触发 systemjs 并加载 masterpage.html。
- 当最终用户单击主页链接时,其他页面会在“
router-outlet
”占位符中加载。
实验 8:使用 Angular 表单实现验证
在此实验中,我们将尝试了解如何使用 Angular 框架实现验证。
良好验证结构的要求
验证是软件应用程序的重要组成部分。 验证通常应用于用户界面 (Forms),用户界面包含控件,我们在控件上应用验证。 | ![]() |
在 Angular 表单验证中,架构结构如下。 顶部是 | ![]() |
实现 Angular 验证有三个主要步骤:
- 创建
FormGroup
。 - 创建
FormControl
并添加适当的验证。 - 将这些验证映射到 HTML 表单。
在此实验中,我们将实现哪种验证?
在此实验中,我们将在我们的 Customer
屏幕上实现以下验证:
- 客户名称必须填写。
- 客户代码必须填写。
- 客户代码的格式应为 A1001、B4004 等。换句话说,第一个字符应为大写字母,后跟 4 位数字。
注意:客户代码具有复合验证。
验证放在哪里?
在开始验证之前,我们需要决定将验证放在哪个合适的位置。如果您在 Angular 中看到,我们有三个主要部分 - UI、Component 和 Model。所以让我们思考一下将验证放在哪个层是合适的。
- UI:UI 主要关注控件的外观、感觉和定位。因此,在此层放置验证是个坏主意。是的,在此层,我们将应用验证,但验证代码应位于其他层。
- Component:Component 是绑定 UI 和 Model 的代码后 (Binding code)。在此层,应放置更多与绑定相关的活动。一个组件可以使用多个模型,因此如果我们在这里放置验证逻辑,则需要在其他组件中也进行重复。
- Model:Model 代表现实世界的实体,如人、用户、管理员、产品等。行为(即验证)是模型的一部分。模型具有数据和行为。因此,验证应该存在的正确位置是此层。
因此,让我们将验证作为模型的一部分。
步骤 1:导入 Angular 验证器所需的组件
所以第一步是在客户模型中导入 Angular 验证器所需的组件。所有 Angular 验证器组件都位于“@angular/forms”文件夹中。我们需要导入五个组件:NgForm
、FormGroup
、FormControl
和 Validators
。
NgForm
:Angular 验证标签FormGroup
:帮助我们创建验证集合FormControl
和Validators
:帮助我们在FormGroup
中创建单个验证FormBuilder
:帮助我们创建 Form group 和 Form controls 的结构。请注意,一个 Form group 可以包含多个FormControls
。
import {NgForm,
FormGroup,
FormControl,
Validators,
FormBuilder } from '@angular/forms'
步骤 2:使用 FormBuilder 创建 FormGroup
第一步是创建一个 FormGroup
对象,我们将在其中包含验证集合。FormGroup
对象将使用“FormBuilder
”类构造。
formGroup: FormGroup = null; // Create object of FormGroup
var _builder = new FormBuilder();
this.formGroup = _builder.group({}); // Use the builder to create object
步骤 3:添加简单验证
创建 FormGroup
对象后,下一步是向 FormGroup
集合添加控件。要添加控件,我们需要使用“addControl
”方法。 “addControl
”方法中的第一个参数是验证的名称,第二个参数是要添加的 Angular 验证器类型。
以下是一个简单代码,我们在其中使用“Validators.required
” FormControl
添加一个“CustomerNameControl
”。请注意,“CustomerNameControl
”不是保留关键字。它可以是任何名称,如“CustControl
”。
this.formGroup.addControl('CustomerNameControl', new
FormControl('',Validators.required));
步骤 4:添加复合验证
如果要创建复合验证,则需要创建一个集合并使用“compose
”方法添加它,如下面的代码所示。
var validationcollection = [];
validationcollection.push(Validators.required);
validationcollection.push(Validators.pattern("^[A-Z]{1,1}[0-9]{4,4}$"));
this.formGroup.addControl('CustomerCodeControl',
new FormControl('', Validators.compose(validationcollection)));
包含验证的完整模型代码
以下是包含前面部分讨论的所有三个验证的完整 Customer 模型代码。我们还注释了代码,以便您可以理解。
// import components from angular/form
import {NgForm,
FormGroup,
FormControl,
Validators,
FormBuilder } from '@angular/forms'
export class Customer {
CustomerName: string = "";
CustomerCode: string = "";
CustomerAmount: number = 0;
// create object of form group
formGroup: FormGroup = null;
constructor(){
// use the builder to create the
// the form object
var _builder = new FormBuilder();
this.formGroup = _builder.group({});
// Adding a simple validation
this.formGroup.addControl('CustomerNameControl', new
FormControl('',Validators.required));
// Adding a composite validation
var validationcollection = [];
validationcollection.push(Validators.required);
validationcollection.push(Validators.pattern("^[A-Z]{1,1}[0-9]{4,4}$"));
this.formGroup.addControl('CustomerCodeControl', new
FormControl('', Validators.compose(validationcollection)));
}
}
步骤 5:在 CustomerModule 中引用“ReactiveFormsModule”
// code has been removed for clarity.
import { FormsModule, ReactiveFormsModule } from "@angular/forms"
@NgModule({
imports: [RouterModule.forChild(CustomerRoute),
CommonModule,
FormsModule,
ReactiveFormsModule,
HttpModule,
InMemoryWebApiModule.forRoot(CustomerService)],
declarations: [CustomerComponent, GridComponent],
bootstrap: [CustomerComponent]
})
export class CustomerModule {
}
步骤 6:将 formGroup 应用于 HTML 表单
下一件事是将“formGroup
”对象应用于 HTML 表单。为此,我们需要在“[formGroup]
” Angular 标签中使用,我们需要在其中指定通过 customer
对象公开的“formGroup
”对象。
<form [formGroup]="CurrentCustomer.formGroup">
</form>
步骤 7:将验证应用于 HTML 控件
下一步是将 formgroup
验证应用于 HTML 输入控件。这通过使用“formControlName
” Angular 属性完成。在“formControlName
”中,我们需要提供在创建验证时创建的表单控件名称。
<input type="text" formControlName="CustomerNameControl"
[(ngModel)]="CurrentCustomer.CustomerName"><br /><br />
步骤 8:检查验证是否正常
当用户开始填写数据并满足验证时,我们希望检查所有验证是否正常,并相应地显示错误消息或启用/禁用 UI 控件。
为了检查所有验证是否正常,我们需要使用“formGroup
”的“valid
”属性。以下是一个简单示例,其中按钮的禁用状态取决于验证是否有效。“[disabled]
”是一个 Angular 属性,用于启用和禁用 HTML 控件。
<input type="button"
value="Click" [disabled]="!(CurrentCustomer.formGroup.valid)"/>
步骤 9:检查单个验证
“CurrentCustomer.formGroup.valid
”会检查“FormGroup
”的所有验证,但如果我们想检查控件的单个验证怎么办?
为此,我们需要使用“hasError
”函数。
“CurrentCustomer.formGroup.controls['CustomerNameControl'].hasError('required')
”会检查“CustomerNameControl
”是否满足“required”验证规则。以下是一个简单代码,我们在其中显示错误消息在“div
”标签中,该标签根据“hasError
”函数返回 true
或 false
来显示或隐藏。
还要注意“hasError
”前面的“!
”(非),这意味着如果“hasError
”为 true
,则 hidden
应为 false
,反之亦然。
<div [hidden]="!(CurrentCustomer.formGroup.controls['CustomerNameControl'].hasError
('required'))">Customer name is required </div>
步骤 10:独立元素
在我们的表单中,我们有三个文本框:“CustomerName
”、“CustomerCode
”和“CustomerAmount
”。在这三个文本框中,只有“CustomerName
”和“CustomerCode
”有验证,而“CustomerAmount
”没有验证。
这有点奇怪,但如果我们不为位于具有“formGroup
”指定的“form
”标签内的用户控件指定验证,您将遇到一个长异常,如下所示。
Error: Uncaught (in promise): Error: Error in ../UI/Customer.html:15:0 caused by:
ngModel cannot be used to register form controls with a
parent formGroup directive. Try using
formGroup's partner directive "formControlName" instead. Example:
<div [formGroup]="myGroup">
<input formControlName="firstName">
</div>
In your class:
this.myGroup = new FormGroup({
firstName: new FormControl()
});
Or, if you'd like to avoid registering this form control,
indicate that it's standalone in ngModelOptions:
Example:
<div [formGroup]="myGroup">
<input formControlName="firstName">
<input [(ngModel)]="showMoreControls" [ngModelOptions]="{standalone: true}">
</div>
Error:
ngModel cannot be used to register form controls with a parent formGroup directive.
Try using
formGroup's partner directive "formControlName" instead. Example:
<div [formGroup]="myGroup">
<input formControlName="firstName">
</div>
In your class:
this.myGroup = new FormGroup({
firstName: new FormControl()
});
Or, if you'd like to avoid registering this form control,
indicate that it's standalone in ngModelOptions:
Example:
<div [formGroup]="myGroup">
<input formControlName="firstName">
<input [(ngModel)]="showMoreControls" [ngModelOptions]="{standalone: true}">
</div>
上述错误可以简化为三点:
- 它表明您将一个 HTML 控件包含在一个具有 Angular 表单验证的 HTML
FORM
标签内。 - HTML
FORM
标签内的所有应用了 Angular 验证的控件都应该有验证。 - 如果 Angular 表单验证中的 HTML 控件没有验证,您可以采取以下任一措施来消除异常:
- 您需要将其指定为
standalone
控件。 - 将控件移出 HTML
FORM
标签。
- 您需要将其指定为
以下是如何为 Angular 验证指定“standalone
”的代码。
<input type="text" [ngModelOptions]="{standalone:true}"
[(ngModel)]="CurrentCustomer.CustomerAmount"><br /><br />
还可以讨论我们可以从表单控件中移除什么以及会发生什么。
应用了验证的 Customer UI 的完整代码
以下是完整的 Customer
UI,已将所有三个验证应用于“CustomerName
”和“CustomerCode
”控件。
<form [formGroup]="CurrentCustomer.formGroup">
<div>
Name:
<input type="text" formControlName="CustomerNameControl"
[(ngModel)]="CurrentCustomer.CustomerName"><br /><br />
<div [hidden]="!(CurrentCustomer.formGroup.controls['CustomerNameControl'].
hasError('required'))">Customer name is required </div>
Code:
<input type="text" formControlName="CustomerCodeControl"
[(ngModel)]="CurrentCustomer.CustomerCode"><br /><br />
<div [hidden]="!(CurrentCustomer.formGroup.controls['CustomerCodeControl'].
hasError('required'))">Customer code is required </div>
<div [hidden]="!(CurrentCustomer.formGroup.controls['CustomerCodeControl'].
hasError('pattern'))">Pattern not proper </div>
Amount:
<input type="text"
[(ngModel)]="CurrentCustomer.CustomerAmount"><br /><br />
</div>
{{CurrentCustomer.CustomerName}}<br /><br />
{{CurrentCustomer.CustomerCode}}<br /><br />
{{CurrentCustomer.CustomerAmount}}<br /><br />
<input type="button"
value="Click" [disabled]="!(CurrentCustomer.formGroup.valid)"/>
</form>
编写响应式表单。
运行并查看您的验证效果
完成后,您应该能够看到验证效果,如下图所示。
脏、原始、已触摸和未触摸
在此实验中,我们涵盖了“valid
”属性和“hasError
”函数。“formGroup
”还有许多其他属性,您在处理验证时会需要它们。以下是一些重要的。
属性 | 解释 |
dirty | 此属性指示 Value 是否已修改。 |
pristine | 此属性表示字段是否已更改。 |
touched | 当该控件失去焦点时发生。 |
untouched | 字段未被触摸。 |
下一篇文章有什么内容?
在下一节中,我们将研究如何使用 Angular 4 创建 SPA 应用程序和验证。要阅读 Learn Angular 的第四部分,请单击此处。
如需进一步阅读,请观看下面的面试准备视频和逐步视频系列。
- Angular 教程面向初学者 | 分步学习 Angular
- Angular 面试问答
- ASP.NET MVC 面试题及答案
- C# 面试问答
- 分步学习 Azure
- SQL Server 分步教程
- RxJS 面试问答
- 1 小时学习 Angular(Angular 9)
- 8 小时学会 Angular
历史
- 2017 年 9 月 22 日:初始版本