远古框架Angular的学习

 目录

Angular框架的学习

一:框架背景

二:Angular CLI

1.安装

2.主要特性

三:架构

1.模块

2.组件

2.1.创建组件

2.2组件生命周期

2.3组件交互

3.模版

3.1插值语法

3.2属性绑定

3.3条件判断

3.4循环语句

3.5事件绑定

3.6双向绑定

3.7模版引用变量

3.8表单控制

3.9表单控件分组

3.10表单验证

3.11自定义表单验证

3.12管道

4.服务

5.依赖注入

6.路由

6.1路由基本使用

6.2路由嵌套

6.3路由传参


一:框架背景

Angular 是一个由 Google维护的开源JavaScript框架,用于在HTML和JavaScript中构建Web应用程序,是三大框架之首。

Angular最显著的特征就是其整合性。涵盖了M、V、C/VM等各个层面,不需要组合、评估其它技术就能完成大部分前端开发任务。这样可以有效降低决策成本,提高决策速度,对需要快速起步的团队是非常有帮助的。

由于它是从一个用来做原型的框架演化而来的,加上诞生时间很早(2009年,作为对比,jQuery诞生于2006年),当时生态不完善,连模块化机制都得自己实现。

但Angular 2就不同了,发布于2016年9月份,它是基于ES6来开发的,它的起点更高,整合了现代前端的各种先进理念,在框架、文档、工具等各个层面提供了全方位的支持

在Angular 中最具特色的就是依赖注入系统了,它把后端开发中用来解决复杂问题、实现高弹性设计的技术引入了前端

二:Angular CLI

1.安装

​ Angular CLI用于简单,快速构建Angular项目,只要掌握几行命令就能构建前端架构。依赖于NodeJs和npm。

//安装脚手架
npm install -g angular-cli
//创建项目
ng new project_name(项目名称)
//启动项目
cd project_name
ng serve --open 

2.主要特性

1. Angular CLI 可以快速搭建框架,创建module,service,class,directive等;

2. 具有webpack的功能,代码分割,按需加载;

3. 代码打包压缩;

4. 模块测试;

5. 热部署,有改动立即重新编译,不用刷新浏览器;而且速度很快

6.  有开发环境,测试环境,生产环境的配置,不用自己操心;

7.  sass,less的预编译Angular CLI都会自动识别后缀来编译;

8. typescript的配置,Angular CLI在创建应用时都可以自己配置;

9.  在创建好的工程也可以做一些个性化的配置,webpack的具体配置还不支持,未来可能会增加;

10.  Angular CLI创建的工程结构是最佳实践,生产可用;

三:架构

1.模块

模块组件的特征在于可以用于执行单个任务的代码块。 您可以从代码(类)中导出值。 Angular应用程序被称为模块,并使用许多模块构建您的应用程序。 Angular 的基本构建块是可以从模块导出的组件类。

2.组件

组件是拥有模板的控制器类,主要处理页面上的应用程序和逻辑的视图。 组件可以拥有独立的样式。

注册组件,使用 @Component注释,可以将应用程序拆分为更小的部分。

2.1.创建组件

使用ng命令ng generate component <component-name>创建的组件会自动生成在`app.module`中的引用,推荐使用ng命令生成组件

也可以简写" ng g c ×××"

//快速创建
ng g c xxx
@Component({

  selector: 'app-root',

  templateUrl: './app.component.html',

  styleUrls: ['./app.component.scss']

})

@Component最常用的几个选项:

selector:这个 CSS 选择器用于在模板中标记出该指令,并触发该指令的实例化。

template:组件的内联模板

templateUrl:组件模板文件的 URL

styleUrls:组件样式文件

styles:组件内联样式

2.2组件生命周期

你的应用可以使用生命周期钩子方法来触发组件或指令生命周期中的关键事件,以初始化新实例,需要时启动变更检测,在变更检测过程中响应更新,并在删除实例之前进行清理。

每个接口都有唯一的一个钩子方法,它们的名字是由接口名再加上 ng 前缀构成的。比如,OnInit 接口的钩子方法叫做 ngOnInit()。如果你在组件或指令类中实现了这个方法,Angular 就会在首次检查完组件或指令的输入属性后,紧接着调用它。

Angular 会按以下顺序执行钩子方法。可以用它来执行以下类型的操作。

