MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Angular路由的路由参数传递与获取

2021-11-251.7k 阅读

Angular路由基础回顾

在深入探讨 Angular 路由参数的传递与获取之前,先来简单回顾一下 Angular 路由的基本概念。Angular 路由是其框架中一个重要的特性,它允许我们在单页应用(SPA)中实现页面的导航和切换,就如同传统多页应用中的页面跳转一样,只不过这一切都在一个 HTML 页面内完成。

我们通过配置路由模块来定义应用中的各个路由路径及其对应的组件。例如,一个简单的路由配置可能如下:

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { HomeComponent } from './home/home.component';
import { AboutComponent } from './about/about.component';

const routes: Routes = [
  { path: 'home', component: HomeComponent },
  { path: 'about', component: AboutComponent }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

在上述代码中,我们定义了两个路由,/home 路径对应 HomeComponent 组件,/about 路径对应 AboutComponent 组件。当用户在浏览器地址栏输入相应路径时,Angular 会将对应的组件渲染到指定的 <router - outlet> 位置。

路由参数的类型

Angular 路由参数主要分为两种类型:路径参数(Path Parameters)和查询参数(Query Parameters)。

路径参数(Path Parameters)

路径参数是嵌入在路由路径中的参数。这种参数常用于标识特定的资源或实体。例如,我们有一个展示用户详情的页面,每个用户有唯一的 id,我们可以通过路径参数来传递这个 id。路由配置如下:

const routes: Routes = [
  { path: 'user/:id', component: UserComponent }
];

在上述配置中,:id 就是路径参数的占位符。当我们导航到 /user/123 时,123 就是 id 参数的值。

查询参数(Query Parameters)

查询参数通常用于传递一些可选的、临时性的信息,它们附加在路由路径的末尾,以 ? 开头,多个参数之间用 & 分隔。例如,我们有一个搜索页面,用户可以通过搜索关键词来筛选结果,这个搜索关键词就可以作为查询参数传递。路由路径可能是这样的:/search?keyword=angular。在 Angular 中,查询参数在路由配置中并没有显式的占位符定义,而是在导航或获取时进行处理。

路径参数的传递

在模板中传递路径参数

在 Angular 模板中,我们可以使用 routerLink 指令来导航到包含路径参数的路由。假设我们有一个用户列表组件,列表中的每个用户项都有一个链接,点击链接可以跳转到对应的用户详情页,传递用户的 id 作为路径参数。

<ul>
  <li *ngFor="let user of users">
    <a [routerLink]="['/user', user.id]">{{ user.name }}</a>
  </li>
</ul>

在上述代码中,['/user', user.id] 表示导航到 /user 路由,并将 user.id 作为路径参数传递。这里 routerLink 接受一个数组,数组的第一个元素是路由路径,后续元素就是路径参数的值。

在组件代码中传递路径参数

除了在模板中传递路径参数,我们也可以在组件的 TypeScript 代码中通过 Router 服务来导航并传递路径参数。假设我们有一个按钮,点击按钮跳转到用户详情页,代码如下:

import { Component } from '@angular/core';
import { Router } from '@angular/router';

@Component({
  selector: 'app - user - list',
  templateUrl: './user - list.component.html',
  styleUrls: ['./user - list.component.css']
})
export class UserListComponent {
  users = [
    { id: 1, name: 'Alice' },
    { id: 2, name: 'Bob' }
  ];

  constructor(private router: Router) { }

  goToUserDetail(user) {
    this.router.navigate(['/user', user.id]);
  }
}

goToUserDetail 方法中,我们调用 router.navigate 方法,传入与模板中类似的数组,实现带路径参数的导航。

路径参数的获取

在组件中获取路径参数

当导航到包含路径参数的路由时,组件需要获取这些参数来进行相应的操作,比如根据 id 获取用户详情。在 Angular 中,我们可以通过 ActivatedRoute 服务来获取路径参数。

import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';

@Component({
  selector: 'app - user',
  templateUrl: './user.component.html',
  styleUrls: ['./user.component.css']
})
export class UserComponent implements OnInit {
  userId: number;

  constructor(private route: ActivatedRoute) { }

  ngOnInit() {
    this.route.params.subscribe(params => {
      this.userId = +params['id'];
      // 这里可以根据 userId 去后端获取用户详情
    });
  }
}

在上述代码中,this.route.params 是一个可观察对象(Observable),当路径参数发生变化时(例如用户通过浏览器的前进后退按钮切换到不同 id 的用户详情页),这个可观察对象会发出新的值。我们通过 subscribe 方法来订阅这个值,并从中获取 id 参数。注意,我们使用 + 将参数值转换为数字类型,因为从路径中获取的参数值默认是字符串类型。

使用快照获取路径参数

除了通过可观察对象来获取路径参数,我们还可以使用 ActivatedRoutesnapshot 属性来获取参数的快照。这种方式适用于参数在组件生命周期内不会发生变化的情况。

import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';

@Component({
  selector: 'app - user',
  templateUrl: './user.component.html',
  styleUrls: ['./user.component.css']
})
export class UserComponent implements OnInit {
  userId: number;

  constructor(private route: ActivatedRoute) { }

  ngOnInit() {
    this.userId = +this.route.snapshot.params['id'];
    // 这里可以根据 userId 去后端获取用户详情
  }
}

使用 snapshot 获取参数的优点是代码更简洁,不需要处理可观察对象。但缺点是当参数变化时,组件不会自动更新,需要手动处理参数变化的逻辑。

查询参数的传递

在模板中传递查询参数

在模板中传递查询参数同样使用 routerLink 指令,不过需要通过 queryParams 属性来指定查询参数。例如,我们有一个搜索按钮,点击按钮跳转到搜索结果页,并传递搜索关键词作为查询参数。

<input type="text" [(ngModel)]="searchKeyword" placeholder="输入搜索关键词">
<button [routerLink]="['/search']" [queryParams]="{ keyword: searchKeyword }">搜索</button>

在上述代码中,[queryParams]="{ keyword: searchKeyword }" 表示将 searchKeyword 的值作为 keyword 查询参数传递到 /search 路由。

在组件代码中传递查询参数

在组件的 TypeScript 代码中,我们可以通过 Router 服务的 navigate 方法传递查询参数。

import { Component } from '@angular/core';
import { Router } from '@angular/router';

@Component({
  selector: 'app - search - form',
  templateUrl: './search - form.component.html',
  styleUrls: ['./search - form.component.css']
})
export class SearchFormComponent {
  searchKeyword = '';

  constructor(private router: Router) { }

  search() {
    this.router.navigate(['/search'], { queryParams: { keyword: this.searchKeyword } });
  }
}

search 方法中,我们调用 router.navigate 方法,第一个参数是路由路径,第二个参数是一个对象,通过 queryParams 属性指定查询参数。

查询参数的获取

在组件中获取查询参数

在组件中获取查询参数同样依赖于 ActivatedRoute 服务。与获取路径参数类似,查询参数也可以通过可观察对象或快照的方式获取。

import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';

@Component({
  selector: 'app - search - result',
  templateUrl: './search - result.component.html',
  styleUrls: ['./search - result.component.css']
})
export class SearchResultComponent implements OnInit {
  keyword: string;

  constructor(private route: ActivatedRoute) { }

  ngOnInit() {
    this.route.queryParams.subscribe(params => {
      this.keyword = params['keyword'];
      // 这里可以根据 keyword 去后端获取搜索结果
    });
  }
}

在上述代码中,this.route.queryParams 是一个可观察对象,用于监听查询参数的变化。当查询参数发生变化时,subscribe 方法中的回调函数会被执行,我们可以从中获取 keyword 参数的值。

使用快照获取查询参数

同样,我们也可以使用 ActivatedRoutesnapshot 属性来获取查询参数的快照。

import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';

@Component({
  selector: 'app - search - result',
  templateUrl: './search - result.component.html',
  styleUrls: ['./search - result.component.css']
})
export class SearchResultComponent implements OnInit {
  keyword: string;

  constructor(private route: ActivatedRoute) { }

  ngOnInit() {
    this.keyword = this.route.snapshot.queryParams['keyword'];
    // 这里可以根据 keyword 去后端获取搜索结果
  }
}

使用快照获取查询参数适用于查询参数在组件生命周期内不会发生变化的场景,优点是代码简洁,缺点是无法自动响应参数变化。

路由参数传递与获取的高级应用

多个参数的传递与获取

在实际应用中,我们经常需要传递多个参数,无论是路径参数还是查询参数。例如,我们有一个订单详情页,不仅需要订单的 id,还可能需要店铺的 id 作为路径参数。路由配置如下:

const routes: Routes = [
  { path: 'order/:orderId/:storeId', component: OrderComponent }
];

在模板中传递参数:

<a [routerLink]="['/order', order.id, order.storeId]">查看订单详情</a>

在组件中获取参数:

import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';

@Component({
  selector: 'app - order',
  templateUrl: './order.component.html',
  styleUrls: ['./order.component.css']
})
export class OrderComponent implements OnInit {
  orderId: number;
  storeId: number;

  constructor(private route: ActivatedRoute) { }

  ngOnInit() {
    this.route.params.subscribe(params => {
      this.orderId = +params['orderId'];
      this.storeId = +params['storeId'];
      // 这里可以根据 orderId 和 storeId 去后端获取订单详情
    });
  }
}

对于查询参数,传递多个参数也很简单。例如:

<button [routerLink]="['/filter']" [queryParams]="{ category: 'electronics', priceRange: '100 - 500' }">筛选</button>

在组件中获取多个查询参数:

import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';

@Component({
  selector: 'app - filter - result',
  templateUrl: './filter - result.component.html',
  styleUrls: ['./filter - result.component.css']
})
export class FilterResultComponent implements OnInit {
  category: string;
  priceRange: string;

  constructor(private route: ActivatedRoute) { }

  ngOnInit() {
    this.route.queryParams.subscribe(params => {
      this.category = params['category'];
      this.priceRange = params['priceRange'];
      // 这里可以根据 category 和 priceRange 去后端获取筛选结果
    });
  }
}

参数变化的处理

有时候,我们需要在参数变化时执行一些特定的操作。例如,当用户在用户详情页通过切换标签改变路径参数 tab 时,组件需要重新加载相应的标签内容。

首先,在路由配置中定义带参数的路由:

const routes: Routes = [
  { path: 'user/:id/:tab', component: UserComponent }
];

在组件中监听参数变化:

import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';

@Component({
  selector: 'app - user',
  templateUrl: './user.component.html',
  styleUrls: ['./user.component.css']
})
export class UserComponent implements OnInit {
  userId: number;
  tab: string;

  constructor(private route: ActivatedRoute) { }

  ngOnInit() {
    this.route.params.subscribe(params => {
      this.userId = +params['id'];
      this.tab = params['tab'];
      this.loadTabContent();
    });
  }

  loadTabContent() {
    // 根据 this.tab 的值加载相应的标签内容
  }
}

这样,当路径参数 tab 发生变化时,subscribe 中的回调函数会被执行,从而调用 loadTabContent 方法重新加载内容。

相对路由与参数传递

在 Angular 中,我们还可以使用相对路由进行导航并传递参数。相对路由是相对于当前激活的路由而言的。例如,我们在 UserComponent 组件中有一个子路由 user/profile,我们可以在 UserComponent 的模板中使用相对路由导航到子路由并传递参数。

假设 UserComponent 的模板中有一个按钮:

<button [routerLink]="['profile', { userId: userId }]">查看个人资料</button>

这里 ['profile', { userId: userId }] 表示相对当前路由(UserComponent 的路由)导航到 profile 子路由,并传递 userId 参数。在 UserComponent 的子路由配置中,我们可以在子组件中获取这个参数。

const userRoutes: Routes = [
  { path: 'profile', component: UserProfileComponent }
];

@NgModule({
  imports: [RouterModule.forChild(userRoutes)],
  exports: [RouterModule]
})
export class UserRoutingModule { }

UserProfileComponent 中获取参数:

import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';

@Component({
  selector: 'app - user - profile',
  templateUrl: './user - profile.component.html',
  styleUrls: ['./user - profile.component.css']
})
export class UserProfileComponent implements OnInit {
  userId: number;

  constructor(private route: ActivatedRoute) { }

  ngOnInit() {
    this.route.parent.params.subscribe(params => {
      this.userId = +params['userId'];
      // 这里可以根据 userId 去后端获取用户资料
    });
  }
}

通过 this.route.parent.params 我们可以获取到父路由传递下来的参数,因为 UserProfileComponentUserComponent 的子组件。

路由参数与导航守卫

导航守卫是 Angular 路由中的一个强大功能,它可以在路由导航发生之前、之后或在特定条件下阻止或允许导航。路由参数在导航守卫中也有重要的应用。

基于参数的导航守卫

例如,我们有一个管理页面,只有管理员用户(通过 role 参数标识)才能访问。我们可以创建一个导航守卫来检查 role 参数。

首先,创建一个导航守卫:

import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router';

@Injectable({
  providedIn: 'root'
})
export class AdminGuard implements CanActivate {
  constructor(private router: Router) { }

  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): boolean {
    const role = route.params['role'];
    if (role === 'admin') {
      return true;
    } else {
      this.router.navigate(['/home']);
      return false;
    }
  }
}

