Angular基础保姆级教程 - 1

Angular 基础总结(完结版)

1. 概述

Angular 是一个使用 HTML、CSS、TypeScript 构建客户端应用的框架,用来构建单页应用程序。

Angular 是一个重量级的框架,内部集成了大量开箱即用的功能模块。

Angular 为大型应用开发而设计,提供了干净且松耦合的代码组织方式,使应用程序整洁更易于维护。

Angular Angular 中文 Angular CLI

2. 架构预览

在这里插入图片描述

2.1 模块

Angular 应用是由一个个模块组成的,此模块指的不是ESModule,而是 NgModule 即 Angular 模块。

NgModule 是一组相关功能的集合,专注于某个应用领域,可以将组件和一组相关代码关联起来,是应用组织代码结构的一种方式。

在 Angular 应用中至少要有一个根模块,用于启动应用程序。

NgModule 可以从其它 NgModule 中导入功能,前提是目标 NgModule 导出了该功能。

NgModule 是由 NgModule 装饰器函数装饰的类。

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

@NgModule({
  imports: [
    BrowserModule
  ]
})
export class AppModule { }
2.2 组件

组件用来描述用户界面,它由三部分组成,组件类、组件模板、组件样式,它们可以被集成在组件类文件中,也可以是三个不同的文件。

组件类用来编写和组件直接相关的界面逻辑,在组件类中要关联该组件的组件模板和组件样式。

组件模板用来编写组件的 HTML 结构,通过数据绑定标记将应用中数据和 DOM 进行关联。

组件样式用来编写组件的组件的外观,组件样式可以采用 CSS、LESS、SCSS、Stylus

在 Angular 应用中至少要有一个根组件,用于应用程序的启动。

组件类是由 Component 装饰器函数装饰的类。

import { Component } from "@angular/core"

