Angular 18 上手开发

0x01 概述

(1)简介

  • 官方网站:https://www.angular.cn/
  • Angular 由 Google 的专业团队维护,Angular 提供了广泛的工具、API 和库,简化和优化开发工作流程
  • Angular 提供了一个坚实的平台,可用于构建快速、可靠、能够随着团队规模和代码库规模扩展的应用程序
  • 特点:
    • 整合性高,降低技术决策成本
    • 简化 DOM 操作,注重业务逻辑
    • 采用后端的依赖注入系统
  • 与 React、Vue 对比,Angular:
    • 在大型企业级应用中提供更成熟的框架和严格的架构模式,适合复杂项目的长期维护
    • 初始加载时间较长,不够灵活和轻量
    • 学习曲线较陡峭

(2)创建项目

Angular 18 于 2024 年 5 月正式发布,以下内容均采用 18.2.10 / 18.2.9 版本,NodeJS 采用 23.0.0 版本,npm 采用 10.9.0

  1. 使用命令 npm install -g @angular/cli 安装脚手架
  2. 使用命令 ng new angular-app 创建名为 angular-app 的项目
    1. 不向 Google 发送数据
    2. 不使用 CSS 预编译
    3. 不使用 SSR
  3. 使用命令 cd angular-app 进入项目目录
  4. 使用命令 ng serve --open 启动项目并自动开启浏览器

使用 ng 命令创建组件等:

脚手架 说明 命令
Component 组件 ng generate component my-new-component
Directive 指令 ng generate directive my-new-directive
Pipe 管道 ng generate pipe my-new-pipe
Service 服务 ng generate service my-new-service
Class ng generate class my-new-class
Interface 接口 ng generate interface my-new-interface
Enum 枚举 ng generate enum my-new-enum
Module 模块 ng generate module my-new-module

(3)项目结构

  • .angular:编译缓存目录

  • node_modules:Node 包目录

  • public:公共资源目录

  • src:代码资源目录

    • app:App 模块代码目录

      • app.component.css:组件样式文件
      • app.component.html:组件模板文件
      • app.component.spec.ts:组件测试文件
      • app.component.ts:组件入口文件
      • app.config.ts:模块配置文件
      • app.routes.ts:模块路由文件
    • index.html:模板文件

      <!doctype html>
      <html lang="en">
      <head>
        <meta charset="utf-8">
        <title>AngularApp</title>
        <base href="/">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="icon" type="image/x-icon" href="favicon.ico">
      </head>
      <body>
        <app-root></app-root>
      </body>
      </html>
      
    • main.ts:入口文件

      import { bootstrapApplication } from '@angular/platform-browser';
      import { appConfig } from './app/app.config';
      import { AppComponent } from './app/app.component';
      
      bootstrapApplication(AppComponent, appConfig)
        .catch((err) => console.error(err));
      
    • style.css:全局样式文件

  • .editorconfig:编辑器配置文件,详情参考 https://editorconfig.org

  • .gitignore:git 忽略文件

  • angular.json:Angular 配置文件

  • package.json:Node 包配置文件

    • "script" 项中添加以下内容:

      {
        "scripts": {
          "dev": "ng serve --open",
          // ...
        },
      }
      

      以后通过命令 npm run dev 来启动项目(代替命令 ng serve --open

  • tsconfig.json:TypeScript 配置文件

flowchart TB 项目-->模块1 & 模块2 & m[...] & 模块n 模块1-->组件1 & 组件2 & c[...] & 组件n

0x02 组件

(1)简介

以 app.component.ts 为例:

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

@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet],
templateUrl: './app.component.html',
styleUrl: './app.component.css'
})
export class AppComponent {
title = 'angular-app';
}
  • @Component 是一个装饰器,用于加载一些配置信息,其中

    • selector:选择器,用于选择模板中的 DOM 标签,并将该组件渲染至该标签中

      <!-- filename: index.html -->
      <body>
        <app-root></app-root>
      </body>
      
    • standalone:独立模式,true 表示该组件不依赖于任何模块,可以直接在应用中使用,而不需要通过模块进行声明

    • imports:导入组件,此处导入了 RouterOutlet 组件

      • RouterOutlet 是 Angular 路由模块中的一个重要组件,用于在应用中定义路由视图的占位符

        类似 React Router 中的 <Outlet />

    • templateUrl:模板文件路径

    • styleUrl:样式文件路径

  • 导出经过装饰器声明的类,类中可以传值

