Skip to content

12 Route

12.1 Overview

在 Angular 中,路由是以模块为单位的,每个模块都可以有自己的路由。

12.2 快速上手

  1. 创建页面组件、Layout 组件以及 Navigation 组件,供路由使用
  2. 创建首页页面组件 ng g c pages/home
  3. 创建关于我们页面组件 ng g c pages/about
  4. 创建布局组件 ng g c pages/layout
  5. 创建导航组件 ng g c pages/navigation

  6. 创建路由规则

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';


const routes: Routes = [
  {
    path: "home",
    component: HomeComponent
  },
  {
    path: "about",
    component: AboutComponent
  }

];
  1. 引入路由模块并启动
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';


@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }
  1. 添加路由插座
<!-- 路由插座即占位组件 匹配到的路由组件将会显示在这个地方-->
<router-outlet></router-outlet>
  1. 在导航组件中定义链接
    <a routerLink="">首页</a>
    <a routerLink="/about">关于我们</a>
    

src/app/app-routing.module.ts

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { LifecycleOnDestoryComponent } from './components/lifecycle/lifecycle-on-destory/lifecycle-on-destory.component';
import { LifecycleOnInitComponent } from './components/lifecycle/lifecycle-on-init/lifecycle-on-init.component';
import { HomeComponent } from './components/route/home/home.component';
import { AboutComponent } from './components/route/about/about.component';

// const routes: Routes = [
//   //--- test for lifecycle-destroy--
//   {
//     path: '', // 根路径
//     component: LifecycleOnDestoryComponent, // 显示 HomeComponent
//     pathMatch: 'full'
//   },
//   {
//     path: 'LifecycleOnInitComponent', // 显示 AboutComponent
//     component: LifecycleOnInitComponent
//   }
//   //--- test for lifecycle-destroy--

// ];



// route 
const routes: Routes = [
  {
    path: 'home', // 根路径
    component: HomeComponent,
  },
  {
    path: 'about', // 显示 AboutComponent
    component: AboutComponent
  }

];

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

src/app/app.component.html:

<!-- route -->
<router-outlet></router-outlet>
<!-- router-outlet是一个指令,用来标记路由组件的位置 -->

src/app/components/route/navigation/navigation.component.html:

<p>navigation works!</p>

<div>
    <a routerLink="/home">home</a>
    <br>
    <a routerLink="/about">about</a>
</div>

src/app/components/route/route-layout/route-layout.component.html:

<app-navigation></app-navigation>
<ng-content></ng-content>

src/app/components/route/home/home.component.html:

<p>home works!</p>
<app-route-layout>
    this is home
</app-route-layout>

src/app/components/route/about/about.component.html:

<p>about works!</p>
<app-route-layout>
    this is about
</app-route-layout>

Screenshot 2025-02-05 at 17.39.08

Screenshot 2025-02-05 at 17.39.21

Screenshot 2025-02-05 at 17.39.30

Screenshot 2025-02-05 at 17.43.24

src/app/app-routing.module.ts:

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { LifecycleOnDestoryComponent } from './components/lifecycle/lifecycle-on-destory/lifecycle-on-destory.component';
import { LifecycleOnInitComponent } from './components/lifecycle/lifecycle-on-init/lifecycle-on-init.component';
import { HomeComponent } from './components/route/home/home.component';
import { AboutComponent } from './components/route/about/about.component';

// const routes: Routes = [
//   //--- test for lifecycle-destroy--
//   {
//     path: '', // 根路径
//     component: LifecycleOnDestoryComponent, // 显示 HomeComponent
//     pathMatch: 'full'
//   },
//   {
//     path: 'LifecycleOnInitComponent', // 显示 AboutComponent
//     component: LifecycleOnInitComponent
//   }
//   //--- test for lifecycle-destroy--

// ];



// route 
const routes: Routes = [
  {
    path: 'home', // 根路径
    component: HomeComponent,
  },
  {
    path: 'about', // 显示 AboutComponent
    component: AboutComponent
  }

];

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