钩子方法用途时机
ngOnChanges()当 Angular 设置或重新设置数据绑定的输入属性时响应。该方法接受当前和上一属性值的SimpleChanges对象

如果组件绑定过输入属性,那么在 ngOnInit() 之前以及所绑定的一个或多个输入属性的值发生变化时都会调用。

注意:如果你的组件没有输入属性,或者你使用它时没有提供任何输入属性,那么框架就不会调用 ngOnChanges()

ngOnInit()在 Angular 第一次显示数据绑定和设置指令/组件的输入属性之后,初始化指令/组件。在第一轮 ngOnChanges() 完成之后调用,只调用一次。而且即使没有调用过 ngOnChanges(),也仍然会调用 ngOnInit()(比如当模板中没有绑定任何输入属性时)。
ngDoCheck()检测,并在发生 Angular 无法或不愿意自己检测的变化时作出反应。紧跟在每次执行变更检测时的 ngOnChanges() 和 首次执行变更检测时的 ngOnInit() 后调用。
ngAfterContentInit()当 Angular 把外部内容投影进组件视图或指令所在的视图之后调用。第一次 ngDoCheck() 之后调用,只调用一次。
ngAfterContentChecked()每当 Angular 检查完被投影到组件或指令中的内容之后调用。ngAfterContentInit() 和每次 ngDoCheck() 之后调用。
ngAfterViewInit()当 Angular 初始化完组件视图及其子视图或包含该指令的视图之后调用。第一次 ngAfterContentChecked() 之后调用,只调用一次。
ngAfterViewChecked()每当 Angular 做完组件视图和子视图或包含该指令的视图的变更检测之后调用。ngAfterViewInit() 和每次 ngAfterContentChecked() 之后调用。
ngOnDestroy()每当 Angular 每次销毁指令/组件之前调用并清扫。在这儿反订阅可观察对象和分离事件处理器,以防内存泄漏。在 Angular 销毁指令或组件之前立即调用。

2.3组件交互

1. ​  @Input

@Input() 装饰器是其中一种非常常用的方法,用于实现父组件向子组件传递数据。

1)定义子组件

在子组件的类中,使用 @Input() 装饰器来声明一个输入属性。这个属性将用于接收父组件传递的数据。

import { Component, Input } from '@angular/core';

@Component({
  selector: 'app-child',
  template: `Child received: {{ data }}`
})
export class ChildComponent {
  @Input() data: any; // 可以指定具体的类型
}

2)在父组件模版中使用子组件

<!-- parent.component.html -->
<app-child [data]="parentData"></app-child>

3)在父组件类中定义数据

// parent.component.ts
export class ParentComponent {
  parentData = 'Hello from Parent!';
}

2. ​  @Output

@Output() 装饰器用于创建自定义事件,允许子组件向父组件发送数据或事件。这通常通过创建一个 EventEmitter 实例来实现,它是一种特殊的类,用于在组件之间发送事件。

1)导入EventEmitter和Output

在子组件的类文件中,导入 EventEmitter 从 @angular/core 模块和 Output 装饰器。

import { Component, EventEmitter, Output } from '@angular/core';

2)在子组件类中定义事件

使用 @Output() 装饰器声明一个 EventEmitter 实例,这将作为事件的输出。

@Component({
  selector: 'app-child',
  template: `<button (click)="sendData()">Send Data</button>`
})
export class ChildComponent {
  @Output() dataSent = new EventEmitter<string>(); // 可以指定具体的类型

  sendData() {
    this.dataSent.emit('Data from Child'); // 发送数据到父组件
  }
}

3)在父组件模版中使用子组件

在父组件的模板中,使用子组件的标签,并使用事件绑定来监听子组件发出的事件。

<!-- parent.component.html -->
<app-child (dataSent)="handleData($event)"></app-child>

4)在父组件的类中定义一个方法来处理从子组件接收到的事件。

// parent.component.ts
export class ParentComponent {
  handleData(data: string) {
    console.log('Data received from child:', data);
  }
}

3. ​    @ViewChild()

@ViewChild 装饰器允许你访问组件模板中的元素、指令或另一个组件。这通常用于父组件和子组件之间的交互,或者在组件内部访问特定的DOM元素。

1)导入ViewChild

在组件的类文件中,从 @angular/core 模块导入 ViewChild 装饰器