(2)创建组件

  1. 使用命令 ng generate component hello(或 ng g c hello)创建一个在 app 目录下,名为 hello 的新组件

  2. app 目录结构

    flowchart TB app-->hello & app.component.css & app.component.html & ... hello-->hello.component.css & hello.component.html & hello.component.spec.ts & hello.component.ts
  3. 修改 hello.component.html

    <div style="width: 200px; height: 200px; background-color: red">
      这里是 hello 组件
    </div>
    
  4. 修改 hello.component.ts

    import { Component } from '@angular/core';
    
    @Component({
      selector: 'app-hello',
      standalone: true,
      imports: [],
      templateUrl: './hello.component.html',
      styleUrl: './hello.component.css'
    })
    export class HelloComponent {
    
    }
    
  5. 修改 app.component.ts

    import { HelloComponent } from "./hello/hello.component";
    
  6. 修改 app.component.html

    <app-hello></app-hello>
    
  7. 启动项目,访问 http://localhost:4200/,此时可以发现页面中存在 hello 组件

(3)生命周期

  • 生命周期钩子及其执行顺序如下:

    钩子 作用 时机
    ngOnChanges() 每当 Angular 设置或重新设置数据绑定的输入属性时响应 ngOnInit() 之前以及所绑定的一个或多个输入属性方式变化时调用
    ngOnInit() 在 Angular 第一次显示数据绑定和设置指令或组件的输入属性后,初始化指令或组件 首次 ngOnChanges() 之后调用,且仅调用一次
    ngDoCheck() 每当发生 Angular 无法或不愿自检的变化时,进行检测 每次执行变更检测时的 ngOnChanges() 之后调用
    首次执行变更检测的 ngOnInit() 之后调用
    ngAfterContentInit() 当 Angular 将外部内容投影至组件或指令中的内容之后调用 首次 ngDoCheck() 之后调用,且仅调用一次
    ngAfterContentChecked() 每当 Angular 检测完被投影至组件或指令中的内容之后调用 每次 ngDoCheck() 之后调用
    首次 ngAfterContentInit() 之后调用
    ngAfterViewInit() 当 Angular 初始化完组件视图、其子视图、或包含该指令的视图之后调用 首次 ngAfterContentChecked() 之后调用,且仅调用一次
    ngAfterViewChecked() 每当 Angular 检测完组件视图、其子视图、或包含该指令的视图的变更之后调用 每次 ngAfterContentChecked() 之后调用
    首次 ngAfterViewInit() 之后调用
    ngOnDestroy() 每当 Angular 销毁指令或组件之前调用 在 Angular 销毁指令或组件之前立即调用
    graph LR ngOnChanges--首次-->ngOnInit-->ngDoCheck--首次-->ngAfterContentInit-->ngAfterContentChecked--首次-->ngAfterViewInit-->ngAfterViewChecked-.->ngOnDestroy ngOnChanges-->ngDoCheck-->ngAfterContentChecked-->ngAfterViewChecked
  • 举例:app.component.ts

    export class AppComponent {
      ngOnChanges() {
        console.log('ngOnChanges');
      }
      ngOnInit() {
        console.log('ngOnInit');
      }
      ngDoCheck() {
        console.log('ngDoCheck');
      }
      ngAfterContentInit() {
        console.log('ngAfterContentInit');
      }
      ngAfterContentChecked() {
        console.log('ngAfterContentChecked');
      }
      ngAfterViewInit() {
        console.log('ngAfterViewInit');
      }
      ngAfterViewChecked() {
        console.log('ngAfterViewChecked');
      }
      ngOnDestroy() {
        console.log('ngOnDestroy');
      }
    }
    