@Component({
  selector: "app-root",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent {
  title = "angular-test"
}

NgModule 为组件提供了编译的上下文环境。

import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }
2.3 服务

服务用于放置和特定组件无关并希望跨组件共享的数据或逻辑。

服务出现的目的在于解耦组件类中的代码,是组件类中的代码干净整洁。

服务是由 Injectable 装饰器装饰的类。

import { Injectable } from '@angular/core';

@Injectable({})
export class AppService { }

在使用服务时不需要在组件类中通过 new 的方式创建服务实例对象获取服务中提供的方法,以下写法错误,切记切记!!!

import { AppService } from "./AppService"

export class AppComponent {
  let appService = new AppService()
}

服务的实例对象由 Angular 框架中内置的依赖注入系统创建和维护。服务是依赖需要被注入到组件中。

在组件中需要通过 constructor 构造函数的参数来获取服务的实例对象。

涉及参数就需要考虑参数的顺序问题,因为在 Angular 应用中会有很多服务,一个组件又不可能会使用到所有服务,如果组件要使用到最后一个服务实例对象,难道要将前面的所有参数都写上吗 ? 这显然不合理。

在组件中获取服务实例对象要结合 TypeScript 类型,写法如下。

import { AppService } from "./AppService"

export class AppComponent {
  constructor (
  	private appService: AppService
  ) {}
}

Angular 会根据你指定的服务的类型来传递你想要使用的服务实例对象,这样就解决了参数的顺序问题。

在 Angular 中服务被设计为单例模式,这也正是为什么服务可以被用来在组件之间共享数据和逻辑的原因。

3. 快速开始

3.1 创建应用
  1. 安装 angular-cli:npm install @angular/cli -g

  2. 创建应用:ng new angular-test --minimal --inlineTemplate false

    1. –skipGit=true
    2. –minimal=true
    3. –skip-install
    4. –style=css
    5. –routing=false
    6. –inlineTemplate
    7. –inlineStyle
    8. –prefix

在这里插入图片描述
3. 运行应用:ng serve

  1. –open=true 应用构建完成后在浏览器中运行

  2. –hmr=true 开启热更新

  3. hmrWarning=false 禁用热更新警告

  4. –port 更改应用运行端口

  5. 访问应用:localhost:4200
    在这里插入图片描述

3.2 默认代码解析
3.2.1 main.ts
// enableProdMode 方法调用后将会开启生产模式
import { enableProdMode } from "@angular/core"
// Angular 应用程序的启动在不同的平台上是不一样的
// 在浏览器中启动时需要用到 platformBrowserDynamic 方法, 该方法返回平台实例对象
import { platformBrowserDynamic } from "@angular/platform-browser-dynamic"
// 引入根模块 用于启动应用程序
import { AppModule } from "./app/app.module"
// 引入环境变量对象 { production: false }
import { environment } from "./environments/environment"

// 如果当前为生产环境
if (environment.production) {
  // 开启生产模式
  enableProdMode()
}
// 启动应用程序
platformBrowserDynamic()
  .bootstrapModule(AppModule)
  .catch(err => console.error(err))

在这里插入图片描述

3.2.2 environment.ts
// 在执行 `ng build --prod` 时, environment.prod.ts 文件会替换 environment.ts 文件
// 该项配置可以在 angular.json 文件中找到, projects -> angular-test -> architect -> configurations -> production -> fileReplacements

export const environment = {
  production: false
}

#####3.2.3 environment.prod.ts

export const environment = {
  production: true
}
3.2.4 app.module.ts
// BrowserModule 提供了启动和运行浏览器应用所必需的服务
// CommonModule 提供各种服务和指令, 例如 ngIf 和 ngFor, 与平台无关
// BrowserModule 导入了 CommonModule, 又重新导出了 CommonModule, 使其所有指令都可用于导入 BrowserModule 的任何模块 
import { BrowserModule } from "@angular/platform-browser"
// NgModule: Angular 模块装饰器
import { NgModule } from "@angular/core"
// 根组件
import { AppComponent } from "./app.component"
// 调用 NgModule 装饰器, 告诉 Angular 当前类表示的是 Angular 模块
@NgModule({
  // 声明当前模块拥有哪些组件
  declarations: [AppComponent],
  // 声明当前模块依赖了哪些其他模块
  imports: [BrowserModule],
  // 声明服务的作用域, 数组中接收服务类, 表示该服务只能在当前模块的组件中使用
  providers: [],
  // 可引导组件, Angular 会在引导过程中把它加载到 DOM 中
  bootstrap: [AppComponent]
})
export class AppModule {}
3.2.5 app.component.ts
import { Component } from "@angular/core"

@Component({
  // 指定组件的使用方式, 当前为标记形式
  // app-home   =>  <app-home></app-home>
	// [app-home] =>  <div app-home></div>
  // .app-home  =>  <div class="app-home"></div>
  selector: "app-root",
  // 关联组件模板文件
  // templateUrl:'组件模板文件路径'
	// template:`组件模板字符串`
  templateUrl: "./app.component.html",
  // 关联组件样式文件
  // styleUrls : ['组件样式文件路径']
	// styles : [`组件样式`]
  styleUrls: ["./app.component.css"]
})
export class AppComponent {}
3.2.6 index.html
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>AngularTest</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>

在这里插入图片描述

3.3 共享模块

共享模块当中放置的是 Angular 应用中模块级别的需要共享的组件或逻辑。

  1. 创建共享模块: ng g m shared

  2. 创建共享组件:ng g c shared/components/Layout

  3. 在共享模块中导出共享组件

    @NgModule({
      declarations: [LayoutComponent],
      exports: [LayoutComponent]
    })
    export class SharedModule {}
    
  4. 在根模块中导入共享模块

    @NgModule({
      declarations: [AppComponent],
      imports: [SharedModule],
      bootstrap: [AppComponent]
    })
    export class AppModule {}
    
  5. 在根组件中使用 Layout 组件

    @Component({
      selector: "app-root",
      template: `
        <div>App works</div>
        <app-layout></app-layout>
      `,
      styles: []
    })
    export class AppComponent { }
    

4. 组件模板

4.1 数据绑定

数据绑定就是将组件类中的数据显示在组件模板中,当组件类中的数据发生变化时会自动被同步到组件模板中(数据驱动 DOM )。

在 Angular 中使用差值表达式进行数据绑定,即 {{ }} 大胡子语法。

<h2>{{message}}</h2>
<h2>{{getInfo()}}</h2>
<h2>{{a == b ? '相等': '不等'}}</h2>
<h2>{{'Hello Angular'}}</h2>
<p [innerHTML]="htmlSnippet"></p> <!-- 对数据中的代码进行转义 -->
4.2 属性绑定
4.2.1 普通属性

属性绑定分为两种情况,绑定 DOM 对象属性和绑定HTML标记属性。

  1. 使用 [属性名称] 为元素绑定 DOM 对象属性。
<img [src]="imgUrl"/>
  1. 使用 [attr.属性名称] 为元素绑定 HTML 标记属性
<td [attr.colspan]="colSpan"></td> 

在大多数情况下,DOM 对象属性和 HTML 标记属性是对应的关系,所以使用第一种情况。但是某些属性只有 HTML 标记存在,DOM 对象中不存在,此时需要使用第二种情况,比如 colspan 属性,在 DOM 对象中就没有,或者自定义 HTML 属性也需要使用第二种情况。

4.2.2 class 属性
<button class="btn btn-primary" [class.active]="isActive">按钮</button>
<div [ngClass]="{'active': true, 'error': true}"></div>
4.2.3 style 属性
<button [style.backgroundColor]="isActive ? 'blue': 'red'">按钮</button>
<button [ngStyle]="{'backgroundColor': 'red'}">按钮</button>
4.3 事件绑定
<button (click)="onSave($event)">按钮</button>
<!-- 当按下回车键抬起的时候执行函数 -->
<input type="text" (keyup.enter)="onKeyUp()"/>
export class AppComponent {
  title = "test"
  onSave(event: Event) {
    // this 指向组件类的实例对象
    this.title // "test"
  }
}
4.4 获取原生 DOM 对象
4.4.1 在组件模板中获取
<input type="text" (keyup.enter)="onKeyUp(username.value)" #username/>
4.4.2 在组件类中获取

使用 ViewChild 装饰器获取一个元素

<p #paragraph>home works!</p>
import { AfterViewInit, ElementRef, ViewChild } from "@angular/core"

export class HomeComponent implements AfterViewInit {
  @ViewChild("paragraph") paragraph: ElementRef<HTMLParagraphElement> | undefined
  ngAfterViewInit() {
    console.log(this.paragraph?.nativeElement)
  }
}

使用 ViewChildren 获取一组元素

<ul>
  <li #items>a</li>
  <li #items>b</li>
  <li #items>c</li>
</ul>
import { AfterViewInit, QueryList, ViewChildren } from "@angular/core"

@Component({
  selector: "app-home",
  templateUrl: "./home.component.html",
  styles: []
})
export class HomeComponent implements AfterViewInit {
  @ViewChildren("items") items: QueryList<HTMLLIElement> | undefined
  ngAfterViewInit() {
    console.log(this.items?.toArray())
  }
}
4.5 双向数据绑定

数据在组件类和组件模板中双向同步。

Angular 将双向数据绑定功能放在了 @angular/forms 模块中,所以要实现双向数据绑定需要依赖该模块。

import { FormsModule } from "@angular/forms"

@NgModule({
  imports: [FormsModule],
})
export class AppModule {}
<input type="text" [(ngModel)]="username" />
<button (click)="change()">在组件类中更改 username</button>
<div>username: {{ username }}</div>
export class AppComponent {
  username: string = ""
  change() {
    this.username = "hello Angular"
  }
}
4.6 内容投影
<!-- app.component.html -->
<bootstrap-panel>
	<div class="heading">
        Heading
  </div>
  <div class="body">
        Body
  </div>
</bootstrap-panel>
<!-- panel.component.html -->
<div class="panel panel-default">
  <div class="panel-heading">
    <ng-content select=".heading"></ng-content>
  </div>
  <div class="panel-body">
    <ng-content select=".body"></ng-content>
  </div>
</div>

如果只有一个ng-content,不需要select属性。

ng-content在浏览器中会被 <div class=“heading”></div> 替代,如果不想要这个额外的div,可以使用ng-container替代这个div。

<!-- app.component.html -->
<bootstrap-panel>
	<ng-container class="heading">
        Heading
    </ng-container>
    <ng-container class="body">
        Body
    </ng-container>
</bootstrap-panel>
4.7 数据绑定容错处理
// app.component.ts
export class AppComponent {
    task = {
        person: {
            name: '张三'
        }
    }
}
<!-- 方式一 -->
<span *ngIf="task.person">{{ task.person.name }}</span>
<!-- 方式二 -->
<span>{{ task.person?.name }}</span>
4.8 全局样式
/* 第一种方式 在 styles.css 文件中 */
@import "~bootstrap/dist/css/bootstrap.css";
/* ~ 相对node_modules文件夹 */
<!-- 第二种方式 在 index.html 文件中  -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" rel="stylesheet" />
// 第三种方式 在 angular.json 文件中
"styles": [
  "./node_modules/bootstrap/dist/css/bootstrap.min.css",
  "src/styles.css"
]

5. 指令 Directive

指令是 Angular 提供的操作 DOM 的途径。指令分为属性指令和结构指令。

属性指令:修改现有元素的外观或行为,使用 [] 包裹。

结构指令:增加、删除 DOM 节点以修改布局,使用*作为指令前缀

5.1 内置指令
5.1.1 *ngIf

根据条件渲染 DOM 节点或移除 DOM 节点。

<div *ngIf="data.length == 0">没有更多数据</div>
<div *ngIf="data.length > 0; then dataList else noData"></div>
<ng-template #dataList>课程列表</ng-template>
<ng-template #noData>没有更多数据</ng-template>
5.1.2 [hidden]

根据条件显示 DOM 节点或隐藏 DOM 节点 (display)。

<div [hidden]="data.length == 0">课程列表</div>
<div [hidden]="data.length > 0">没有更多数据</div>
5.1.3 *ngFor

遍历数据生成HTML结构

interface List {
  id: number
  name: string
  age: number
}

list: List[] = [
  { id: 1, name: "张三", age: 20 },
  { id: 2, name: "李四", age: 30 }
]
<li
    *ngFor="
      let item of list;
      let i = index;
      let isEven = even;
      let isOdd = odd;
      let isFirst = first;
      let isLast = last;
    "
  >
  </li>
<li *ngFor="let item of list; trackBy: identify"></li>
identify(index, item){
  return item.id; 
}
5.2 自定义指令

需求:为元素设置默认背景颜色,鼠标移入时的背景颜色以及移出时的背景颜色。

<div [appHover]="{ bgColor: 'skyblue' }">Hello Angular</div>
import { AfterViewInit, Directive, ElementRef, HostListener, Input } from "@angular/core"

// 接收参的数类型
interface Options {
  bgColor?: string
}

@Directive({
  selector: "[appHover]"
})
export class HoverDirective implements AfterViewInit {
  // 接收参数
  @Input("appHover") appHover: Options = {}
  // 要操作的 DOM 节点
  element: HTMLElement
	// 获取要操作的 DOM 节点
  constructor(private elementRef: ElementRef) {
    this.element = this.elementRef.nativeElement
  }
	// 组件模板初始完成后设置元素的背景颜色
  ngAfterViewInit() {
    this.element.style.backgroundColor = this.appHover.bgColor || "skyblue"
  }
	// 为元素添加鼠标移入事件
  @HostListener("mouseenter") enter() {
    this.element.style.backgroundColor = "pink"
  }
	// 为元素添加鼠标移出事件
  @HostListener("mouseleave") leave() {
    this.element.style.backgroundColor = "skyblue"
  }
}

6. 管道 Pipe

管道的作用是格式化组件模板数据。

6.1 内置管道
  1. date 日期格式化
  2. currency 货币格式化
  3. uppercase 转大写
  4. lowercase 转小写
  5. json 格式化json 数据
{{ date | date: "yyyy-MM-dd" }}
6.2 自定义管道

需求:指定字符串不能超过规定的长度

// summary.pipe.ts
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
   name: 'summary' 
});
export class SummaryPipe implements PipeTransform {
    transform (value: string, limit?: number) {
        if (!value) return null;
        let actualLimit = (limit) ? limit : 50;
        return value.substr(0, actualLimit) + '...';
    }
}
// app.module.ts
import { SummaryPipe } from './summary.pipe'
@NgModule({
    declarations: [
      SummaryPipe
    ] 
});