Screenshot 2025-02-05 at 17.45.20

Screenshot 2025-02-05 at 17.45.49

12.3 匹配规则

12.3.1 重定向

const routes: Routes = [
  {
    path: 'home', // 根路径
    component: HomeComponent,
  },
  {
    path: 'about', // 显示 AboutComponent
    component: AboutComponent
  },
  {
    path: '',
    // 重定向
    redirectTo: 'home',
    // 完全匹配
    pathMatch: 'full'
    // full是完全匹配,prefix是前缀匹配, 默认是prefix
  }

];

Screenshot 2025-02-05 at 18.51.23

12.3.2 404 页面

const routes: Routes = [
  {
    path: 'home', // 根路径
    component: HomeComponent,
  },
  {
    path: 'about', // 显示 AboutComponent
    component: AboutComponent
  },
  {
    path: '**',
    component: NotFoundComponent
  },
  {
    path: '',
    // 重定向
    redirectTo: 'home',
    // 完全匹配
    pathMatch: 'full'
    // full是完全匹配,prefix是前缀匹配, 默认是prefix
  }

];

Screenshot 2025-02-05 at 18.51.36

12.4 Passing Parameters in Routing

12.4.1 查询参数

src/app/components/route/navigation/navigation.component.html:

<p>navigation works!</p>

<div>
    <a routerLink="/home">home</a>
    <br>
    <a routerLink="/about" [queryParams]="{ name: 'sam' }">about</a>
</div>

src/app/components/route/about/about.component.ts

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

@Component({
  selector: 'app-about',
  templateUrl: './about.component.html',
  styleUrls: ['./about.component.css']
})
export class AboutComponent implements OnInit {
  constructor(private route: ActivatedRoute) { }


  ngOnInit() {
    this.route.queryParamMap.subscribe(query => {
      console.log(query.get('name'));
    })
  }
}

Screenshot 2025-02-05 at 19.23.18

Screenshot 2025-02-05 at 19.24.37

12.4.2 动态参数

src/app/app-routing.module.ts:

const routes: Routes = [
  {
    path: 'home', // 根路径
    component: HomeComponent,
  },
  {
    path: 'about/:name', // 显示 AboutComponent
    component: AboutComponent
  },
  {
    path: '**',
    component: NotFoundComponent
  },
  {
    path: '',
    // 重定向
    redirectTo: 'home',
    // 完全匹配
    pathMatch: 'full'
    // full是完全匹配,prefix是前缀匹配, 默认是prefix
  }

];

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

src/app/components/route/navigation/navigation.component.html:

<p>navigation works!</p>

<div>
    <a routerLink="/home">home</a>
    <br>
    <!-- <a routerLink="/about" [queryParams]="{ name: 'sam' }">about</a> -->
    <a [routerLink]="['/about','joe']">about</a>
</div>

src/app/components/route/about/about.component.ts:

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

@Component({
  selector: 'app-about',
  templateUrl: './about.component.html',
  styleUrls: ['./about.component.css']
})
export class AboutComponent implements OnInit {
  constructor(private route: ActivatedRoute) { }


  ngOnInit() {
    // this.route.queryParamMap.subscribe(query => {
    //   console.log(query.get('name'));
    // })

    this.route.paramMap.subscribe(query => {
      console.log(query.get('name'));
    })
  }
}

Screenshot 2025-02-05 at 19.29.18

Screenshot 2025-02-05 at 19.29.23

12.5 路由嵌套

路由嵌套指的是如何定义子级路由。

src/app/app-routing.module.ts:

const routes: Routes = [
  {
    path: 'home', // 根路径
    component: HomeComponent,
  },
  {
    path: 'about/:name', // 显示 AboutComponent
    component: AboutComponent
  },
  {
    path: '',
    // 重定向
    redirectTo: 'home',
    // 完全匹配
    pathMatch: 'full'
    // full是完全匹配,prefix是前缀匹配, 默认是prefix
  },
  {
    path: 'news',
    component: NewsComponent,
    children: [
      {
        path: "company",
        component: CompanyComponent
      },
      {
        path: "industry",
        component: IndustryComponent
      }
    ]
  },
  {
    path: '**',
    component: NotFoundComponent
  },
  // order is important
];

