|
| 1 | +#添加用户服务 |
| 2 | + |
| 3 | +本节我们将创建用户服务 UserService。 UserService 专注于为视图提供数据。 |
| 4 | + |
| 5 | +同时, 不需要使用 new 来创建此服务的实例,而要依靠 Angular 的依赖注入机制把它注入到 UsersComponent 的构造函数中。 |
| 6 | + |
| 7 | + |
| 8 | +##创建 UserService |
| 9 | + |
| 10 | + |
| 11 | +使用 Angular CLI 创建一个名叫 user 的服务。 |
| 12 | + |
| 13 | +```ts |
| 14 | +nggenerateserviceuser |
| 15 | +``` |
| 16 | + |
| 17 | + |
| 18 | +可以在控制台看到如下输出信息: |
| 19 | + |
| 20 | +```ts |
| 21 | +nggenerateserviceuser |
| 22 | + |
| 23 | +CREATEsrc/app/user.service.spec.ts (362bytes) |
| 24 | +CREATEsrc/app/user.service.ts (133bytes) |
| 25 | +``` |
| 26 | + |
| 27 | +其中,该命令会在 src/app/user.service.ts 中生成的是 UserService 类的骨架。 UserService 类的代码如下: |
| 28 | + |
| 29 | +```ts |
| 30 | +import {Injectable }from'@angular/core'; |
| 31 | + |
| 32 | +@Injectable({ |
| 33 | + providedIn:'root' |
| 34 | +}) |
| 35 | +exportclassUserService { |
| 36 | + |
| 37 | +constructor() { } |
| 38 | +} |
| 39 | +``` |
| 40 | + |
| 41 | + |
| 42 | +在 src/app/user.service.ts 中生成的是 UserService 类的测试代码: |
| 43 | + |
| 44 | +```ts |
| 45 | +import {TestBed,inject }from'@angular/core/testing'; |
| 46 | + |
| 47 | +import {UserService }from'./user.service'; |
| 48 | + |
| 49 | +describe('UserService', ()=> { |
| 50 | +beforeEach(()=> { |
| 51 | +TestBed.configureTestingModule({ |
| 52 | + providers: [UserService] |
| 53 | + }); |
| 54 | + }); |
| 55 | + |
| 56 | +it('should be created',inject([UserService], (service:UserService)=> { |
| 57 | +expect(service).toBeTruthy(); |
| 58 | + })); |
| 59 | +}); |
| 60 | +``` |
| 61 | + |
| 62 | + |
| 63 | + |
| 64 | +##@Injectable() 服务 |
| 65 | + |
| 66 | +注意,这个新的服务导入了 Angular 的 Injectable 符号,并且给这个服务类添加了@Injectable() 装饰器。 UserService 类将会提供一个可注入的服务,并且它还可以拥有自己的待注入的依赖。 目前它还没有依赖,但是很快就会有了。 |
| 67 | + |
| 68 | +@Injectable() 装饰器会接受该服务的元数据对象,就像@Component() 对组件类的作用一样。 |
| 69 | + |
| 70 | +如果你熟悉 Java 的注解,那么这个@Injectable() 装饰器可以简单理解为是 Java 中的@Inject 注解。 |
| 71 | + |
| 72 | +##获取用户数据 |
| 73 | + |
| 74 | +UserService 职责是提供用户数据的查询,而使用这个服务的调用方是不需要关心 UserService 的数据来源的。UserService 可以从任何地方去获取数据:Web 服务、本地存储(LocalStorage)或一个模拟的数据源。 |
| 75 | + |
| 76 | +我们将要从组件中移除数据访问逻辑,将数据访问的逻辑移到 UserService 中,这样组件只需要依赖于 UserService 来提供数据。这意味着将来任何时候你都可以改变目前的 UserService 实现方式,而不用改动任何组件。因为,这些组件不需要了解该服务的内部实现。 |
| 77 | + |
| 78 | +我们将原来在组件中实现的“获取模拟的用户列表”的功能,迁移至 UserService 中。具体修改如下。 |
| 79 | + |
| 80 | +###导入 User 和 USERS |
| 81 | + |
| 82 | +导入 User 和 USERS: |
| 83 | + |
| 84 | +```ts |
| 85 | +import {User }from'./user'; |
| 86 | +import {USERS }from'./mock-users'; |
| 87 | +``` |
| 88 | + |
| 89 | +###添加一个 getUsers 方法 |
| 90 | + |
| 91 | +添加一个 getUsers 方法,让它返回模拟的用户列表。 |
| 92 | + |
| 93 | +```ts |
| 94 | +getUsers():User[] { |
| 95 | +returnUSERS; |
| 96 | +} |
| 97 | +``` |
| 98 | + |
| 99 | + |
| 100 | +##提供 UserService |
| 101 | + |
| 102 | +在要求 Angular 把 UserService 注入到 UsersComponent 之前,你必须先把这个服务提供给依赖注入系统。你可以通过注册提供商来做到这一点。提供商用来创建和交付服务,在这个例子中,它会对 UserService 类进行实例化,以提供该服务。 |
| 103 | + |
| 104 | +现在,你需要确保 UserService 已经作为该服务的提供商进行过注册。你要用一个注入器注册它。注入器就是一个对象,负责在需要时选取和注入该提供商。 |
| 105 | + |
| 106 | +默认情况下,Angular CLI 命令 ng generate service 会通过给@Injectable 装饰器添加元数据的形式,为该服务把提供商注册到根注入器上。 |
| 107 | + |
| 108 | +如果你看看 UserService 紧前面的@Injectable() 语句定义,就会发现 providedIn 元数据的值是 'root': |
| 109 | + |
| 110 | +```ts |
| 111 | +@Injectable({ |
| 112 | + providedIn:'root', |
| 113 | +}) |
| 114 | +``` |
| 115 | + |
| 116 | + |
| 117 | +当你在顶层提供该服务时,Angular 就会为 UserService 创建一个单一的、共享的实例,并把它注入到任何想要它的类上。 在@Injectable 元数据中注册该提供商,还能让 Angular 可以通过移除那些完全没有用过的服务,来进行优化。 |
| 118 | + |
| 119 | +如果需要,你也可以在不同的层次上注册提供商 —— 在 UsersComponent 中、在 AppComponent 中,或在 AppModule 中。 比如,你可以通过附加 --module=app 参数来告诉 CLI 要自动在模块级提供该服务。 |
| 120 | + |
| 121 | +```ts |
| 122 | +nggenerateserviceuser--module=app |
| 123 | +``` |
| 124 | + |
| 125 | + |
| 126 | + |
| 127 | +现在 UserService 已经准备好插入到 UsersComponent 中了。 |
| 128 | + |
| 129 | +##修改 UsersComponent |
| 130 | + |
| 131 | +打开 UsersComponent 类文件 users.component.ts。 |
| 132 | + |
| 133 | +删除 USERS 的导入语句,因为你以后不会再用它了。 转而导入 UserService。 |
| 134 | + |
| 135 | + |
| 136 | +```ts |
| 137 | +import {UserService }from'../user.service'; |
| 138 | +``` |
| 139 | + |
| 140 | +把 heroes 属性的定义改为一句简单的声明。 |
| 141 | + |
| 142 | +```ts |
| 143 | +users:User[]; |
| 144 | +``` |
| 145 | + |
| 146 | +###注入 UserService |
| 147 | + |
| 148 | +往构造函数中添加一个私有的 userService,其类型为 UserService。 |
| 149 | + |
| 150 | +```ts |
| 151 | +constructor(privateuserService:UserService) { } |
| 152 | +``` |
| 153 | + |
| 154 | +这个参数同时做了两件事: |
| 155 | + |
| 156 | +1. 声明了一个私有 userService 属性; |
| 157 | +2. 把它标记为一个 UserService 的注入点。 |
| 158 | + |
| 159 | +当 Angular 创建 UsersComponent 时,依赖注入系统就会把这个 userService 参数设置为 UserService 的单例对象。 |
| 160 | + |
| 161 | +###添加 getHeroes() |
| 162 | + |
| 163 | +创建一个函数,以便从服务中获取这些用户数据。 |
| 164 | + |
| 165 | +```ts |
| 166 | +getUsers():void { |
| 167 | +this.users=this.userService.getUsers(); |
| 168 | +} |
| 169 | +``` |
| 170 | + |
| 171 | +###在 ngOnInit 中调用它 |
| 172 | + |
| 173 | +你固然可以在构造函数中调用 getUsers(),但那不是最佳实践。 |
| 174 | + |
| 175 | +让构造函数保持简单,只做初始化操作,比如把构造函数的参数赋值给属性。构造函数不应该做任何事。它肯定不能调用某个函数来向远端服务(比如真实的数据服务)发起 HTTP 请求。 |
| 176 | + |
| 177 | +而是选择在 ngOnInit 生命周期钩子中调用 getUsers(),之后交由 Angular 处理,它会在构造出 UsersComponent 的实例之后的某个合适的时机调用 ngOnInit。 |
| 178 | + |
| 179 | +```ts |
| 180 | +ngOnInit() { |
| 181 | +this.getUsers(); |
| 182 | +} |
| 183 | +``` |
| 184 | + |
| 185 | +###查看运行效果 |
| 186 | + |
| 187 | +执行 ng serve 命名以启动应用,在浏览器中访问<http://localhost:4200/>。该应用的运行效果,应该跟之前是一样的。 |