7. 组件通讯

7.1 向组件内部传递数据
<app-favorite [isFavorite]="true"></app-favorite>
// favorite.component.ts
import { Input } from '@angular/core';
export class FavoriteComponent {
    @Input() isFavorite: boolean = false;
}

注意:在属性的外面加 [] 表示绑定动态值,在组件内接收后是布尔类型,不加 [] 表示绑定普通值,在组件内接收后是字符串类型。

<app-favorite [is-Favorite]="true"></app-favorite>
import { Input } from '@angular/core';

export class FavoriteComponent {
  @Input("is-Favorite") isFavorite: boolean = false
}
7.2 组件向外部传递数据

需求:在子组件中通过点击按钮将数据传递给父组件

<!-- 子组件模板 -->
<button (click)="onClick()">click</button>
// 子组件类
import { EventEmitter, Output } from "@angular/core"

export class FavoriteComponent {
  @Output() change = new EventEmitter()
  onClick() {
    this.change.emit({ name: "张三" })
  }
}
<!-- 父组件模板 -->
<app-favorite (change)="onChange($event)"></app-favorite>
// 父组件类
export class AppComponent {
  onChange(event: { name: string }) {
    console.log(event)
  }
}

8. 组件生命周期

在这里插入图片描述

  1. constructor

    Angular 在实例化组件类时执行, 可以用来接收 Angular 注入的服务实例对象。

    export class ChildComponent {
      constructor (private test: TestService) {
        console.log(this.test) // "test"
      }
    }
    
  2. ngOnInit

    在首次接收到输入属性值后执行,在此处可以执行请求操作。

    <app-child name="张三"></app-child>
    
    export class ChildComponent implements OnInit {
      @Input("name") name: string = ""
      ngOnInit() {
        console.log(this.name) // "张三"
      }
    }
    
  3. ngAfterContentInit

    当内容投影初始渲染完成后调用。

    <app-child>
    	<div #box>Hello Angular</div>
    </app-child>
    
    export class ChildComponent implements AfterContentInit {
      @ContentChild("box") box: ElementRef<HTMLDivElement> | undefined
    
      ngAfterContentInit() {
        console.log(this.box) // <div>Hello Angular</div>
      }
    }
    
  4. ngAfterViewInit

    当组件视图渲染完成后调用。

    <!-- app-child 组件模板 -->
    <p #p>app-child works</p>
    
    export class ChildComponent implements AfterViewInit {
      @ViewChild("p") p: ElementRef<HTMLParagraphElement> | undefined
      ngAfterViewInit () {
        console.log(this.p) // <p>app-child works</p>
      }
    }
    