(4)交互

组件间传参

  • @Input:用于父组件给子组件绑定属性,设置输入类数据

    1. hello.component.ts

      import { Component, Input } from '@angular/core';
      
      @Component({/* ... */})
      export class HelloComponent {
        @Input() username!: string;
      
        ngOnInit() {
          console.log(this.username);
        }
      }
      
      
    2. app.component.html

      <app-hello [username]="'SRIGT'"></app-hello>
      
  • @Output:用于子组件弹射触发事件,该事件来自父组件给子组件的传递

    1. app.component.ts

      export class AppComponent {
        list = [1, 2, 3];
        add(newNumber: number) {
          this.list.push(newNumber);
        }
      }
      
    2. hello.component.ts

      import { Component, EventEmitter, Output } from '@angular/core';
      
      @Component({
        selector: 'app-hello',
        standalone: true,
        imports: [],
        templateUrl: './hello.component.html',
        styleUrl: './hello.component.css',
      })
      export class HelloComponent {
        @Output() add = new EventEmitter();
      
        clickHandler() {
          this.add.emit(4);
        }
      }
      
    3. app.component.html

      <app-hello (add)="add($event)"></app-hello>
      <p>{{ list.toString() }}</p>
      
    4. hello.component.html

      <button (click)="clickHandler()">+4</button>
      
  • @ViewChild:获取子组件实例与数据

    1. app.component.ts

      import { Component, ViewChild } from '@angular/core';
      // ...
      @Component({/* ... */})
      export class AppComponent {
        @ViewChild('childComp') child: any;
      
        clickHandler() {
          console.log(this.child);
        }
      }
      
    2. app.component.html

      <app-hello #childComp></app-hello>
      <button (click)="clickHandler()">Click</button>
      

0x03 模板

(1)插值

  • “插值”指将表达式嵌入到 HTML 文档中,通过 {{ }} 实现

    <!-- filename: app.component.html -->
    <p>1 + 1 = {{ 1 + 1 }}</p>
    
  • 在 app.component.ts 导出的类中包含的变量值,也可以通过插值语法嵌入到 HTML 文档中

    • app.component.ts

      // import ...
      
      @Component({/* ... */})
      export class AppComponent {
        expression = `1 + 1 = ${1 + 1}`;
      }
      
    • app.component.html

      <p>{{ expression }}</p>
      

(2)绑定

a. 属性绑定

  • 一般地,属性绑定使用 [ ] 实现,如:

    <h1 [id]="'h1-title'">Title</h1>
    

    渲染后变成了

    <h1 _ngcontent-ng-xxxxxxxxxxx id="h1-title">Title</h1>
    
  • 类绑定

    • 单一类绑定:

      <h1 [class]="'h1-title'">Title</h1>
      <!-- 或 -->
      <h1 [class.h1-title]="true">Title</h1>
      
    • 多个类绑定:

      <h1 [class]="'h1-title text-4xl font-bold'">Title</h1>
      <!-- 或 -->
      <h1 [class]="{ 'h1-title': true, 'text-4xl': false }">Title</h1>
      <!-- 或 -->
      <h1 [class]="[ 'h1-title', 'text-4xl' ]">Title</h1>
      
  • 样式绑定

    • 单一样式绑定:

      <h1 [style]="'font-size:1em'">Title</h1>
      <!-- 或 -->
      <h1 [style.font-size]="'1em'">Title</h1>
      <!-- 或 -->
      <h1 [style.font-size.em]="'1'">Title</h1>
      
    • 多重样式绑定:

      <h1 [style]="'font-size:1em;font-weight:900;'">Title</h1>
      <!-- 或 -->
      <h1 [style]="{ 'font-size': '1em', 'font-weight': 900 }">Title</h1>
      

b. 事件绑定

  • 一般地,事件绑定使用 ( ) 实现,如:

    • app.component.ts

      export class AppComponent {
        clickHandler() {
          alert("Button clicked")
        }
      }
      
    • app.component.html

      <button (click)="clickHandler()">Click</button>
      
  • 对于事件对象,使用 $event 传递

    • app.component.ts

      export class AppComponent {
        clickHandler(event: Event) {
          console.log(event)
        }
      }
      
    • app.component.html

       <button (click)="clickHandler($event)">Click</button>
      