import { Component, ViewChild, ElementRef, AfterViewInit } from '@angular/core';

2)在组件类中使用@ViewChild

使用 @ViewChild 装饰器来声明一个引用变量,该变量将用于访问模板中的元素、指令或组件。

@Component({
  selector: 'app-parent',
  template: '<app-child #childComponent></app-child>'
})
export class ParentComponent implements AfterViewInit {
  @ViewChild('childComponent') childComponent: any; // 使用具体类型

  ngAfterViewInit() {
    // 这里可以安全地访问 childComponent 引用
    console.log(this.childComponent);
  }
}

3)在模版中使用引用变量

<!-- 在子组件模板中 -->
<div #myDiv>Some content</div>

4)访问模版中的元素

在组件类中,使用 @ViewChild 装饰器来访问通过模板引用变量标识的元素。

@ViewChild('myDiv') myDiv: ElementRef;

5)使用引用变量

组件的生命周期钩子中(通常是 ngAfterViewInit),你可以安全地访问和操作引用变量。

ngAfterViewInit() {
  console.log(this.myDiv.nativeElement.innerText);
}

6)访问子组件的属性或方法

如果想要访问子组件的属性或方法,确保子组件的类中公开了这些属性或方法。

// 在子组件类中
export class ChildComponent {
  public someProperty = 'Some value';
  someMethod() {
    console.log('Method called');
  }
}

7)使用子组件的属性或方法

@ViewChild(ChildComponent) childComponent: ChildComponent;

ngAfterViewInit() {
  console.log(this.childComponent.someProperty);
  this.childComponent.someMethod();
}

3.模版

在 Angular 中,模板就是一块 HTML。在模板中,你可以通过一种特殊语法来使用 Angular 的许多功能

3.1插值语法

所谓 "插值" 是指将表达式嵌入到标记文本中。 默认情况下,插值会用双花括号 `{{` 和 `}}` 作为分隔符

<h3>hello {{ name }}</h3>

花括号之间的文本通常是组件属性的名字。Angular 会把这个名字替换为响应组件属性的字符串的值

下面是一个模板表达式, 我们可以在`{{}}`内编写js运算

<h3>hello {{ 1+1 }}</h3>

3.2属性绑定

3.2.1.  类绑定

//单一类绑定
<h3 [class.h3-dom]="true">hello {{ 1+1 }}</h3>
    
//多重类绑定
<h3 [class]="'h3-dom title-dom min-title'">hello {{ 1+1 }}</h3>
<h3 [class]="{'h3-dom':true,'title-dom':false}">hello {{ 1+1 }}</h3>
<h3 [class]="['h3-dom','title-dom']">hello {{ 1+1 }}</h3>

//ngClass
export class AppComponent {
   isActive = true;
}
    
<h3 [ngClass]="{'active': isActive}">hello {{ 1+1 }}</h3>

3.2.2.  样式绑定

//单一样式绑定
<h3 [style.width]="'300px'">hello {{ 1+1 }}</h3>
    
//带单位的单一样式绑定
<h3 [style.width.px]="'300'">hello {{ 1+1 }}</h3>
    
//多重样式绑定
<h3 [style]="'background:red;color:#fff'">hello {{ 1+1 }}</h3>
<h3 [style]="{'background':'red','color':'#fff'}">hello {{ 1+1 }}</h3>
    
//ngStyle
export class AppComponent {
   isMax = false;
}
<h3 [ngStyle]="{'color': 'red'}">hello {{ 1+1 }}</h3>
<h3 [ngStyle]="{'font-size': isMax ? '24px' : '12px'}">hello {{ 1+1 }}</h3>

3.3条件判断

*ngIf 是一个结构型指令,用于根据条件动态地添加或移除DOM元素。它可以根据一个表达式的真值来决定是否渲染元素。

 ngIf是直接影响元素是否被渲染,而非控制元素的显示和隐藏

<!-- 基本用法 -->
<div *ngIf="condition; else elseBlock">...</div>

<!-- 使用 else 子模板 -->
<div *ngIf="condition; else elseBlock">
  <!-- 条件为真时显示的内容 -->
</div>
<ng-template #elseBlock>
  <!-- 条件为假时显示的内容 -->
</ng-template>

举例

// component.ts
export class MyComponent {
  isVisible: boolean = true;
}

<!-- component.html -->
<div *ngIf="isVisible">This div will be visible if isVisible is true.</div>

