QQ登录

只需一步,快速开始

Angular笔记:依赖注入

依梦瑶 发表于 2019-5-23 14:25:26 | 显示全部楼层 |阅读模式

这次学习的是 Angular 的依赖注入,一开始练习时就遇到了 Angular  中的一个坑,这个坑是什么?请往下看。

在这次练习中首先中首先创建一个空的 Angular 项目,然后再在项目中建立一个名为“user”的组件,然后再使用“ng g service”命令来创建一个服务组件。就在这一步遇到问题。

使用命令:

  1. ng g service share/user
复制代码

在执行完后出现了“Error: ELOOP: too many symbolic links encountered”错误,这个错误折磨了我大半天,终于 google 到的解决方法是将将项目中的node_modules 文件删除,使用 npm install命令重新生成该文件,不要使用cnpm install命令!

所说在 Angular Cli 使用ng g 命令创建新的 module 时,也会出现相同的问题(虽然我还没用过这个),解决的方法同上。

接下来就用一些实例来描述。

一、简单的依赖注入与调取

1.1、创建一个用户服务组件

上面有创建服务组件的命令,这里就不多写了。然后再在这个服务的 TS 文件里面输入以下的代码:

  1. import {Injectable} from '@angular/core';
  2. @Injectable()
  3. export class UserService {
  4.   constructor() {
  5.   }
  6.   getUser(): User {
  7.     return new User(1, 'IBM');
  8.   }
  9. }
  10. export class User {
  11.   constructor(public id: number,
  12.               public name: string) {
  13.   }
  14. }
复制代码

1.2、在 USER 组件的修改

控制器中输入以下调取的代码:

  1. import {Component, OnInit} from '@angular/core';
  2. import {User, UserService} from "../share/user.service";
  3. @Component({
  4.   selector: 'app-user',
  5.   templateUrl: './user.component.html',
  6.   styleUrls: ['./user.component.css']
  7. })
  8. export class UserComponent implements OnInit {
  9.   private user:User;
  10.   constructor(public userService: UserService) {
  11.   }
  12.   ngOnInit() {
  13.     this.user = this.userService.getUser();
  14.   }
  15. }
复制代码

在 USER 组件的模板中输入以下呈现的代码:

  1. <p>
  2.   user works!
  3. </p>
  4. <p>
  5.   依赖注入 Demo
  6. </p>
  7. <h1>用户信息</h1>
  8. <p>
  9.   用户ID:{{user.id}}<br>
  10.   用户名:{{user.name}}
  11. </p>
复制代码

1.3、项目的应用(app.component)的模板中输入显示用户组件的代码:

  1. <app-user></app-user>
复制代码

1.4、在 AppModule 中引入用户服务组件:

  1. ……
  2. import { AppComponent } from './app.component';
  3. import { UserComponent } from './user/user.component';
  4. import {UserService} from "./share/user.service";
  5. @NgModule({
  6.   declarations: [
  7.     AppComponent,
  8.     UserComponent
  9.   ],
  10.   imports: [
  11.     BrowserModule
  12.   ],
  13.   providers: [UserService],
  14.   bootstrap: [AppComponent]
  15. })
  16. ……
复制代码

下图为执行后的效果

1.png

二、作用域

继续上面的第一大步,在项目中分别创建 user2 组件和 anotherUser 服务。

2.1、在 anotherUser 服务中将 user 服务注入进来

注入之后要实现 user 服务里面所有的方法,代码 如下:

  1. import { Injectable } from '@angular/core';
  2. import {User, UserService} from "./user.service";
  3. @Injectable()
  4. export class AnotherUserService implements UserService{
  5.   getUser(): User {
  6.     // 上一步是返回 IBM,这次是返回 google
  7.     return new User(2, 'google');
  8.   }
  9.   constructor() { }
  10. }
复制代码

2.2、在 user2 组件的控制器中注入 anotherUser 服务

在注入 anotherUser 服务之后,就可调取的 anotherUser 服务中的方法。代码如下:

  1. import { Component, OnInit } from '@angular/core';
  2. import {User, UserService} from "../share/user.service";
  3. import {AnotherUserService} from "../share/another-user.service";
  4. @Component({
  5.   selector: 'app-user2',
  6.   templateUrl: './user2.component.html',
  7.   styleUrls: ['./user2.component.css'],
  8.   // 这里的 useClass 相当于 new,就是将实例化的 AnotherUserService 注入进去
  9.   providers: [{provide: UserService, useClass: AnotherUserService}]
  10. })
  11. export class User2Component implements OnInit {
  12.   private user: User;
  13.   constructor(public userService: UserService) { }
  14.   ngOnInit() {
  15.     this.user = this.userService.getUser();
  16.   }
  17. }
复制代码

user2 组件的模板代码为:

  1. <p>
  2.   user2 works!
  3. </p>
  4. <h1>用户信息</h1>
  5. <p>
  6.   用户ID:{{user.id}}<br>
  7.   用户名:{{user.name}}
  8. </p>