src/app/components/route/news/news.component.html:

<app-route-layout>
    <p>news works!</p>
    <div>
        <a routerLink="/news/company">Company News</a>
        <br>
        <a routerLink="/news/industry">Industry News</a>
    </div>
    <router-outlet></router-outlet>
</app-route-layout>

Screenshot 2025-02-05 at 19.52.57

Screenshot 2025-02-05 at 19.53.24

12.6 命名插座

进入到news, 同时显示company, industry news而不是跳转显示.

将子级路由组件显示到不同的路由插座中.

src/app/app-routing.module.ts:

// route 
const routes: Routes = [
  {
    path: 'home', // 根路径
    component: HomeComponent,
  },
  {
    path: 'about/:name', // 显示 AboutComponent
    component: AboutComponent
  },
  {
    path: '',
    // 重定向
    redirectTo: 'home',
    // 完全匹配
    pathMatch: 'full'
    // full是完全匹配,prefix是前缀匹配, 默认是prefix
  },
  {
    path: 'news',
    component: NewsComponent,
    children: [
      {
        path: "company",
        component: CompanyComponent,
        outlet: "left"
      },
      {
        path: "industry",
        component: IndustryComponent,
        outlet: "right"
      }
    ]
  },
  {
    path: '**',
    component: NotFoundComponent
  },
  // order is important
];

src/app/components/route/news/news.component.html:

<app-route-layout>
    <p>news works!</p>
    <div>
        <router-outlet name="left"></router-outlet>
    </div>
    <div>
        <router-outlet name="right"></router-outlet>
    </div>

</app-route-layout>

src/app/components/route/navigation/navigation.component.html:

<p>navigation works!</p>

<div>
    <a routerLink="/home">home</a>
    <br>
    <!-- <a routerLink="/about" [queryParams]="{ name: 'sam' }">about</a> -->
    <a [routerLink]="['/about','joe']">about</a>
    <br>
    <!-- <a routerLink="/news">News</a> -->
    <a [routerLink]="['/news', {outlets: {
        left: ['company'],
        right: ['industry']
     } }]">News</a>
</div>

Screenshot 2025-02-05 at 20.14.51

12.7 导航路由

src/app/components/route/home/home.component.html:

<!-- <app-layout>this is home</app-layout> -->
<p>home works!</p>
<app-route-layout>
    <button (click)="jump()">Jump</button>
</app-route-layout>

src/app/components/route/home/home.component.ts:

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

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {

  constructor(private router: Router) { }

  ngOnInit(): void {
  }

  jump() {
    this.router.navigate(['/about', 'zhangsan'], {
      queryParams: {
        age: 18
      }
    });
  }

}

Screenshot 2025-02-05 at 20.21.51

Screenshot 2025-02-05 at 20.21.59

Screenshot 2025-02-05 at 20.21.31

12.8 路由模块

ng g m appRouting --flat=true

会放在app文件夹下面, 而不是单独创建一个文件

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { LifecycleOnDestoryComponent } from './components/lifecycle/lifecycle-on-destory/lifecycle-on-destory.component';
import { LifecycleOnInitComponent } from './components/lifecycle/lifecycle-on-init/lifecycle-on-init.component';
import { HomeComponent } from './components/route/home/home.component';
import { AboutComponent } from './components/route/about/about.component';
import { NotFoundError } from 'rxjs';
import { NotFoundComponent } from './components/route/not-found/not-found.component';
import { NewsComponent } from './components/route/news/news.component';
import { CompanyComponent } from './components/route/company/company.component';
import { IndustryComponent } from './components/route/industry/industry.component';

// const routes: Routes = [
//   //--- test for lifecycle-destroy--
//   {
//     path: '', // 根路径
//     component: LifecycleOnDestoryComponent, // 显示 HomeComponent
//     pathMatch: 'full'
//   },
//   {
//     path: 'LifecycleOnInitComponent', // 显示 AboutComponent
//     component: LifecycleOnInitComponent
//   }
//   //--- test for lifecycle-destroy--