<div *ngIf="isVisible; else notVisibleBlock">This div will be visible if isVisible is true.</div>
<ng-template #notVisibleBlock>This content will be shown if isVisible is false.</ng-template>

3.4循环语句

*ngFor 是一个结构型指令,用于基于一个集合或数组来重复渲染一组元素。它允许你迭代列表中的每个项,并为每个项创建一个模板实例。

<!-- 基本用法 -->
<ng-container *ngFor="let item of items; index as i; trackBy: trackById">
  {{ item }}
</ng-container>

<!-- 使用 let 来声明当前项 -->
<!-- 使用 index as 来声明索引 -->
<!-- 使用 trackBy: 来指定追踪函数 -->

举例

// component.ts
export class MyComponent {
  items = ['Apple', 'Banana', 'Cherry'];
}

<!-- component.html -->
<ul>
  <li *ngFor="let fruit of items">{{ fruit }}</li>
</ul>


//使用索引
<ul>
  <li *ngFor="let fruit of items; index as i">{{ i }}: {{ fruit }}</li>
</ul>


//可以提供一个 trackBy 函数来帮助Angular识别列表项的唯一性。
// component.ts
trackById(index: number, item: any): any {
  return item.id; // 假设每个项都有一个唯一的 id 属性
}
<!-- component.html -->
<ul>
  <li *ngFor="let fruit of items; trackBy: trackById">{{ fruit }}</li>
</ul>

3.5事件绑定

Angular 的事件绑定语法由等号左侧括号内的目标事件名和右侧引号内的模板语句组成。目标事件名是 click ,模板语句是 onSave()   事件对象通过$event传递

export class AppComponent {
   onSave(){
       console.log('点击了按钮')
   }
}

<button (click)="onSave()">Save</button>

3.6双向绑定

双向绑定是应用中的组件共享数据的一种方式。使用双向绑定绑定来侦听事件并在父组件和子组件之间同步更新值

ngModel指令只对表单元素有效,所以在使用之前需要导入FormsModule板块

import { FormsModule } from '@angular/forms';

@NgModule({
  // 申明组件内用到的视图
  declarations: [
    AppComponent,
    HelloComponent,
  ],
  //引入模块需要的类
  imports: [
    BrowserModule,
    AppRoutingModule,
    FormsModule
  ],
  //全局服务
  providers: [],
  //根组件
  bootstrap: [AppComponent]
})
export class AppComponent {
  userName='';
}
<div>
    输入: <input [(ngModel)]="userName">
	<h1>你输入了: {{userName}}</h1>
</div>

3.7模版引用变量

模板引用变量(Template Reference Variables)提供了一种方式来引用DOM元素或指令的实例,允许你在组件的类中直接操作这些元素或指令。

<!-- 定义模板引用变量 -->
<input #inputBox type="text">

举例:假设你有一个组件,其中包含一个输入框,你想要获取输入框的引用,并在用户输入时执行一些操作。

// component.ts
import { Component, ViewChild, ElementRef } from '@angular/core';

@Component({
  selector: 'app-my-component',
  template: `
    <input #inputBox type="text">
    <button (click)="focusInput()">Focus Input</button>
  `
})
export class MyComponent {
  // 使用 ViewChild 获取模板引用变量的引用
  @ViewChild('inputBox') inputBox: ElementRef;

  focusInput() {
    // 使用 ElementRef 来操作 DOM 元素
    this.inputBox.nativeElement.focus();
  }
}

Angular 根据你所声明的变量的位置给模板变量赋值:

- 如果在组件上声明变量,该变量就会引用该组件实例。

- 如果在标准的 HTML 标记上声明变量,该变量就会引用该元素。

- 如果你在 <ng-template> 元素上声明变量,该变量就会引用一个 TemplateRef 实例来代表此模板。

3.8表单控件

使用表单控件有三个步骤。

1. 在你的应用中注册响应式表单模块。该模块声明了一些你要用在响应式表单中的指令。

2. 生成一个新的 FormControl 实例,并把它保存在组件中。

3. 在模板中注册这个 FormControl。

要使用响应式表单控件,就要从 @angular/forms 包中导入 ReactiveFormsModule,并把它添加到你的 NgModule 的 imports 数组中。

import { ReactiveFormsModule } from '@angular/forms';