c. 双向绑定

  • 双向绑定是应用内组件共享数据的方式

  • 一般地,双向绑定使用 [( )] 实现

    • app.component.ts

      export class AppComponent {
        number: number = 10;
      
        add() {
          this.number += 1;
        }
      
        sub() {
          this.number -= 1;
        }
      }
      
    • app.component.html

      <button (click)="sub()">-1</button>
      <button (click)="add()">+1</button>
      <span>Current number is {{ number }}</span>
      
  • 表单元素的双向绑定需要 @NgModule 装饰器,使用前需要从 @angular/forms 中导入 FormsModule

    • app.component.ts

      // ...
      import { FormsModule } from '@angular/forms';
      
      @Component({
        // ...
        imports: [/*...*/, FormsModule],
      })
      export class AppComponent {
        text = '';
      }
      
    • app.component.html

      <input [(ngModel)]="text" />
      <p>Input text: {{ text }}</p>
      

d. 模板变量

  • 模板变量使用 # 声明

    • app.component.ts

      export class AppComponent {
        clickHandler(value: string) {
          alert(value);
        }
      }
      
    • app.component.html

      <input #text />
      <button (click)="clickHandler(text.value)">Submit</button>
      
  • Angular 根据声明模板变量的位置进行赋值

    • 在标准 HTML 元素上,则引用该元素
    • 在组件上,则引用该组件实例
    • <ng-template> 元素上,则引用一个 TemplateRef 实例来代表模板

(3)渲染

a. 条件

I. if

使用 *ngIF 实现,使用前需要从 @angular/common 导入 NgIf

注意:仅用于元素是否被渲染,而非控制元素的显隐

  • app.component.ts

    // ...
    import { NgIf } from '@angular/common';
    
    @Component({
      // ...
      imports: [/* ... */, NgIf],
    })
    export class AppComponent {
      isRender = false;
    }
    
  • app.component.html

    <p>
      My name is
      <span *ngIf="isRender">John</span>
      <span *ngIf="!isRender">SRIGT</span>
      .
    </p>
    <!-- My name is SRIGT . -->
    

II. switch

使用 *ngSwitch 实现,使用前需要从 @angular/common 导入 NgSwitch

  • app.component.ts

    // ...
    import { NgSwitch, NgSwitchCase, NgSwitchDefault } from '@angular/common';
    
    @Component({
      // ...
      imports: [/* ... */, NgSwitch, NgSwitchCase, NgSwitchDefault],
    })
    export class AppComponent {
      nameType = "nickname"
    }
    
  • app.component.html

    <p [ngSwitch]="nameType">
      My name is
      <span *ngSwitchCase="'realname'">John</span>
      <span *ngSwitchCase="'nickname'">SRIGT</span>
      <span *ngSwitchDefault>Someone</span>
      .
    </p>
    <!-- My name is SRIGT . -->
    

b. 循环

循环渲染使用 *ngFor 实现,使用前需要从 @angular/common 导入 NgFor

  • app.component.ts

    // ...
    import { NgFor } from '@angular/common';
    
    @Component({
      // ...
      imports: [/* ... */, NgFor],
    })
    export class AppComponent {
      names = ['Alex', 'Bob', 'Charlie'];
    }
    
  • app.component.html

    <div *ngFor="let name of names; let i = index; let odd = odd">
      {{ odd }} {{ i }} {{ name }}
    </div>
    <!--
    false 0 Alex
    true 1 Bob
    false 2 Charlie
    -->
    

c. 控制流