8.2 更新阶段
  1. ngOnChanges

    1. 当输入属性值发生变化时执行,初始设置时也会执行一次,顺序优于 ngOnInit
    2. 不论多少输入属性同时变化,钩子函数只会执行一次,变化的值会同时存储在参数中
    3. 参数类型为 SimpleChanges,子属性类型为 SimpleChange
    4. 对于基本数据类型来说, 只要值发生变化就可以被检测到
    5. 对于引用数据类型来说, 可以检测从一个对象变成另一个对象, 但是检测不到同一个对象中属性值的变化,但是不影响组件模板更新数据。

    基本数据类型值变化

    <app-child [name]="name" [age]="age"></app-child>
    <button (click)="change()">change</button>
    
    export class AppComponent {
      name: string = "张三";
    	age: number = 20
      change() {
        this.name = "李四"
        this.age = 30
      }
    }
    
    export class ChildComponent implements OnChanges {
      @Input("name") name: string = ""
    	@Input("age") age: number = 0
    
      ngOnChanges(changes: SimpleChanges) {
        console.log("基本数据类型值变化可以被检测到")
      }
    }
    

    引用数据类型变化

    <app-child [person]="person"></app-child>
    <button (click)="change()">change</button>
    
    export class AppComponent {
      person = { name: "张三", age: 20 }
      change() {
        this.person = { name: "李四", age: 30 }
      }
    }
    
    export class ChildComponent implements OnChanges {
      @Input("person") person = { name: "", age: 0 }
    
      ngOnChanges(changes: SimpleChanges) {
        console.log("对于引用数据类型, 只能检测到引用地址发生变化, 对象属性变化不能被检测到")
      }
    }
    
  2. ngDoCheck:主要用于调试,只要输入属性发生变化,不论是基本数据类型还是引用数据类型还是引用数据类型中的属性变化,都会执行。

  3. ngAfterContentChecked:内容投影更新完成后执行。

  4. ngAfterViewChecked:组件视图更新完成后执行。