@NgModule({
  imports: [
    // other imports ...
    ReactiveFormsModule
  ],
})
export class AppModule { }

要注册一个表单控件,就要导入 FormControl 类并创建一个 FormControl 的新实例,将其保存为类的属性。

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

@Component({
  selector: 'app-name-editor',
  templateUrl: './name-editor.component.html',
  styleUrls: ['./name-editor.component.css']
})
export class NameEditorComponent {
  name = new FormControl('');
}

//使用这种模板绑定语法,把该表单控件注册给了模板中名为 name 的输入元素。这样,表单控件和 DOM 元素就可以互相通讯了:视图会反映模型的变化,模型也会反映视图中的变化

<label>
  Name:
  <input type="text" [formControl]="name">
</label>
<p>
  Value: {{ name.value }}
</p>

修改name值可以通过FormControl 提供的 setValue() 方法

updateName() {
  this.name.setValue('Tina');
}

3.9表单控件分组

表单中通常会包含几个相互关联的控件。响应式表单提供了两种把多个相关控件分组到同一个输入表单中的方法

要将表单组添加到此组件中,执行以下步骤。

1. 创建一个 FormGroup 实例。

2. 把这个 FormGroup 模型关联到视图。

3. 保存表单数据。

在组件类中创建一个名叫 loginForm 的属性,并设置为 FormGroup 的一个新实例。要初始化这个 FormGroup,请为构造函数提供一个由控件组成的对象,对象中的每个名字都要和表单控件的名字一一对应

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

@Component({
  selector: 'app-profile-editor',
  templateUrl: './profile-editor.component.html',
  styleUrls: ['./profile-editor.component.css']
})
export class ProfileEditorComponent {
  loginForm = new FormGroup({
    userName: new FormControl(''),
    password: new FormControl(''),
  });
}

//模板渲染
<form [formGroup]="loginForm">
  
  <label>
    账号:
    <input type="text" formControlName="userName">
  </label>

  <label>
    密码:
    <input type="text" formControlName="password">
  </label>

</form>

3.10表单验证

​ 表单元素添加required关键字表示必填,通过绑定ngModel的引用可以拿到到当前组件的信息,通过引用获取到验证的信息

export class AppComponent {
    fromData={
       name:'',
       password:''
    };

    subBtnFUn(obj){
      console.log(obj)
    }
}

<form  action="">
    账号:<input required #nameInp="ngModel" type="text" [(ngModel)]="fromData.name" name="userName">
    <br>
    <span>{{nameInp.valid }}</span>
    <hr>
    密码:<input required  #pasInp="ngModel" type="text" [(ngModel)]="fromData.password" name="password">
    <br>
    <span>{{pasInp.valid }}</span>
    <hr>
    <button (click)="subBtnFUn(nameInp)">提交</button>
</form>

我们还可以通过 ngModel 跟踪修改状态与有效性验证,它使用了三个 CSS 类来更新控件,以便反映当前状态。

状态为 true 时的类为 false 时的类
控件已经被访问过ng-touchedng-untouched
控件值已经变化 ng-dirty   ng-pristine
控件值是有效的ng-valid ng-invalid

3.11自定义表单验证

​ 先引入表单的一些内置依赖

import { FormGroup, FormBuilder,Validators } from '@angular/forms';

//构造函数里注入FormBuilder
constructor(private fb:FormBuilder) { }

//错误提醒数据
formErrors = {
  'title': '',
  'content': ''
};
//在组件类的初始化函数里对表单中的元素的校验进行定义,并调用表单的valueChanges方法,检测表单的输入的变化
ngOnInit():void {
  this.taskInfo.isComplete = 1;
  this.tasksForm = this.fb.group({
     userName: ['', [Validators.required,
    				 Validators.maxLength(18),
                     Validators.minLength(6) ] ],
    password: ['', [this.passWordVal]],
    phone: ['', [Validators.required,this.phoneVal],]
  });

  phoneVal(phone: FormControl): object {
    const value = phone.value || '';
    if(!value) return  {desc:'请输入手机号'}
    const valid =  /[0-9]{11}/.test(value);
    return valid ? {} :{desc:'联系电话必须是11位数字'}
  }
  passWordVal(password:FormControl):object{
    const value = password.value || '';
    const valid = value.match(/^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,20}$/);
    return valid ? {} : {passwordValidator: {desc:'密码至少包含 数字和英文,长度6-20'}}
  }
}