复制代码

2.3、将 user2 组件加到应用(app.component)的模板中。

  1. <app-user></app-user>
  2. <app-user2></app-user2>
复制代码

通过这样的修改的效果。

2.png

这是因为层级不同,user server 是的主层级中执行的,而 anotherUser 是声明在 user 组件这一级中。

三、服务之间如何互相注入

在这个项目中再添加一个 logger 服务组件,然后将这个组件注入到 user 服务组件中。

3.1、建立 LoggerService 组件里面的代码是:

  1. import { Injectable } from '@angular/core';
  2. @Injectable()
  3. export class LoggerService {
  4.   constructor() { }
  5.   log(message:string){
  6.     console.log(message);
  7.   }
  8. }
复制代码

3.2、将 LoggerService 注入到 UserService 组件里面

  1. import {Injectable} from '@angular/core';
  2. import {LoggerService} from "./logger.service";
  3. @Injectable()
  4. export class UserService {
  5.   constructor(public logger: LoggerService) {
  6.   }
  7.   getUser(): User {
  8.     this.logger.log("getUser方法被调用");
  9.     return new User(1, 'IBM');
  10.   }
  11. }
  12. export class User {
  13.   constructor(public id: number,
  14.               public name: string) {
  15.   }
  16. }
复制代码

3.3、同样的也将 LoggerService 也注入到 AnotherUserService 组件里面

因为 anotherUser 服务里面已经注入了 anotherUser 服务,并实现了 user 服务里面的所有方法,现在 user 服务里面的构造函数里面注入了 LoggerService ,所以 anotherUser 服务的构造函数也和 user 服务的构造函数一样写上 LoggerService 的注入。

  1. import { Injectable } from '@angular/core';
  2. import {User, UserService} from "./user.service";
  3. import {LoggerService} from "./logger.service";
  4. @Injectable()
  5. export class AnotherUserService implements UserService{
  6.   getUser(): User {
  7.     // 上一步是返回 IBM,这次是返回 google
  8.     return new User(2, 'google');
  9.   }
  10.   constructor(public logger: LoggerService) { }
  11. }
复制代码

3.4、在 AppModule 中也要引入 LoggerService 服务组件

  1. ……
  2. import { AppComponent } from './app.component';
  3. import { UserComponent } from './user/user.component';
  4. import {UserService} from "./share/user.service";
  5. import { User2Component } from './user2/user2.component';
  6. import {LoggerService} from "./share/logger.service";
  7. @NgModule({
  8.   declarations: [
  9.     AppComponent,
  10.     UserComponent,
  11.     User2Component,
  12.   ],
  13.   imports: [
  14.     BrowserModule
  15.   ],
  16.   providers: [UserService, LoggerService],
  17.   bootstrap: [AppComponent]
  18. })
  19. ……
复制代码

下图便是运行之后的效果

3.png

四、使用工厂方法或者值对象来定义提供器

这里继续使用前面的项目。

4.1、将 User2Component 里面的删除之前注入的代码:

  1. providers: [{provide: UserService, useClass: AnotherUserService}]
复制代码

删除之后 UserComponant 与 User2Component 将共同 UserService 一个提供器。因为组件级的提供器已经被删除了。

4.2、对 AppModule 中模块声明进行修改

  1. ……
  2. import { AppComponent } from './app.component';
  3. import { UserComponent } from './user/user.component';
  4. import {UserService} from "./share/user.service";
  5. import { User2Component } from './user2/user2.component';
  6. import {LoggerService} from "./share/logger.service";
  7. import {AnotherUserService} from "./share/another-user.service";
  8. @NgModule({
  9.   declarations: [
  10.     AppComponent,
  11.     UserComponent,
  12.     User2Component,
  13.   ],
  14.   imports: [
  15.     BrowserModule
  16.   ],
  17.   providers: [
  18.     // 在这里使用工厂函数来实例化 UserServer
  19.     {provide: UserService, useFactory: ()=>{
  20.       let logger = new LoggerService();
  21.       // 采用随机数的不同,调取不同的服务
  22.       let dev = Math.random() > 0.5;
  23.       if (dev) {
  24.         return new UserService(logger);
  25.       } else {
  26.         return new AnotherUserService(logger);
  27.       }
  28.     }}, LoggerService],
  29.   bootstrap: [AppComponent]
  30. })
  31. ……
复制代码

使用工厂方法 new 出来的对象是单一对象,工厂方法只会在创建第一个需要注入的对象时被调用一次,然后整个运用中所有被注入 provide 的实例都是同一个对象。虽然这里在 component 中被声明了再次,但是工作方法只会被调一次。

4.3、如何在工厂方式中使用提供器