8.3 卸载阶段
  1. ngOnDestroy

    当组件被销毁之前调用, 用于清理操作。

    export class HomeComponent implements OnDestroy {
      ngOnDestroy() {
        console.log("组件被卸载")
      }
    }
    

9. 依赖注入

9.1 概述

依赖注入 ( Dependency Injection ) 简称DI,是面向对象编程中的一种设计原则,用来减少代码之间的耦合度

class MailService {
  constructor(APIKEY) {}
}

class EmailSender {
  mailService: MailService
  constructor() {
    this.mailService = new MailService("APIKEY1234567890")
  }

  sendMail(mail) {
    this.mailService.sendMail(mail)
  }
}

const emailSender = new EmailSender()
emailSender.sendMail(mail)

EmailSender 类运行时要使用 MailService 类,EmailSender 类依赖 MailService 类,MailService 类是 EmailSender 类的依赖项。

以上写法的耦合度太高,代码并不健壮。如果 MailService 类改变了参数的传递方式,在 EmailSender 类中的写法也要跟着改变。

class EmailSender {
  mailService: MailService
  constructor(mailService: MailService) {
    this.mailService = mailService;
  }
}
const mailService = new MailService("APIKEY1234567890")
const emailSender = new EmailSender(mailService)

在实例化 EmailSender 类时将它的依赖项通过 constructor 构造函数参数的形式注入到类的内部,这种写法就是依赖注入。