// ];



// route 
const routes: Routes = [
  {
    path: 'home', // 根路径
    component: HomeComponent,
  },
  {
    path: 'about/:name', // 显示 AboutComponent
    component: AboutComponent
  },
  {
    path: '',
    // 重定向
    redirectTo: 'home',
    // 完全匹配
    pathMatch: 'full'
    // full是完全匹配,prefix是前缀匹配, 默认是prefix
  },
  {
    path: 'news',
    component: NewsComponent,
    children: [
      {
        path: "company",
        component: CompanyComponent,
        outlet: "left"
      },
      {
        path: "industry",
        component: IndustryComponent,
        outlet: "right"
      }
    ]
  },
  {
    path: '**',
    component: NotFoundComponent
  },
  // order is important
];

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

src/app/app.module.ts:

// BrowserModule 提供了启动和运行浏览器应用所必需的服务
// CommonModule 提供各种服务和指令,例如 ngIf 和 ngFor,与平台无关
// BrowserModule 导入了 ComonModule,又重新导出了 ComonModule,使其所有指令都可用于导入BrowserModule 的任何模块
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
// NgModule: Angular 模块装饰器,用于定义模块
import { AppRoutingModule } from './app-routing.module';
// 根组件
import { AppComponent } from './app.component';
import { SharedModule } from './shared/shared.module';
import { DataBindingComponent } from './components/component-template/data-binding/data-binding.component';
import { PropertyBindingComponent } from './components/component-template/property-binding/property-binding.component';
import { EventBindingComponent } from './components/component-template/event-binding/event-binding.component';
import { AccessNativeDomObjectsComponent } from './components/component-template/access-native-dom-objects/access-native-dom-objects.component';
import { TwoWayDataBindingComponent } from './components/component-template/two-way-data-binding/two-way-data-binding.component';
import { FormsModule } from '@angular/forms';
import { ContentProjectionComponent } from './components/component-template/content-projection/content-projection.component';
import { ErrorHandlingComponent } from './components/component-template/error-handling/error-handling.component';
import { GlobalStylesComponent } from './components/component-template/global-styles/global-styles.component';
import { StructuralDirectivesComponent } from './components/directive/build-in/structural-directives/structural-directives.component';
import { AttributeDirectivesComponent } from './components/directive/build-in/attribute-directives/attribute-directives.component';
import { CustomDirective } from './components/directive/custom/custom.directive';
import { PipeComponent } from './components/pipe/pipe/pipe.component';
import { CustomPipe } from './components/pipe/custom/custom.pipe';
import { ChildComponent } from './components/communication/child/child.component';
import { LifecycleOnInitComponent } from './components/lifecycle/lifecycle-on-init/lifecycle-on-init.component';
import { LifecycleOnUpdateComponent } from './components/lifecycle/lifecycle-on-update/lifecycle-on-update.component';
import { LifecycleOnDestoryComponent } from './components/lifecycle/lifecycle-on-destory/lifecycle-on-destory.component';
import { HomeComponent } from './components/route/home/home.component';
import { AboutComponent } from './components/route/about/about.component';
import { NavigationComponent } from './components/route/navigation/navigation.component';
import { RouteLayoutComponent } from './components/route/route-layout/route-layout.component';
import { NotFoundComponent } from './components/route/not-found/not-found.component';
import { NewsComponent } from './components/route/news/news.component';
import { CompanyComponent } from './components/route/company/company.component';
import { IndustryComponent } from './components/route/industry/industry.component';