Angular 16 新特性

  • @if@else if@else

    • app.component.ts

      export class AppComponent {
        isRender = false;
      }
      
    • app.component.html

      <p>
        My name is
        @if (isRender) { {{ "John" }} }
        @else { {{ "SRIGT" }} }
        .
      </p>
      <!-- My name is SRIGT . -->
      
  • @switch@case@default

    • app.component.ts

      export class AppComponent {
        nameType = "nickname"
      }
      
    • app.component.html

      <p>
        My name is
        @switch (nameType) {
          @case ('realname') {
            {{ "John" }}
          }
          @case ('nickname') {
            {{ "SRIGT" }}
          }
          @default {
            {{ "Someone" }}
          }
        }
        .
      </p>
      <!-- My name is SRIGT . -->
      
  • @for

    • app.component.ts

      export class AppComponent {
        items = [
          { id: 1, name: 'Alex' },
          { id: 2, name: 'Bob' },
          { id: 3, name: 'Charlie' },
        ];
      }
      
    • app.component.html

      <ul>
        @for (item of items; track item.id) {
        <li>{{ item.name }}</li>
        } @empty {
        <li>There are no items.</li>
        }
      </ul>
      

(4)表单

a. 单个表单控件

  1. 注册响应式表单模块,需要从 @angular/forms 导入 ReactiveFormsModule

    // ...
    import { ReactiveFormsModule } from '@angular/forms';
    
    @Component({
      // ...
      imports: [/* ... */, ReactiveFormsModule],
    })
    export class AppComponent {}
    
  2. 生成一个新的 FormControl 实例并保存在组件中,需要从 @angular/forms 导入 FormControl

    // ...
    import { FormControl, ReactiveFormsModule } from '@angular/forms';
    
    @Component({/* ... */})
    export class AppComponent {
      username = new FormControl('');
    
      reset() {
        this.username.setValue('');
      }
    }
    
  3. 在模板中注册该实例

    <label>
      username:
      <input type="text" [formControl]="username" />
    </label>
    <p>{{ username.value }}</p>
    <button (click)="reset()">reset</button>
    

b. 多个表单控件

  1. 创建 FormGroup 实例,需要从 @angular/forms 导入 FormGroup

    // ...
    import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
    
    @Component({/* ... */})
    export class AppComponent {
      loginForm = new FormGroup({
        username: new FormControl(''),
        password: new FormControl(''),
      });
    
      onSubmit() {
        alert(
          `username: ${this.loginForm.value.username}\npassword: ${this.loginForm.value.password}`
        );
      }
    }
    
  2. 在模板中注册该实例

    <form [formGroup]="loginForm">
      <label>
        username:
        <input type="text" formControlName="username" />
      </label>
      <br />
      <label>
        password:
        <input type="password" formControlName="password" />
      </label>
      <br />
      <button (click)="onSubmit()">Login</button>
    </form>
    

c. 表单验证

  • Angular 支持 HTML5 提供的表单验证关键字(如 required 等),通过双向绑定可以获取组件信息,从得到验证结果

    • app.component.ts

      // ...
      import {
        FormControl,
        FormGroup,
        ReactiveFormsModule,
        ValidationErrors,
        Validators,
      } from '@angular/forms';
      
      @Component({
        // ...
        imports: [/* ... */, ReactiveFormsModule],
      })
      export class AppComponent {
        // 表单数据
        formData = { username: '', password: '' };
        
        // 自定义验证方法
        passwordValidator(control: FormControl): ValidationErrors | null {
          return control.value.length < 5 ? { password: true } : null;
        }
      
        // 表单控件组
        loginForm = new FormGroup({
          username: new FormControl(this.formData.username, [
            Validators.maxLength(10),
          ]),
          password: new FormControl(this.formData.password, [
            Validators.required,
            this.passwordValidator,
          ]),
        });
      
        // 提交方法
        onSubmit() {
          alert(
            `username: ${this.loginForm.value.username}\npassword: ${this.loginForm.value.password}`
          );
        }
      }
      
    • app.component.html

      <form [formGroup]="loginForm">
        <label>
          username:
          <input type="text" formControlName="username" required />
        </label>
        <br />
        <span>{{ loginForm.get("username")?.valid }}</span>
        <br />
        <label>
          password:
          <input type="password" formControlName="password" />
        </label>
        <br />
        <span>{{ loginForm.get("password")?.valid }}</span>
        <br />
        <button (click)="onSubmit()">Login</button>
      </form>
      

      其中,.valid 是布尔类型

  • ngModel 可用于跟踪修改状态与有效性验证,通过三个 CSS 类来更新控件:

    状态 为真 为假
    已被访问 ng-touched ug-untouched
    已变化 ng-dirty ng-pristine
    有效 ng-valid ng-invalid
    input {
      outline: none;
    }
    
    input.ng-invalid {
      border: 1px solid red;
    }
    