通过依赖注入降了代码之间的耦合度,增加了代码的可维护性。MailService 类中代码的更改再也不会影响 EmailSender 类。

9.2 DI 框架

Angular 有自己的 DI 框架,它将实现依赖注入的过程隐藏了,对于开发者来说只需使用很简单的代码就可以使用复杂的依赖注入功能。

在 Angular 的 DI 框架中有四个核心概念:

  1. Dependency:组件要依赖的实例对象,服务实例对象
  2. Token:获取服务实例对象的标识
  3. Injector:注入器,负责创建维护服务类的实例对象并向组件中注入服务实例对象。
  4. Provider:配置注入器的对象,指定创建服务实例对象的服务类和获取实例对象的标识。
9.2.1 注入器 Injectors

注入器负责创建服务类实例对象,并将服务类实例对象注入到需要的组件中。

  1. 创建注入器

    import { ReflectiveInjector } from "@angular/core"
    // 服务类
    class MailService {}
    // 创建注入器并传入服务类
    const injector = ReflectiveInjector.resolveAndCreate([MailService])
    
  2. 获取注入器中的服务类实例对象

    const mailService = injector.get(MailService)
    
  3. 服务实例对象为单例模式,注入器在创建服务实例后会对其进行缓存

    const mailService1 = injector.get(MailService)
    const mailService2 = injector.get(MailService)
    
    console.log(mailService1 === mailService2) // true
    
  4. 不同的注入器返回不同的服务实例对象

    const injector = ReflectiveInjector.resolveAndCreate([MailService])
    const childInjector = injector.resolveAndCreateChild([MailService])
    
    const mailService1 = injector.get(MailService)
    const mailService2 = childInjector.get(MailService)
    
    console.log(mailService1 === mailService2)
    
  5. 服务实例的查找类似函数作用域链,当前级别可以找到就使用当前级别,当前级别找不到去父级中查找

    const injector = ReflectiveInjector.resolveAndCreate([MailService])
    const childInjector = injector.resolveAndCreateChild([])
    
    const mailService1 = injector.get(MailService)
    const mailService2 = childInjector.get(MailService)
    
    console.log(mailService1 === mailService2)
    
9.2.2 提供者 Provider
  1. 配置注入器的对象,指定了创建实例对象的服务类和访问服务实例对象的标识。

    const injector = ReflectiveInjector.resolveAndCreate([
      { provide: MailService, useClass: MailService }
    ])
    
  2. 访问依赖对象的标识也可以是字符串类型

    const injector = ReflectiveInjector.resolveAndCreate([
      { provide: "mail", useClass: MailService }
    ])
    const mailService = injector.get("mail")
    
  3. useValue

    const injector = ReflectiveInjector.resolveAndCreate([
      {
        provide: "Config",
        useValue: Object.freeze({
          APIKEY: "API1234567890",
          APISCRET: "500-400-300"
        })
      }
    ])
    const Config = injector.get("Config")
    

将实例对象和外部的引用建立了松耦合关系,外部通过标识获取实例对象,只要标识保持不变,内部代码怎么变都不会影响到外部。

10. 服务 Service

10.1 创建服务
import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class TestService { }
export class AppComponent {
 	constructor (private testService: TestService) {}
}
10.2 服务的作用域