// 调用NgModule装饰器, 告诉Angular 当前类表示的是Angular模块
@NgModule({
  // 声明当前模块拥有哪些组建
  declarations: [
    AppComponent,
    DataBindingComponent,
    PropertyBindingComponent,
    EventBindingComponent,
    AccessNativeDomObjectsComponent,
    TwoWayDataBindingComponent,
    ContentProjectionComponent,
    ErrorHandlingComponent,
    GlobalStylesComponent,
    StructuralDirectivesComponent,
    AttributeDirectivesComponent,
    CustomDirective,
    PipeComponent,
    CustomPipe,
    ChildComponent,
    LifecycleOnInitComponent,
    LifecycleOnUpdateComponent,
    LifecycleOnDestoryComponent,
    HomeComponent,
    AboutComponent,
    NavigationComponent,
    RouteLayoutComponent,
    NotFoundComponent,
    NewsComponent,
    CompanyComponent,
    IndustryComponent, // 属于当前模块的组件
  ],
  // 声明当前模块依赖了哪些其他模块
  imports: [
    BrowserModule,
    AppRoutingModule,
    SharedModule,
    FormsModule
  ],
  // 声明服务的作用域, 数组中接收服务类, 表示该服务只能在当前模块的组件中使用
  providers: [],
  // 可引导组件, Angular 会在引导过程中把它加载到DOM中
  bootstrap: [AppComponent],
})
export class AppModule { }

12.9 路由懒加载

路由懒加载是以模块为单位的。

  1. 创建用户模块 ng g m user --routing=true 一并创建该模块的路由模块

  2. 创建登录页面组件 ng g c user/pages/1ogin

  3. 创建注册页面组件 ng g c user/pages/register

  4. 配置户模块的路由规则

src/app/components/route/user/user-routing.module.ts:

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { LoginComponent } from './pages/login/login.component';

const routes: Routes = [
  {
    path: "login",
    component: LoginComponent
  }
];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})
export class UserRoutingModule { }
  1. 将用户路由模块关联到主路由模块

src/app/app-routing.module.ts:

// route 
const routes: Routes = [
  {
    path: 'home', // 根路径
    component: HomeComponent,
  },
  {
    path: 'about/:name', // 显示 AboutComponent
    component: AboutComponent
  },
  {
    path: '',
    // 重定向
    redirectTo: 'home',
    // 完全匹配
    pathMatch: 'full'
    // full是完全匹配,prefix是前缀匹配, 默认是prefix
  },
  {
    path: 'news',
    component: NewsComponent,
    children: [
      {
        path: "company",
        component: CompanyComponent,
        outlet: "left"
      },
      {
        path: "industry",
        component: IndustryComponent,
        outlet: "right"
      }
    ]
  },
  {
    path: "user",
    loadChildren: () => import('./components/route/user/user.module').then(m => {
      console.log(m);
      return m.UserModule
    })
  },
  {
    path: '**',
    component: NotFoundComponent
  },
  // order is important
];
  1. 在导航组件中添加访问链接

src/app/components/route/navigation/navigation.component.html:

<p>navigation works!</p>

<div>
    <a routerLink="/home">home</a>
    <br>
    <!-- <a routerLink="/about" [queryParams]="{ name: 'sam' }">about</a> -->
    <a [routerLink]="['/about','joe']">about</a>
    <br>
    <!-- <a routerLink="/news">News</a> -->
    <a [routerLink]="['/news', {outlets: {
        left: ['company'],
        right: ['industry']
     } }]">News</a>

     <a routerLink="/user/login">login</a>
</div>

Screenshot 2025-02-05 at 20.50.59

Screenshot 2025-02-05 at 20.51.07

Screenshot 2025-02-05 at 20.50.50

12.10 路由守卫

路由守卫会告诉路由是否允许导航到请求的路由。

路由守方法可以返回 boolean 或 Observable 或 Promise ,它们在将来的某个时间点解析为布尔值。

import { Injectable } from "@angular/core"

import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree, Router } from

"@angular/router"

import { Observable } from "rxjs"

@Injectable({

providedIn: "root"

})