3.12管道

​ 管道的作用就是传输。不同的管道具有不同的作用。

管道功能
DatePipe根据本地环境中的规则格式化日期值
JsonPipe
UpperCasePipe把文本全部转换成大写
LowerCasePipe把文本全部转换成小写
DecimalPipe把数字转换成带小数点的字符串,根据本地环境中的规则进行格式化
CurrencyPipe把数字转换成货币字符串,根据本地环境中的规则进行格式化
SlicePipe将数组或者字符串裁剪成新子集
PercentPipe把数字转换成百分比字符串,根据本地环境中的规则进行格式化

`pipe`用法

- {{ 输入数据 | 管道 : 管道参数}} (其中‘|’是管道操作符)

- 链式管道 {{ 输入数据 | date | uppercase}}

- 管道流通方向自左向右,逐层执行

举例:日期格式化

// component.ts
export class MyComponent {
  currentDate = new Date();
}

<!-- component.html -->
<p>{{ currentDate | date:'medium' }}</p>

4.服务

angular中,把从组件内抽离出来的代码叫服务,服务的本质就是函数

 官方认为组件不应该直接获取或保存数据, 它们应该聚焦于展示数据,而把数据访问的职责委托给某个服务。而服务就充当着数据访问,逻辑处理的功能。把组件和服务区分开,以提高模块性和复用性。通过把组件中和视图有关的功能与其他类型的处理分离开,可以让组件类更加精简、高效。

使用命令ng g s xxx创建一个服务,通过@Injectable()装饰器标识服务。

//导入Injectable装饰器
import { Injectable } from '@angular/core';
//使用Injectable装饰器声明服务
@Injectable({
  //作用域设定,'root'表示默认注入,注入到AppModule里
  providedIn: 'root',
})
export class TestService {
}

组件中如何使用服务呢,必须将服务依赖注入系统、组件或者模块,才能够使用服务。我们可以用注册提供商和根注入器实现。

   该服务本身是 CLI 创建的一个类,并且加上了 @Injectable() 装饰器。默认情况下,该装饰器是用 providedIn 属性进行配置的,它会为该服务创建一个提供商。

5.依赖注入

依赖注入是一种设计模式,用于实现控制反转,使得组件之间的耦合度降低,代码更加模块化和易于测试。Angular 的依赖注入系统是框架的核心特性之一,用于创建和维护对象(依赖)的生命周期,并将其传递给需要它们的对象

基本组件

  1. 令牌(Token):一个唯一标识符,用于请求依赖项。
  2. 提供者(Provider):定义如何创建依赖项的类或值。
  3. 注入器(Injector):负责创建或检索服务实例,并将其分配给请求者。

依赖注入的过程

  1. 声明依赖:组件、指令或服务在其构造函数中声明它们需要的依赖项。
  2. 提供依赖:在模块或组件的 providers 数组中提供依赖项。
  3. 注入依赖:Angular 的注入器根据提供的提供者自动解析并注入依赖项。

举例:假设我们有一个服务 MyService 和一个组件 MyComponentMyComponent 需要使用 MyService

// my.service.ts
import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root' // 提供服务,Angular 会自动在应用范围内提供单例服务
})
export class MyService {
  getData() {
    return 'Data from MyService';
  }
}

// my.component.ts
import { Component } from '@angular/core';
import { MyService } from './my.service';

@Component({
  selector: 'app-my-component',
  template: `<p>{{ data }}</p>`
})
export class MyComponent {
  data: string;

  constructor(private myService: MyService) {
    this.data = this.myService.getData();
  }
}

在这个例子中,MyService 被标记为 Injectable 并提供了 getData 方法。MyComponent 在其构造函数中声明了对 MyService 的依赖,Angular 的依赖注入系统会自动提供 MyService 的实例。

依赖注入的多种方式

  • 构造函数注入:最常见的方式,如上例所示。
  • 属性注入:使用 @Inject 装饰器来注入依赖项。
  • 方法注入:在组件的方法中注入依赖项。

6.路由

​ 路由就是连接组件的筋络,它也是树形结构的.有了它,就可以在angular中实现路径的导航模式

可以把路由看成是一组规则,它决定了url的变化对应着哪一种状态,具体表现就是不同视图的切换