在上述代码中,canActivate 方法会在导航到受保护的路由之前被调用。它从 ActivatedRouteSnapshot 中获取 role 参数,如果是 admin,则允许导航,否则导航到首页并阻止当前导航。

然后,在路由配置中使用这个导航守卫:

const routes: Routes = [
  { path:'manage/:role', component: ManageComponent, canActivate: [AdminGuard] }
];

这样,只有当 role 参数为 admin 时,用户才能导航到 manage 路由对应的 ManageComponent 组件。

导航守卫中处理参数变化

有时候,我们需要在导航守卫中处理参数变化。例如,当用户在管理页面切换不同的模块(通过路径参数标识)时,我们需要检查用户是否有权限访问新的模块。

import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router';

@Injectable({
  providedIn: 'root'
})
export class ModuleGuard implements CanActivate {
  constructor(private router: Router) { }

  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): boolean {
    const currentModule = route.params['module'];
    // 假设这里有一个检查权限的函数
    if (this.hasPermission(currentModule)) {
      return true;
    } else {
      this.router.navigate(['/manage', 'defaultModule']);
      return false;
    }
  }

  hasPermission(module: string): boolean {
    // 实际的权限检查逻辑
    return true;
  }
}

在上述代码中,canActivate 方法在每次导航到 manage 路由且参数 module 变化时都会被调用,根据权限检查结果决定是否允许导航。

总结与最佳实践

通过以上对 Angular 路由参数传递与获取的详细介绍,我们了解了路径参数和查询参数的传递与获取方式,以及它们在高级应用场景和导航守卫中的应用。

在实际开发中,有以下一些最佳实践:

  1. 合理选择参数类型:根据参数的性质和用途,合理选择路径参数或查询参数。如果参数是标识特定资源且是 URL 结构的一部分,使用路径参数;如果参数是可选的、临时性的筛选或配置信息,使用查询参数。
  2. 参数验证与处理:在获取参数后,一定要进行必要的验证和类型转换,确保参数的正确性和安全性。例如,将路径参数中的 id 转换为合适的数字类型。
  3. 处理参数变化:如果参数在组件生命周期内可能发生变化,要使用可观察对象(paramsqueryParams)来订阅参数变化,并在回调函数中执行相应的更新操作。
  4. 结合导航守卫:利用导航守卫根据路由参数进行权限控制、数据预加载等操作,确保应用的安全性和性能。

希望通过本文的介绍,你能熟练掌握 Angular 路由参数的传递与获取,从而开发出更加健壮和灵活的 Angular 应用。