(5)管道

  • 管道用于传输,即数据处理
  • 管道采用链式连接,自左向右,依次执行
  • 语法格式:{{ 输入数据 | 管道1:参数 | 管道2... }}

注意:管道操作符的优先级高于 JavaScript 三元运算符 ?:,同时使用时需要使用 ( ) 包裹三元运算符部分

a. 内置管道

  • 内置管道包括

    管道 功能
    date 格式化日期
    json 经过 JSON.stringify()
    uppercase 字母大写转换
    lowercase 字母小写转换
    decimal 数值特定格式
    currectcy 数值货币格式
    percent 数值百分比格式
    slice 切片成新子集
  • 举例:

    • app.component.ts

      // ...
      import { CommonModule } from '@angular/common';
      
      @Component({
        // ...
        imports: [/* ... */, CommonModule],
      })
      export class AppComponent {
        today: Date = new Date();
      } 
      
    • app.component.html

      <p>{{ today | date: 'yyyy-MM-dd HH:mm:ss'}}</p>
      

b. 自定义管道

使用命令 ng g p newPipe 生成名为 newPipe 的自定义管道,通过 @Pipe 装饰器标识管道

  • prefix.pipe.ts

    import { Pipe, PipeTransform } from '@angular/core';
    
    @Pipe({
      name: 'prefix',
      standalone: true,
    })
    export class PrefixPipe implements PipeTransform {
      transform(value: string, ...args: unknown[]): unknown {
        return `angular-${value}`;
      }
    }
    
  • app.component.ts

    // ...
    import { CommonModule } from '@angular/common';
    import { PrefixPipe } from './prefix.pipe';
    
    @Component({
      // ...
      imports: [/* ... */, CommonModule, PrefixPipe],
    })
    export class AppComponent {}
    
  • app.component.html

    <p>{{ "SRIGT" | prefix }}</p>
    

0x04 依赖注入

(1)服务

  • 从组件中抽离出来的代码成为服务,其本质上是函数

    • 主要将数据访问的职责从组件中独立成服务,使组件专注于数据展示
  • 使用命令 ng g s list 生成一个名为 list 的服务,通过 @Injectable 装饰器标识服务

    import { Injectable } from '@angular/core';
    
    @Injectable({
      providedIn: 'root',
    })
    export class ListService {
      constructor() {}
    }
    
  • 服务需要作为依赖,注入到系统、组件或模块才能使用,通过注册提供商和根注入器实现

(2)依赖注入

  • 在上述新建的服务中,providedIn: 'root' 指定 Angular 应该在根注入器中提供该服务

  • providedIn 取值包括:

    • 'root':注入到 App Module,所有子组件都可以使用
    • null:不设定服务在作用域
    • 组件名:只作用于该组件,常用于懒加载
  • 完善上述服务:

    • list.service.ts

      import { Injectable } from '@angular/core';
      
      @Injectable({
        providedIn: 'root',
      })
      export class ListService {
        constructor() {}
      
        names = ['Alex', 'Bob', 'Charlie'];
      
        getNames() {
          return this.names;
        }
      }
      
    • app.component.ts

      // ...
      import { ListService } from './list.service';
      
      @Component({/* ... */})
      export class AppComponent {
        constructor(private listService: ListService) {}
      
        names: Array<string> | undefined;
      
        ngOnInit() {
          this.names = this.listService.getNames();
        }
      }
      
    • app.component.html

      <ul>
        @for (name of names; track $index) {
        <li>{{ name }}</li>
        }
      </ul>
      