在angular中,路由是非常重要的组成部分, 组件的实例化与销毁,模块的加载,组件的某些生命周期钩子的发起,都是与它有关

6.1路由基本使用

路由器是一个调度中心,它是一套规则的列表,能够查询当前URL对应的规则,并呈现出相应的视图.

路由是列表里面的一个规则,即路由定义,它有很多功能字段:

- path字段,表示该路由中的URL路径部分

- Component字段,表示与该路由相关联的组件

每个带路由的Angular应用都有一个路由器服务的单例对象,通过路由定义的列表进行配置后使用。

import { RouterModule, Routes } from '@angular/router';
import { NgModule } from '@angular/core';
// 假设AbcComponent和AboutComponent都在同一目录下,如果不是,请根据实际路径导入
import { AbcComponent } from './abc/abc.component';
import { DefComponent } from './def/def.component';

export const routes: Routes = [
  { path: 'abc', component: AbcComponent },
  { path: 'def', component: DefComponent }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }
<router-outlet></router-outlet>
<nav>
  <a routerLink="/abc">abc</a>
  <hr>
  <a routerLink="/def">def</a>
</nav>
<router-outlet></router-outlet>

上述具体的工作流程,可以举例简单描述为:

- 当浏览器地址栏的URL变化时,路径部分/home满足了列表中path为"home"的这个路由定义,激活对应HomeComponent的实例,显示它的视图

- 当应用程序请求导航到路径/hello时,符合了另外的规则,激活对应视图且展示内容,并将该路径更新到浏览器地址栏和历史

6.2路由嵌套

​   父子路由嵌套配置:

const routes: Routes = [
    {path:'abc',
      component:AbcComponent,
      children:[
        {path:'ghi',component:GhiComponent}
      ]
    },
];

在abc Component内这是router-outlet路由出口,即可在 abc 路由内渲染子级路由​ 在非home Component内跳转到/home/hello路由需要写全路径

//app template
<h2>app Component</h2>
<a [routerLink]="['/abc/ghi']">hello</a>
<router-outlet></router-outlet>

6.3路由传参

6.3.1使用路由参数

这是最基本的传参方式,适用于传递少量数据,如用户ID或产品ID。

1)定义带参数的路由

// app.module.ts定义带参数的路由
import { Routes } from '@angular/router';

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

2)访问路由参数

// product.component.ts
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Params } from '@angular/router';

@Component({
  selector: 'app-product',
  template: `Size: {{ size }}, Color: {{ color }}`
})
export class ProductComponent implements OnInit {
  size: string;
  color: string;

  constructor(private route: ActivatedRoute) {}

  ngOnInit() {
    this.route.queryParams.subscribe(params => {
      this.size = params['size'];
      this.color = params['color'];
    });
  }
}

6.3.2使用查询参数

1)定义查询参数的路由

// 通过 [queryParams] 在组件中绑定查询参数
this.router.navigate(['product'], { queryParams: { size: 'large', color: 'red' } });

2)访问查询参数

// product.component.ts
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Params } from '@angular/router';

@Component({
  selector: 'app-product',
  template: `Size: {{ size }}, Color: {{ color }}`
})
export class ProductComponent implements OnInit {
  size: string;
  color: string;

  constructor(private route: ActivatedRoute) {}

  ngOnInit() {
    this.route.queryParams.subscribe(params => {
      this.size = params['size'];
      this.color = params['color'];
    });
  }
}

6.3.3使用服务传递数据

如果你需要在多个组件之间共享路由参数,可以使用服务来存储和传递数据。

1)创建服务

// data.service.ts
import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class DataService {
  private data = new BehaviorSubject<any>(null);

  getData() {
    return this.data.asObservable();
  }

  setData(value) {
    this.data.next(value);
  }
}

2)在路由中使用服务

// 在导航时设置数据
this.dataService.setData(someData);
this.router.navigate(['product'], { queryParams: { id: someId } });

在组件中使用服务

// product.component.ts
import { Component, OnInit } from '@angular/core';
import { DataService } from './data.service';

@Component({
  selector: 'app-product',
  template: `Data: {{ data }}`
})
export class ProductComponent implements OnInit {
  data: any;

  constructor(private dataService: DataService) {}

  ngOnInit() {
    this.dataService.getData().subscribe(data => {
      this.data = data;
    });
  }
}

  • 29
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值