我是靠谱客的博主 聪慧小伙,最近开发中收集的这篇文章主要介绍Angular进阶学习之深入了解路由和表单,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

本篇文章是Angular的进阶学习,我们一起来详细了解一下Angular中的路由和表单,希望对大家有所帮助!

Angular的路由介绍

在单页面应用中,需要在定义好的不同视图中(组件)来回切换,而不用去服务器中获取新页面,要实现从一个视图到另外一个视图的导航,就需要使用到Angular中的Router。【相关教程推荐:《angular教程》】

路由创建

1、新建文件app-routing.module.ts,将要跳转的视图配置放到里边

import { NgModule } from '@angular/core';
// 引入路由模块 RouterModule和 Routes
import { 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 {}
登录后复制

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>  <!--此指令通过路由来动态加载组件-->
登录后复制

路由中两个重要API的介绍

ActivatedRoute

用于{获取路由信息},它所包含路由的信息比如: 路由参数,静态数据...具体提供的方法可参照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);
    });
  }

}
登录后复制

Router

是一个{提供导航和操纵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传递多个参数的时候,可以选择用这种方式进行路由传参

路由中传参的3种具体方法

1. routerLink

单一参数:

 <a [routerLink]="['/goodsList', id]" routerLinkActive="active-class">切换到商品列表组件</a>
 // 其中/goodsListl是我设置的路由path,id是需要传递的参数   
 // 多个参数的时候,也可以用这种形式,只不过看起来不够直观,所以不推荐多个参数的时候也用这种方法
登录后复制

多个参数:

 <a [routerLink]="['/personalCenter']" [queryParams]="{name: 'zs', age: 16}">切换到个人中心组件</a>
 // 参数的格式可以自行组织成各种object
登录后复制

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

// 传的第一个参数是数组的形式,而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}是一个用来{指定路由匹配策略}的字符串。可选项有 prefix(默认值)和 full。

路由顺序

Router在匹配路由的时候,使用的是”{先到先得}“的策略,所以应当将{具体的静态的路由}放前边,然后可以放置{默认路由匹配}的空路径路由,{通配符}路由是最后一个(他可以匹配所以路由,当其他路由都没有匹配对的时候,Router才会选择他)

const routes: Routes = [
  // 静态路由
  {
    path: 'goodsList/:id',
    component: GoodsListComponent,
  },
  {
    path: 'personalCenter', 
    component: PersonalCenterComponent,
  },
  // 默认空路径路由
  {
    path: '',
    redirectTo: '/personalCenter',
    pathMatch: 'full'
  },
  // 通配符路由
  { path: '**', component: PageNotFoundComponent },
];
登录后复制

路由嵌套

当应用变的复杂的时候,可能需要创建一些根组件之外的相对路由,这些路由成为子路由,这意味着在项目中需要添加第二个 {<router-outlet>},因为它是 AppComponent 之外的另一个 {<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>
登录后复制

路由懒加载

你可以配置路由定义来实现惰性加载模块,这意味着 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 属性发出导航事件。这些事件的范围贯穿从导航开始和结束之间的多个时间点。想了解更多请移步

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
  • pristine 和 dirty
  • valid 和 invalid
控件状态的CSS 类

Angular 会自动把很多{控件属性}作为 {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、把这个验证器添加到表单中的相应字段。

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>
登录后复制
自定义表单验证器
 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>
登录后复制

如何更新表单数据

使用FormBuilder服务生成控件

FormBuilder 服务提供了一些{便捷方法}来生成表单控件。FormBuilder 在幕后也使用同样的方式来创建和返回这些实例,只是用起来更简单。

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进阶学习之深入了解路由和表单的全部内容,希望文章能够帮你解决Angular进阶学习之深入了解路由和表单所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(70)

评论列表共有 0 条评论

立即
投稿
返回
顶部