使用服务可以轻松实现跨模块跨组件共享数据,这取决于服务的作用域。

  1. 在根注入器中注册服务,所有模块使用同一个服务实例对象。

    import { Injectable } from '@angular/core';
    
    @Injectable({
      providedIn: 'root'
    })
    
    export class CarListService {
    }
    
  2. 在模块级别注册服务,该模块中的所有组件使用同一个服务实例对象。

    import { Injectable } from '@angular/core';
    import { CarModule } from './car.module';
    
    @Injectable({
      providedIn: CarModule,
    })
    
    export class CarListService {
    }
    
    import { CarListService } from './car-list.service';
    
    @NgModule({
      providers: [CarListService],
    })
    export class CarModule {
    }
    
  3. 在组件级别注册服务,该组件及其子组件使用同一个服务实例对象。

    import { Component } from '@angular/core';
    import { CarListService } from '../car-list.service.ts'
    
    @Component({
      selector:    'app-car-list',
      templateUrl: './car-list.component.html',
      providers:  [ CarListService ]
    })
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/780161.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Hadoop3:NameNode和DataNode多目录配置(扩充磁盘的技术支持)

一、NameNode多目录 1、说明 NameNode多目录&#xff0c;需要在刚搭建Hadoop集群的时候&#xff0c;就配置好 因为&#xff0c;配置这个&#xff0c;需要格式化NameNode 所以&#xff0c;如果一开始没配置NameNode多目录&#xff0c;后面&#xff0c;就不要配置了。 2、配置…

移动校园(3):处理全校课程数据excel文档,实现空闲教室查询与课程表查询

首先打开教学平台 然后导出为excel文档 import mathimport pandas as pd import pymssql serverName 127.0.0.1 userName sa passWord 123456 databaseuniSchool conn pymssql.connect(serverserverName,useruserName,passwordpassWord,databasedatabase) cursor conn.cur…

阶段三:项目开发---大数据开发运行环境搭建:任务6:安装配置HBase

任务描述 知识点&#xff1a;安装配置HBase 重 点&#xff1a; 安装配置HBase 难 点&#xff1a;无 内 容&#xff1a; 本阶段任务是安装配置HBase&#xff0c;实时飞行数据是保存在HBase中的&#xff0c;因为HBase具有高效的读写能力&#xff0c;在当前项目中我们是…

hive表小练习

-- 将对应的命令写在每个步骤中 -- 1.在hive中创建数据库school create database if not exists school; -- 2.在数据库school中创建如下的表 每张表的列分隔符都是, 存储格式是textfile 创建表名为student_info, 字段为stu_id 类型为string,注释为学生id 字段为stu_name 类…

Python酷库之旅-第三方库Pandas(008)

目录 一、用法精讲 16、pandas.DataFrame.to_json函数 16-1、语法 16-2、参数 16-3、功能 16-4、返回值 16-5、说明 16-6、用法 16-6-1、数据准备 16-6-2、代码示例 16-6-3、结果输出 17、pandas.read_html函数 17-1、语法 17-2、参数 17-3、功能 17-4、返回值…

element-ui输入框如何实现回显的多选样式?

废话不多说直接上效果&#x1f9d0; 效果图 <template><div><el-form:model"params"ref"queryForm"size"small":inline"true"label-width"68px"><el-form-item label"标签" prop"tag&q…

SSM高校教师教学质量评估系统-计算机毕业设计源码03344

摘要 在高等教育中&#xff0c;教学质量是培养优秀人才的关键。为了提高教学质量&#xff0c;高校需要建立一套科学、有效的教师教学质量评估系统。本研究采用 SSM技术框架&#xff0c;旨在开发一款高校教师教学质量评估系统。 SSM框架作为一种成熟的Java开发框架&#xff0c;具…

软件测试《用例篇》

测试用例 测试用例的概念 测试用例是被测试人员向被测试系统发起的一组集合&#xff0c;包括测试环境&#xff0c;操作步骤&#xff0c;预期结果&#xff0c;测试数据等 使用测试用例的好处 使用测试用例进行测试的好处主要有&#xff1a;提高测试效率&#xff0c;降低测试的重…

NextJs - SSR渲染解决antd首屏加载CSS样式的闪烁问题

