Angular进阶学习之深入了解路由和表单
Angular的路由介绍
在单页面应用中,需要在定义好的不同视图中(组件)来回切换,而不用去服务器中获取新页面,要实现从一个视图到另外一个视图的导航,就需要使用到Angular中的Router。【相关教程推荐:《angular教程》】
需要路由跳转的场景:
假如页面上有两个跳转链接,我们希望点击这些链接的时候可以分别跳转到商品列表页面
和个人中心页面
。我们可以先创建两个组件分别是 GoodsListcomponent 和PErsonalcenterComponent。具体流程如下所示:
路由创建
1、新建文件app-routing.module.ts,将要跳转的视图配置放到里边
import { NgModule } From '@angular/core'; // 引入路由模块 RouterModule和 Routesimport { Routes, RouterModule } from '@angular/router'; // 引入在不同URL下,需要展示的组件import { GoodsListComponent } from './goods-list/goods-list.COMponent'; import { PersonalCenterComponent } from './personal-center/personal-center.component'; // 配置的路由数组const routes: Routes = [ { path: 'goodsList', // 定义路由名称 component: GoodsListComponent, // 指定显示的那个组件 } , { path: 'personalCenter', // 定义路由名称 component: PersonalCenterComponent, // 指定显示的那个组件 } ]; @NgModule({ // forRoot() 方法会创建一个 NgModule,其中包含所有指令、给定的路由以及 Router 服务本身。 imports: [RouterModule.forRoot(routes)], exports: [RouterModule]} )export class ApPRoutingModule { }
在 Routes 数组
中定义你的路由(这个数组中的每个路由都是一个包含两个属性的 JavaScript 对象。第一个属性 path 定义了该路由的 URL 路径。第二个属性 component 定义了相对应的路径下需要显示的组件)
2、在app.module.ts中导入AppRoutingModule
import { AppRoutingModule } from './app-routing.module';
3、在app.component.htML中把这些路由添加进来,以便控制导航的展示
div class="route-change-container"> a routerLink="/goodsList"> 切换到商品列表组件/a> !--routerLink将你定义的路由连接到模板文件中--> a routerLink="/personalCenter"> 切换到个人中心组件/a> /div> router-outlet> /router-outlet> !--此指令通过路由来动态加载组件-->
- 件。
- 接下来,在模板中添加
router-outlet>
标签。该元素会通知 Angular,你可以用所选路由的组件更新应用的视图。用法和组件一样,充当占位符,用来标记路由器应该把组件显示到那个位置上。
路由中两个重要API的介绍
ActivatedRoute
用于@H_512_75@{ 获取路由信息} 获取路由信息,它所包含路由的信息比如: 路由参数,静态数据...具体提供的方法可参照ActivatedRoute官网
import { Component, OnInIT } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; // ① 先引入ActivatedRoute@Component({ selector: 'app-goods-list', templateUrl: './goods-list.component.html', styleUrls: ['./goods-list.component.scss']} )export class GoodsListComponent implements OnInit { constructor( private route: ActivatedRoute, // ② 依赖注入这个服务 ) { } // ③ 在初始化的生命周期中去或者url的路由信息 public ngOnInit(): void { // 第一种获取参数的方式 const params = this.route.snapshot.params; // 第二种获取参数的方式 this.route.params.subscribe(params => { console.LOG(params); } ); } }
上述两种获取参数方法的区别和使用:
route.snapshot:
- 需要直接访问参数,主要获取
初始值
,不用订阅的情况下用snapshot;
route.params.subscribe():
- 参数每次变化都要获取到或者需要连续导航多次的时候的用params;
是一个{ 提供导航和操纵URL} 提供导航和操纵URL的模块,具体提供的方法可参照Router官网,开发中常用到的方法如下,具体讲解在路由传参中会详细介绍:
// 使用前,需要在组件中先引入Router这个服务类import { Router } from '@angular/router';
navigate() 该方法支持的参数类型和routerLink指令一样,所以他们的作用也是一样的;
navigateByUrl() 绝对路由;
每次导航前都会调用events方法;(暂时仅了解)
路由传参以及获取
路由传参的两种形式
1. params (是/:id 动态路由)
使用场景: 比如当我们点击商品列表链接时,希望把用户信息,或者商品种类信息通过url,传到商品列表的组件中去。
// 需要配置路由const routes: Routes = [ { path: 'goodsList/:id', // 定义路由名称 component: GoodsListComponent, // 指定显示的那个组件 } ,];
2. queryParams(是?id=xx 形式)
使用场景: 当我们希望通过url传递多个参数的时候,可以选择用这种方式进行路由传参
1个参数可以优先用动态路由,2个以上还是用query更加方便点
路由中传参的3种具体方法
1. routerLink
单一参数:
a [routerLink]="['/goodsList', id]" routerLinkActive="active-class"> 切换到商品列表组件/a> // 其中/goodsListl是我设置的路由path,id是需要传递的参数 // 多个参数的时候,也可以用这种形式,只不过看起来不够直观,所以不推荐多个参数的时候也用这种方法
多个参数:
a [routerLink]="['/personalCenter']" [queryParams]="{ name: 'zs', age: 16} "> 切换到个人中心组件/a> // 参数的格式可以自行组织成各种object
routerLinkActive
跟踪元素上链路路由当前是否处于活动状态
。并允许你指定一个或者多个css类,以便在链接路由处于活动状态时添加该元素。
2. router.navigate
基于所提供的命令数组和起点路由进行导航。 如果没有指定起点路由,则从根路由开始进行绝对导航
单一参数:
public goToGoodsListPage(): void { // 第一种方式 this._router.navigate([`/goodsList/${ this.id} `]); // 第二种方式 与第一种方式达到相同的效果 this._router.navigate(['/goodsList', this.id]); } // html中调用这个方法,等同于使用a标签的routerLink属性button (click)="goToGoodsListPage()"> 切换到商品列表组件/button>
多个参数:
public goToPersonalCenterPage(): void { this._router.navigate(['/personalCenter'], { queryParams:{ name: 'zs', age: 16} } ); } // html中调用 button (click)="goToPersonalCenterPage()"> 切换到个人中心组件/button>
3. router.navigateByUrl
基于所提供的 URL 进行导航,必须使用绝对路径
。对于单一参数和多个参数的使用方法与router.navigate一致。
// 传的第一个参数是数组的形式,而navigate的第一个参数是数组的方式// 他的传参目前是拼接在url上边的 public goToPersonalCenterPage(): void { this._router.navigateByUrl(`/personalCenter?name=zs& age=16`); }
在路由中接收参数的2种方式
1. 接收params类型的参数
import { ActivatedRoute } from '@angular/router'; constructor( private _route: ActivatedRoute, ) { } public ngOnInit(): void { // 第一种只获取初始值方式 const param = this.route.snapshot.params; // 第二种动态获取方式 this.route.params.subscribe(params => { console.log(params); } ); }
2. 接收queryParams类型的参数
import { ActivatedRoute } from '@angular/router'; constructor( private _route: ActivatedRoute, ) { } public ngOnInit(): void { // 第一种只获取初始值方式 const queryParam = this.route.snapshot.queryParams; // 第二种动态获取方式 this.route.queryParams.subscribe(params => { console.log(params); } ); }
路由重定向
应用场景:当在一个应用中,希望用户默认跳转到某个页面时,就需要使用得到路由重定向。
重定向的组成:
- 使用重定向源的path
- 重定向目标的component
- pathMatch值来配置路由
{ path: '', redirectTo: '/personalCenter', pathMatch: 'full' } , // 重定向到 `personalCenter`
{ pathMatch} pathMatch是一个用来{ 指定路由匹配策略} 指定路由匹配策略的字符串。可选项有 prefix(默认值)和 full。
prefix: 以指定的路径开头 (就是path中的值与用户输入的的开头路径是一样的,比如path是”abc“,那么用户输入/abc, /abc/a都是可以的,但是输入 /abcd这样就不可以); 如果path是空,那么所有路由都会匹配到这个页面上
full: 与指定路径完全一样(这个就要求与path要求的路径完全一样,比如说path是‘abc’,那么用户输入/abc , /abc/是可以的 /abcd,/abc/d是不可以的); 如果path是空,那么只有localhost:4200后边什么都不加的情况下才匹配到这个页面上。
路由顺序
Router在匹配路由的时候,使用的是”{ 先到先得} 先到先得“的策略,所以应当将{ 具体的静态的路由} 具体的静态的路由放前边,然后可以放置{ 默认路由匹配} 默认路由匹配的空路径路由,{ 通配符} 通配符路由是最后一个(他可以匹配所以路由,当其他路由都没有匹配对的时候,Router才会选择他)
const routes: Routes = [ // 静态路由 { path: 'goodsList/:id', component: GoodsListComponent, } , { path: 'personalCenter', component: PersonalCenterComponent, } , // 默认空路径路由 { path: '', redirectTo: '/personalCenter', pathMatch: 'full' } , // 通配符路由 { path: '**', component: PageNotFoundComponent } ,];
路由嵌套
当应用变的复杂的时候,可能需要创建一些根组件之外的相对路由,这些路由成为子路由,这意味着在项目中需要添加第二个 { router-outlet> } router−outlet> ,因为它是 AppComponent 之外的另一个 { router-outlet> } router−outlet> 。
场景: 我们需要在个人中心的页面中,跳转到他的两个子页面中,分别是{ 个人配置页面} 个人配置页面和{ 个人详情页面} 个人详情页面,具体代码实现如下所示:
import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; import { GoodsListComponent } from './goods-list/goods-list.component'; import { PersonalCenterComponent } from './personal-center/personal-center.component'; import { PersonalSettingComponent } from './personal-setting/personal-setting.component'; import { PersonalDetailComponent } from './personal-detail/personal-detail.component'; const routes: Routes = [ { path: 'goodsList/:id', component: GoodsListComponent, } , { path: 'personalCenter', component: PersonalCenterComponent, children: [ // 主要是这里添加了一个children数组,用来存放子路由 { path: 'personalDetail', component: PersonalDetailComponent, } , { path: 'personalSetting', component: PersonalSettingComponent, } , ] } , { path: '', redirectTo: '/personalCenter', pathMatch: 'full' } ,]; // 个人中心的html中,需要新增router-outlet> 从而来指定子页面展示的位置div class="personal-center-container"> 这是个人中心页面 a routerLink="./personalDetail"> 切换个人详情页面/a> a routerLink="./personalSetting"> 切换到个人配置页面/a> /div> router-outlet> /router-outlet>
子路由和其它路由一样,同时需要 path 和 component。唯一的区别是你要把子路由放在父路由的 children
数组中。
路由懒加载
你可以配置路由定义来实现惰性加载模块,这意味着 Angular只会在{ 需要时} 需要时才加载这些模块,而不是在应用{ 启动时} 启动时就加载全部。
1、先给之前的两个页面组件增加一个module文件,然后routes中使用loadChildren代替component进行配置
import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; const routes: Routes = [ { path: 'goodsList/:id', loadChildren: () => import('./goods-list/goods-list.module') .then(m => m.GoodListModule) } , { path: 'personalCenter', loadChildren: () => import('./personal-center/personal-center.module') .then(m => m.PersonalCenterModule) } , { path: '', redirectTo: '/personalCenter', pathMatch: 'full' } ,]; @NgModule({ // forRoot() 方法会创建一个 NgModule,其中包含所有指令、给定的路由以及 Router 服务本身。 imports: [RouterModule.forRoot(routes)], exports: [RouterModule]} )export class AppRoutingModule { }
2、在之前的两个页面组件中添加路由模块文件,添加一个指向该组件的路由。
import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; import { GoodsListComponent } from './goods-list.component'; const routes: Routes = [ { path: '', component: GoodsListComponent } ,]; @NgModule({ imports: [RouterModule.forChild(routes)], exports: [RouterModule]} )export class GoodsListRoutingModule { }
路由守卫(仅了解)
使用路由守卫来防止用户未经授权就导航到应用的某些部分,想了解更多请移步 路由守卫官网介绍
- 用CanActivate来处理导航到某路由的情况。 // 进入路由的时候
- 用CanActivateChild来处理导航到某子路由的情况。
- 用CanDeactivate来处理从当前路由离开的情况. // 离开路由的时候
- 用Resolve在路由激活之前获取路由数据。
- 用CanLoad来处理异步导航到某特性模块的情况。CanActivate仍然会加载某个模块,canload不会去加载当不满足条件的情况下
export class testGuard implements CanActivate { canActivate( next: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { //判断是否可以进入某个路由的逻辑 } }
{ path: 'test-path', component: TestComponent, canActivate: [TestGuard], // 使用路由守卫guard}
路由事件(仅了解)
Router 在每次导航过程中都会通过 Router.events 属性发出导航事件。这些事件的范围贯穿从导航开始和结束之间的多个时间点。想了解更多请移步
路由事件官网详细介绍
https://angular.cn/guide/router-reference#router-events
Angular中的表单介绍
响应式表单与模板驱动表单的区别
响应式 | 模板驱动 | |
---|---|---|
建立表单模型 | 显示的,在组件类中创建 | 隐式的,有指令创建 |
数据模型 | 结构化和不可变的(可观察对象) | 非结构化和可变的(数据双向绑定) |
数据流 | 同步 | 异步 |
表单验证 | 函数 | 指令 |
常用表单公共基础类
响应式表单和模板驱动表单都建立在下列基础类之上。
FormControl 实例用于追踪单个表单控件的值和验证状态。
FormGroup 用于追踪一个表单控件组的值和状态。
FormArray 用于追踪表单控件数组的值和状态。(了解)
模板驱动表单
{ 依赖} 依赖模板中的{ 指令} 指令来创建和操作底层的对象模型。主要用于添加简单的表单,使用起来较简单,但是扩展性方面不如响应式表单。当控件值更新的时候相关的属性值会被修改为新值。
创建表单
!-- #form是模板引用变量,他就代表是form整个元素,我们可以在当前模板的任何地方使用模板引用变量。--> !-- 这些变量提供了从模块中直接访问元素的能力 --> !-- name名称一定要加上 因为表单中的所有组件和控件都应该有名称--> form (ngSubmit)="onSubmit(myform.value); " #myform="ngForm" novalidate> label for="name"> 姓名: /label> input type="text" name="name" [(ngModel)]="name"> label for="password"> 密码: /label> input type="password" name="password" [(ngModel)]="password"> button> 提交/button> /form>
模板驱动表单中验证输入
!--假如我们希望给上述的表单做个校验,密码不能为空,名字的长度不能少于2个字符--> form (ngSubmit)="onSubmit(myform); " #myform="ngForm"> label for="name"> 姓名: /label> input type="text" name="name" [(ngModel)]="name" minlength="2"> label for="password"> 密码: /label> input type="password" name="password" [(ngModel)]="password" required> button> 提交/button> !-- 主要通过hasError方法来实现--> div *ngIf="myform.form.hasError('minlength', 'name')"> 姓名的长度不能少于2位/div> div *ngIf="myform.form.hasError('required', 'password')"> 密码不能为空/div> /form>
响应式表单(重点)
提供对{ 底层表单对象模型} 底层表单对象模型直接、显式的访问。它们与模板驱动表单相比,更加健壮,它们的可扩展性、可复用性和可测试性都更高。而且控件更新的{ 效率更高} 效率更高,因为FormControl 的实例总会返回一个新值,而不会更新现有的数据模型,如果表单是你的应用程序的关键部分,或者可扩展性很强,那就使用响应式表单
使用响应式表单
1、在模块文件中引入响应式表单模块
import { ReactiveFormsModule } from '@angular/forms';
2、在组件中引入表单组
import { FormGroup, FormControl } from '@angular/forms';
3、创建一个FormGrouP实例,并把这个FormGroup模型关联到视图
this.profileForm = new FormGroup({ name: new FormControl(''), password: new FormControl(''), } );
form [formGroup]="profileForm" (ngSubmit)="submitForm(profileForm.value)"> label for="name"> First Name: /label> input id="name" type="text" formControlName="name"> label for="password"> Last Name: /label> input id="password" type="text" formControlName="password"> button> 提交/button> /form>
表单嵌套
表单组可以同时接受{ 单个表单控件实例} 单个表单控件实例和其它表单组实例作为其{ 子控件} 子控件。这可以让复杂的表单模型更容易维护,并在逻辑上把它们分组到一起。
1、创建一个嵌套的表单组。
this.profileForm = new FormGroup({ name: new FormControl('', [Validators.required]), password: new FormControl(''), // 在表单中嵌套一个address的表单组 address: new FormGroup({ street: new FormControl(''), city: new FormControl(''), } ) } );
2、在模板中对这个嵌套表单分组。
form [formGroup]="profileForm" (ngSubmit)="submitForm(profileForm.value)"> label for="name"> First Name: /label> input id="name" type="text" formControlName="name"> label for="password"> Last Name: /label> input id="password" type="text" formControlName="password"> // 使用formGroupName来声明新的表单组 div formGroupName="address"> h3> Address/h3> label for="street"> Street: /label> input id="street" type="text" formControlName="street"> label for="city"> City: /label> input id="city" type="text" formControlName="city"> /div> button> 提交/button> /form>
表单验证
控件状态的字段
在学习表单验证之前,我们先来了解一下angular针对表单所提供的一些{ 状态字段} 状态字段,这些字段可帮助用户决定在那些阶段去做表单验证。
- touched 和 untouched
用来判断用户是否获取过焦点
,如果获取过焦点则touched=true;
untouched=false,如果从来没有获取过焦点,touched=false;
untouched=true。
- pristine 和 dirty
用来判断用户是否操作过字段
,dirty表示用户操作过表单元素,pristine表示未操作过表单元素。
- valid 和 invalid
判断表单元素是否有效
,当表单元素上有多个验证条件事,只有所有的验证条件都满足验证要求valid才是true,只要有一个验证条件不满足,那么invalid这个属性值就是true。
控件状态的CSS 类
Angular 会自动把很多{ 控件属性} 控件属性作为 { CSS 类} CSS类映射到控件所在的{ 元素} 元素上。你可以使用这些类来{ 根据表单} 根据表单 { 状态} 状态给表单{ 控件元素} 控件元素添加样式。
- .ng-valid
- .ng-invalid
- .ng-pending
- .ng-pristine
- .ng-dirty
- .ng-untouched
- .ng-touched
场景: 比如我们希望当控件在不满足验证条件的情况下,控件边框显示成红色~ 实现方式如下:
.ng-touched:not(form){ & .ng-invalid:not(form){ // 这是scss的一种写法 border: 1px solid red } }
表单验证的步骤
1、在表单组件中导入一个验证器函数,Angular具体提供了那些内置验证器请参照表单验证器官网
import { Validators } from '@angular/forms'; // angular提供的内置验证器主要有: min max required email minLength maxLength pattern...
2、把这个验证器添加到表单中的相应字段。
场景:需要做验证的内容分别是,① 名称不能为空; ② 密码不能为空,且最小长度不能少于4; ③住址中的街道不能为空
this.profileForm = new FormGroup({ name: new FormControl('', [Validators.required]), password: new FormControl('', [Validators.required, Validators.minLength(4)]), address: new FormGroup({ street: new FormControl('', [Validators.required]), city: new FormControl(''), } ) } );
3、在模板中错误信息的提醒逻辑
form [formGroup]="profileForm" (ngSubmit)="submitForm(profileForm.value)"> !-- 姓名--> label for="name"> 姓名: /label> input id="name" type="text" formControlName="name"> div [hidden]="profileForm?.get('name').valid || profileForm?.get('name')?.untouched"> p [hidden]="!profileForm.hasError('required', 'name')"> 名字是必填项/p> /div> !-- 密码--> label for="password"> 密码: /label> input id="password" type="text" formControlName="password"> !-- 先验证密码当密码是合法的或者用户从未操作多的时候,不显示错误信息 --> !-- 然后在根据hasError来判断具体的错误提醒--> div [hidden]="profileForm?.get('password').valid || profileForm?.get('password')?.untouched"> p [hidden]="!profileForm.hasError('minlength', 'password')"> 密码的位数不能少于四位/p> p [hidden]="!profileForm.hasError('required', 'password')"> 密码不能为空/p> /div> !-- 地址表单组--> div formGroupName="address"> h3> Address/h3> label for="street"> Street: /label> input id="street" type="text" formControlName="street"> div [hidden]="profileForm?.get('address')?.get('street')?.valid || profileForm?.get('address')?.get('street')?.untouched"> div [hidden]="!profileForm.hasError('required', ['address', 'street'])"> 街道不能为空/div> /div> label for="city"> City: /label> input id="city" type="text" formControlName="city"> /div> button> 提交/button> /form>@H_952_1406@自定义表单验证器
场景: 用户希望在填写手机号的时候,可以验证手机号的格式是否输入正确,便可以通过自定义函数去做这件事
public ngOnInit(): void { this.profileForm = new FormGroup({ phone: new FormControl('', [this.mobileValidator]) } ); } public mobileValidator = (control: FormControl): any => { const value = control.value; // 手规号码正则 const mobileReg = /^0?(13[0-9]|15[012356789]|17[013678]|18[0-9]|14[57])[0-9]{ 8} $/; const result = mobileReg.test(value); // 有效值返回null, 无效时返回验证的错误对象,验证对象是名为mobileFormat的验证秘钥属性。值一般可以是任意值 // mobileFormat 用于在模板中获取错误信息的时候会被再次使用 return result ? null : { mobileFormat: { info: '手机格式不正确'} } ; } // 在模板中的使用 form [formGroup]="profileForm" (ngSubmit)="submitForm(profileForm.value)"> input id="phone" type="text" formControlName="phone"> div> { { profileForm?.get('phone').errors | json} } /div> // 可以将在自定义函数中定义的报错信息打印出来 div [hidden]="profileForm?.get('phone').valid || profileForm?.get('phone')?.untouched"> p [hidden]="!profileForm.hasError('mobileFormat', 'phone')"> 手机格式不正确/p> /div> button> 提交/button> /form>
如何更新表单数据
- 使用
setValue()
方法来为单个控件设置新值。 setValue() 方法会严格遵循表单组的结构,并整体性
替换控件的值。 - 使用
patchValue()
方法可以用对象中所定义的任何属性为表单模型进行替换。
使用FormBuilder服务生成控件
FormBuilder 服务提供了一些{ 便捷方法} 便捷方法来生成表单控件。FormBuilder 在幕后也使用同样的方式来创建和返回这些实例,只是用起来更简单。
服务有三个方法:control()、group() 和 array()。这些方法都是工厂方法,用于在组件类中分别生成 FormControl、FormGroup 和 FormArray。
1.在组件中引入FormBuilder类,并注入服务
import { FormBuilder } from '@angular/forms'; constructor(private fb: FormBuilder) { }
2.生成表单内容
this.profileForm = this.fb.group({ name: [''], password: [''] } ); // 等同于 this.profileForm = new FormGroup({ name: new FormControl(''), password: new FormControl(''),} );
跨字段交叉验证(仅了解)
const heroForm = new FormGroup({ 'name': new FormControl(), 'alterEgo': new FormControl(), 'power': new FormControl()} , { validators: identityrevealedValidator } ); export const identityRevealedValidator: ValidatorFn = (control: AbstractControl): ValidationErrors | null => { const name = control.get('name'); const alterEgo = control.get('alterEgo'); return name & & alterEgo & & name.value === alterEgo.value ? { identityRevealed: true } : null; } ;
更多编程相关知识,请访问:编程视频!!
以上就是Angular进阶学习之深入了解路由和表单的详细内容,更多请关注其它相关文章!
声明:本文内容由网友自发贡献,本站不承担相应法律责任。对本内容有异议或投诉,请联系2913721942#qq.com核实处理,我们将尽快回复您,谢谢合作!
若转载请注明出处: Angular进阶学习之深入了解路由和表单
本文地址: https://pptw.com/jishu/593378.html