export class AuthGuard implements CanActivate {

constructor(private router: Router) {}

canActivate(): boolean | UrlTree {

1/ 用于实现跳转

return this.router.createUrlTree(["/user/login"] ])

11 禁止访问目标路由

return false

11 允许访问目标路由

12.10.1 CanActivate

检查用户是否可以访问某一个路由。

CanActivate 为接口,路由守卫类要实现该接口,该接口规定类中需要有 canActivate 方法,方法决定是否允许访问目标路由。

路由可以应用多个守卫,所有守卫方法都允许,路由才被允许访问,有一个守卫方法不允许,则路由不允许被访问。

创建路由守卫:ng g quard guards/auth

Screenshot 2025-02-05 at 21.01.55

src/app/components/route/auth-gard.guard.ts

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

@Injectable({
  providedIn: 'root'
})
export class AuthGardGuard implements CanActivate { // 这个CanActivate是一个接口,它有一个方法canActivate,这个方法返回一个boolean值,表示是否可以激活路由
  // 注意大小写,这个接口的名字是CanActivate,而不是canActivate
  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
    return true;
  }

}

改成false就无法访问了

Screenshot 2025-02-05 at 21.03.23

点击about就跳回home

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

@Injectable({
  providedIn: 'root'
})
export class AuthGardGuard implements CanActivate { // 这个CanActivate是一个接口,它有一个方法canActivate,这个方法返回一个boolean值,表示是否可以激活路由
  // 注意大小写,这个接口的名字是CanActivate,而不是canActivate

  constructor(private router: Router) { }
  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
    return this.router.createUrlTree(['/home']);
    // return true;
  }

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

@Injectable({
  providedIn: 'root'
})
export class AuthGardGuard implements CanActivate { // 这个CanActivate是一个接口,它有一个方法canActivate,这个方法返回一个boolean值,表示是否可以激活路由
  // 注意大小写,这个接口的名字是CanActivate,而不是canActivate

  constructor(private router: Router) { }
  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
    // return this.router.createUrlTree(['/home']);
    // // return true;

    console.log(route); // 待激活路由快照
    return false;
  }

}

Screenshot 2025-02-05 at 21.07.23

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

@Injectable({
  providedIn: 'root'
})
export class AuthGardGuard implements CanActivate { // 这个CanActivate是一个接口,它有一个方法canActivate,这个方法返回一个boolean值,表示是否可以激活路由
  // 注意大小写,这个接口的名字是CanActivate,而不是canActivate

  constructor(private router: Router) { }
  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
    // return this.router.createUrlTree(['/home']);
    // // return true;

    console.log(state);
    return false;
  }

}

Screenshot 2025-02-05 at 21.08.28

12.10.2 CanActivateChild

检查用户是否方可访问某个子路由。

创建路由守卫:ng g guard guards/admin 注意:选择 CanActivateChild,需要将箭头移动到这个选项并且敲击空格确认选择。

src/app/components/route/user/user-routing.module.ts:

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { LoginComponent } from './pages/login/login.component';
import { UserComponent } from './pages/user/user.component';
import { GuardCanActivateChildGuard } from '../guards/guard-can-activate-child.guard';

const routes: Routes = [
  {
    path: "login",
    component: LoginComponent,
    canActivateChild: [GuardCanActivateChildGuard],
    children: [
      {
        path: "user",
        component: UserComponent,
      }
    ]
  }
];

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

src/app/components/route/guards/guard-can-activate-child.guard.ts:

import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivateChild, RouterStateSnapshot, UrlTree } from '@angular/router';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class GuardCanActivateChildGuard implements CanActivateChild {
  canActivateChild(
    childRoute: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
    return false;
  }

}

src/app/components/route/user/pages/login/login.component.html:

<p>login works!</p>
<a routerLink="/user/login/user">user</a>
<router-outlet></router-outlet>

Screenshot 2025-02-09 at 15.58.42

Screenshot 2025-02-09 at 15.59.59

12.10.3 CanDeactivate

检查用户是否可以退出路由。比如用户在表单中输入的内容没有保存,用户又要离开路由,此时可以调用该守卫提示用户。

src/app/components/route/guards/guard-can-deactivate.guard.ts:

import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanDeactivate, RouterStateSnapshot, UrlTree } from '@angular/router';
import { Observable } from 'rxjs';