在 4.2 的例子中看出在工厂方式中手工实例化了 LoggerService,这说明工厂方法与 LoggerService 这个类是紧密的耦合在了一起,而耦合肯定是个不好的事情,而实际下边是有声明了 LoggerService 这个提供器的。那么如何在工厂方式中使用提供器?解决方法加一个“deps”属性,代码如下:

  1. ……
  2. @NgModule({
  3.   declarations: [
  4.     AppComponent,
  5.     UserComponent,
  6.     User2Component,
  7.   ],
  8.   imports: [
  9.     BrowserModule
  10.   ],
  11.   providers: [
  12.     // 在这里使用工厂函数来实例化 UserServer
  13.     {provide: UserService, useFactory: (logger:LoggerService)=>{
  14.       let dev = Math.random() > 0.5;
  15.       if (dev) {
  16.         return new UserService(logger);
  17.       } else {
  18.         return new AnotherUserService(logger);
  19.       }
  20.     }, deps: [LoggerService]}, LoggerService],
  21.   bootstrap: [AppComponent]
  22. })
  23. ……
复制代码

4.4、在工厂方法是否可以将变量注入进来

在这里使用 useValue 来声明一个提供器。然后在 deps 里面写想要 token ,代码如下:

  1. @NgModule({
  2.   declarations: [
  3.     AppComponent,
  4.     UserComponent,
  5.     User2Component,
  6.   ],
  7.   imports: [
  8.     BrowserModule
  9.   ],
  10.   providers: [
  11.     // 在这里使用工厂函数来实例化 UserServer
  12.     {provide: UserService, useFactory: (logger:LoggerService, isDev)=>{
  13.       console.log(isDev);
  14.       if (isDev) {
  15.         return new UserService(logger);
  16.       } else {
  17.         return new AnotherUserService(logger);
  18.       }
  19.     }, deps: [LoggerService, "IS_DEV_ENV"]}, LoggerService,
  20.     {provide: "IS_DEV_ENV", useValue: false}],
  21.   bootstrap: [AppComponent]
  22. })
复制代码

“useValue”这里面还可以是个对象,比如:

  1. @NgModule({
  2.   declarations: [
  3.     AppComponent,
  4.     UserComponent,
  5.     User2Component,
  6.   ],
  7.   imports: [
  8.     BrowserModule
  9.   ],
  10.   providers: [
  11.     // 在这里使用工厂函数来实例化 UserServer
  12.     {provide: UserService, useFactory: (logger:LoggerService, isDev)=>{
  13.       console.log(isDev);
  14.       if (isDev) {
  15.         return new UserService(logger);
  16.       } else {
  17.         return new AnotherUserService(logger);
  18.       }
  19.     }, deps: [LoggerService, "IS_DEV_ENV"]}, LoggerService,
  20.     {provide: "IS_DEV_ENV", useValue: {isDev:true}}],
  21.   bootstrap: [AppComponent]
  22. })
复制代码

五、手工使用注入器来获取想要的依赖

在其他框架中就存在着有很多的注入点,而在 Angular 里面只有在构造函数。比如可以将 User2Component 改成:

  1. ……
  2. export class User2Component implements OnInit {
  3.   private user: User;
  4.   public userService: UserService;
  5.   // Injector 就是 Angular 的注入器
  6.   constructor(public injector:Injector) {
  7.     this.userService = this.injector.get(UserService);
  8.   }
  9.   ngOnInit() {
  10.     this.user = this.userService.getUser();
  11.   }
  12. }
  13. ……
复制代码

在写代码时最好必免使用这么写代码,除非确实需要它,一、实际上这种方式是错误的方式,没法做自动化测试。二、通过构造函数你没法知道它需要什么,做什么。

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

精华推荐
  • 探究!做设计之前的“构思”

    探究!做设计之前的“构思”

  • 汉字之美!中文字体设计原则

    汉字之美!中文字体设计原则

  • 一支互联网雪糕的诞生

    一支互联网雪糕的诞生

  • 设计灵感来自何处?

    设计灵感来自何处?

  • 自行车停靠架和旧自行车变废为宝家居创意作品大全

    自行车停靠架和旧自行车变废为宝家居创意作

  • 造车生死局:要么转型,要么死

    造车生死局:要么转型,要么死

  • 从欠8千万到年赚8亿

    从欠8千万到年赚8亿

  • 一座非典型五线小城的日常

    一座非典型五线小城的日常

QQ客服热线
QQ:1090281100 周一至周日:09:00 - 21:00
WeChat:duzhe1069
Email:kaixin1069@vip.qq.com

优创意logo

勿要吝啬你无形资产,请为创新续源,知识、点子、灵感、经验、需求等均是创新源泉,你不经意的一句话将是另一个人的灵感。明天的明天,还有明天,我们应该把握今天,每一个今天,都有一个新的事物在出现,今天的漠视明天的落后,不浪费每一个学习的时刻,学习助力非凡。

技术支持 Discuz! X3.4 - 3.5 beta © 2001-2019 Comsenz Inc.

小黑屋|手机版|优创意 ( 粤ICP备16085288号-1 )|申请友链

粤公网安备 44011102001144 号 GMT+8, 2020-10-31 01:44 , Processed in 0.093464 second(s), 29 queries , Gzip On.

快速回复 返回顶部 返回列表