0x05 路由

(1)配置

  • 路由器(Router)是一套规则列表,可用于查询 URL 对应的视图规则

  • 路由(Route)是列表中的一条规则

    • path:URL 目标
    • component:对应视图(组件)
  • 一般地,路由规则配置在 .routes.ts 文件中,如 app.routes.ts:

    import { Routes } from '@angular/router';
    
    export const routes: Routes = [];
    
  • 举例,设置链接,从 / 跳转至 /hello

    1. app.routes.ts

      import { Routes } from '@angular/router';
      import { HelloComponent } from './hello/hello.component';
      
      export const routes: Routes = [
        {
          path: 'hello',
          component: HelloComponent,
        },
      ];
      
    2. app.component.ts

      import { Component } from '@angular/core';
      import { RouterLink, RouterOutlet } from '@angular/router';
      
      @Component({
        selector: 'app-root',
        standalone: true,
        imports: [RouterOutlet, RouterLink],
        templateUrl: './app.component.html',
        styleUrl: './app.component.css',
      })
      export class AppComponent {}
      
    3. app.component.html

      <a [routerLink]="['/hello']">Hello</a>
      <router-outlet></router-outlet>
      
    4. hello.component.html

      <p>Hello, Angular</p>
      
  • 使用通配路由实现 404 页面

    export const routes: Routes = [
      // ...
      {
        path: '**',
        component: NotFoundComponent,
      },
    ];
    

(2)嵌套

  • 路由规则的 children 属性可用于实现路由嵌套

    // filename: app.routes.ts
    import { Routes } from '@angular/router';
    import { HelloComponent } from './hello/hello.component';
    
    export const routes: Routes = [
      {
        path: 'hello',
        component: HelloComponent,
        children: [
          {
            path: 'angular',
            component: HelloComponent,
          },
        ],
      },
    ];
    
  • 在非父级路由 /hello 需要跳转到 /hello/angular 时,需要些完整路径

    <!-- filename: app.component.html -->
    <a [routerLink]="['/hello/angular']">Hello</a>
    <router-outlet></router-outlet>
    

(3)传参

  • query:通过 <a>queryParams 参数传递参数,通过 ActivatedRoute.snapshot.queryParams 获取参数

    1. app.component.html

      <a [routerLink]="['/hello']" [queryParams]="{ name: 'Angular' }">Hello</a>
      <router-outlet></router-outlet>
      
    2. hello.component.ts

      import { Component } from '@angular/core';
      import { ActivatedRoute } from '@angular/router';
      
      @Component({/* ... */})
      export class HelloComponent {
        constructor(private activatedRoute: ActivatedRoute) {}
      
        name = ""
      
        ngOnInit() {
          this.name = this.activatedRoute.snapshot.queryParams["name"]
        }
      }
      
    3. hello.component.html

      <p>Hello, {{ name }}</p>
      
  • params:通过在路由配置中加入参数名来传递参数,通过 `` 获取参数

    1. app.routes.ts

      import { Routes } from '@angular/router';
      import { HelloComponent } from './hello/hello.component';
      
      export const routes: Routes = [
        {
          path: 'hello/:name',
          component: HelloComponent,
        },
      ];
      
    2. hello.component.ts

      import { Component } from '@angular/core';
      import { ActivatedRoute, Params } from '@angular/router';
      
      @Component({
        selector: 'app-hello',
        standalone: true,
        imports: [],
        templateUrl: './hello.component.html',
        styleUrl: './hello.component.css',
      })
      export class HelloComponent {
        constructor(private activatedRoute: ActivatedRoute) {}
      
        name = '';
      
        ngOnInit() {
          this.activatedRoute.params.subscribe(
            (params: Params) => (this.name = params['name'])
          );
        }
      }
      
    3. hello.component.html

      <p>Hello, {{ name }}</p>
      
    4. 访问 http://localhost:4200/hello/angular

更多详细内容参考官方文档

-End-

posted @ 2024-11-01 15:36  SRIGT  阅读(122)  评论(0编辑  收藏  举报