NextJs - SSR渲染解决antd首屏加载CSS样式的闪烁问题 闪烁现状解决方案 闪烁现状 我们写一个非常简单的页面&#xff1a; import { Button } from antdexport default async function Page() {return <><Button typeprimary>AAA</Button></> }NextJs…

[答疑]EA中SysML块定义图的引用关联怎样在references分栏中出现

DDD领域驱动设计批评文集 做强化自测题获得“软件方法建模师”称号 《软件方法》各章合集 例如&#xff0c;用EA在BDD&#xff08;块定义图&#xff09;上画了一个关联&#xff1a; 此时&#xff0c;“手机”和“SIM卡”中都没有出现references栏。 右击“手机”&#xff0c…

Pytorch 实践手写数字识别深度学习网络 LeNet-5

Pytorch 实践手写数字识别深度学习网络 LeNet-5 文章目录 Pytorch 实践手写数字识别深度学习网络 LeNet-5认识 LeNet-5认识数据集处理数据集下载数据集读取数据定义Dataset的继承类把数据进行载入载入dataloader 编写网络编写训练与测试代码实践结果展示完整代码 训练手写体识别…

#数据结构 笔记一

数据结构是计算机存储、组织数据的方式。 数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。数据结构是带有结构特性的数据元素的集合&#xff0c;它研究的是数据的逻辑结构和物理结构以及它们之间的相互关系&#xff0c;并对这种结构定义相适应的运算&#xff0…

微软正在放弃React

最近&#xff0c;微软Edge团队撰写了一篇文章&#xff0c;介绍了微软团队如何努力提升Edge浏览器的性能。但在文中&#xff0c;微软对React提出了批评&#xff0c;并宣布他们将不再在Edge浏览器的开发中使用React。 我将详细解析他们的整篇文章内容&#xff0c;探讨这一决定对…

Java对象通用比对工具

目录 背景 思路 实现 背景 前段时间的任务中&#xff0c;遇到了需要识别两个对象不同属性的场景&#xff0c;如果使用传统的一个个属性比对equals方法&#xff0c;会存在大量的重复工作&#xff0c;而且为对象新增了属性后&#xff0c;比对方法也需要同步修改&#xff0c;不方…

微软拼音输入法不显示选字框问题

问题展示&#xff1a;不显示选字框 解决方式 打开兼容性即可&#xff08;估计是升级带来的bug&#xff09;

STM32 - 内存分区与OTA

最近搞MCU&#xff0c;发现它与SOC之间存在诸多差异&#xff0c;不能沿用SOC上一些技术理论。本文以STM L4为例&#xff0c;总结了一些STM32 小白入门指南。 标题MCU没有DDR&#xff1f; 是的。MCU并没有DDR&#xff0c;而是让代码存储在nor flash上&#xff0c;临时变量和栈…

LeetCode题练习与总结:直线上最多的点数--149

一、题目描述 给你一个数组 points &#xff0c;其中 points[i] [xi, yi] 表示 X-Y 平面上的一个点。求最多有多少个点在同一条直线上。 示例 1&#xff1a; 输入&#xff1a;points [[1,1],[2,2],[3,3]] 输出&#xff1a;3示例 2&#xff1a; 输入&#xff1a;points [[1,…

水箱高低水位浮球液位开关

水箱高低水位浮球液位开关概述 水箱高低水位浮球液位开关是一种用于监测和控制水箱中液位的自动化设备&#xff0c;它能够在水箱液位达到预设的高低限制时&#xff0c;输出开关信号&#xff0c;以控制水泵或电磁阀的开闭&#xff0c;从而维持水箱液位在一个安全的范围内。这类设…

STM32快速复习(八)SPI通信

文章目录 前言一、SPI是什么&#xff1f;SPI的硬件电路&#xff1f;SPI发送的时序&#xff1f;二、库函数二、库函数示例代码总结 前言 SPI和IIC通信算是我在大学和面试中用的最多&#xff0c;问的最多的通信协议 IIC问到了&#xff0c;一般SPI也一定会问到。 SPI相对于IIC多了…

3.js - 模板渲染 - 简单

3.js 真tm枯燥啊&#xff0c;狗都不学 效果图 源码 // ts-nocheck// 引入three.js import * as THREE from three// 导入轨道控制器 import { OrbitControls } from three/examples/jsm/controls/OrbitControls// 导入lil.gui import { GUI } from three/examples/jsm/libs/li…