export interface canLeave {
  canLeave: () => boolean;
}

@Injectable({
  providedIn: 'root'
})
export class GuardCanDeactivateGuard implements CanDeactivate<canLeave> {
  canDeactivate(
    component: canLeave,
    currentRoute: ActivatedRouteSnapshot,
    currentState: RouterStateSnapshot,
    nextState?: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
    if (component.canLeave()) {
      return true;
    } else {
      if (confirm("Are you sure you want to leave?")) {
        return true;
      } else {
        return false;
      }
    }
  }

}

src/app/components/route/guards/guard-can-deactivate/guard-can-deactivate.component.ts:

import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';

@Component({
  selector: 'app-guard-can-deactivate',
  templateUrl: './guard-can-deactivate.component.html',
  styleUrls: ['./guard-can-deactivate.component.css']
})
export class GuardCanDeactivateComponent implements OnInit {

  constructor() { }

  ngOnInit(): void {
  }

  form: FormGroup = new FormGroup({
    username: new FormControl()
  })

  canLeave() {
    return !this.form.get('username')!.dirty;
  }

}

src/app/components/route/guards/guard-can-deactivate/guard-can-deactivate.component.html:

<p>guard-can-deactivate works!</p>
<form [formGroup]="form">
    <input type=" text" formControlName="username" />
    <button>submit</button>
</form>

<app-navigation></app-navigation>

src/app/components/route/navigation/navigation.component.html:

<p>navigation works!</p>

<div>
    <a routerLink="/home">home</a>
    <br>
    <!-- <a routerLink="/about" [queryParams]="{ name: 'sam' }">about</a> -->
    <a [routerLink]="['/about','joe']">about</a>
    <br>
    <!-- <a routerLink="/news">News</a> -->
    <a [routerLink]="['/news', {outlets: {
        left: ['company'],
        right: ['industry']
     } }]">News</a>
    <br>
    <a routerLink="/user/login">login</a>
    <br>
    <a routerLink="/candeactivate">guard-can-deactivate</a>
</div>

src/app/app-routing.module.ts:

  {
    path: "candeactivate",
    component: GuardCanDeactivateComponent,
    canDeactivate: [GuardCanDeactivateGuard]
  },

Screenshot 2025-02-09 at 16.32.56

Screenshot 2025-02-09 at 16.33.08

12.10.4 Resolve

允许在进入路由之前先获取数据,待数据获取完成之后再进入路由。

ng g resolver <name>

防止在网速比较的慢的情况下, 用户直接进入这个路由看到却是一个空的路由组件. 给用户错觉的这个页面出bug了.

src/app/components/route/resolver/get-name.resolver.ts:

import { Injectable } from '@angular/core';
import {
  Router, Resolve,
  RouterStateSnapshot,
  ActivatedRouteSnapshot
} from '@angular/router';
import { Observable, of } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class GetNameResolver implements Resolve<String> {
  resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<String> {
    return new Promise(function (resolve) {
      setTimeout(function () {
        resolve("Name from resolver");
      }, 3000);
    })
  }
}

src/app/components/route/home/home.component.ts:

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

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {

  constructor(private router: Router, private route: ActivatedRoute) { }

  ngOnInit(): void {
    console.log(this.route.snapshot.data['name']);
  }

  jump() {
    this.router.navigate(['/about', 'zhangsan'], {
      queryParams: {
        age: 18
      }
    });
  }

}

src/app/app-routing.module.ts:

// route 
const routes: Routes = [
  {
    path: 'home', // 根路径
    component: HomeComponent,
    resolve: {
      name: GetNameResolver
    }
  },

src/app/components/route/user/pages/login/login.component.html:

<p>login works!</p>
<a routerLink="/user/login/user">user</a>
<br>
<a routerLink="/home">home</a>
<router-outlet></router-outlet>

Screenshot 2025-02-09 at 16.44.23

Screenshot 2025-02-09 at 16.46.35

click home, 2s later

Screenshot 2025-02-09 at 16.47.00