服务(Service)
继学习笔记7,可以使用主从组件,现在继续学习(服务)Service;
问题:为什么需要服务?
因为:组件不应该直接获取或保存数据,它们不应该了解是否在展示假数据。 它们应该聚焦于展示数据,而把数据访问的职责委托给某个服务。
这次将创建一个
HeroService
,应用中的所有类都可以使用它来获取英雄列表。 不要使用
new
来创建此服务,而要依靠 Angular 的依赖注入机制把它注入到
HeroesComponent
的构造函数中
服务是在多个“互相不知道”的类之间共享信息的好办法。
1.创建HeroService
使用 Angular CLI 创建一个名叫
hero
的服务。
ng generate service hero
wjydeMacBook-Pro:demo wjy$ ng generate service hero
CREATE src/app/hero.service.spec.ts (362 bytes)
CREATE src/app/hero.service.ts (133 bytes)
执行完这个命令以后,打开
src/app/hero.service.ts
中生成
HeroService,
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class HeroService {
constructor() { }
}
@Injectable() 服务
这个新的服务导入了 Angular 的Injectable 符号,并且给这个服务类添加了
@
Injectable
()
装饰器。 它把这个类标记为依赖注入系统的参与者之一。
HeroService
类将会提供一个可注入的服务,并且它还可以拥有自己的待注入的依赖。 目前它还没有依赖.
@
Injectable
()
装饰器会接受该服务的元数据对象,就像
@Component()
对组件类的作用一样。
2.在HeroService中获取hero的数据
HeroService
可以从任何地方获取数据:Web 服务、本地存储(LocalStorage)或一个模拟的数据源。
从组件中移除数据访问逻辑,意味着将来任何时候你都可以改变目前的实现方式,而不用改动任何组件。
在HeroService中导入
Hero
和
HEROES,并写一个获取hero列表的方法:getHeroes()
import {Injectable} from '@angular/core';
import {Hero} from './hero';
import {HEROES} from './mock-heroes';
@Injectable({
providedIn: 'root'
})
export class HeroService {
constructor() {
}
getHeroes(): Hero[] {
return HEROES;
}
}
3.提供(provide) HeroService
HeroService
在要求 Angular 把
HeroService
注入到
HeroesComponent
之前,你必须先把这个服务提供给依赖注入系统。通过注册提供商来做到这一点。提供商用来创建和交付服务,在这个例子中,它会对
HeroService
类进行实例化,以提供该服务,需要确保
HeroService
已经作为该服务的提供商进行过注册。 你要用一个注入器注册它。注入器就是一个对象,负责在需要时选取和注入该提供商。默认情况下,Angular CLI 命令
ng generate service
会通过给
@Injectable
装饰器添加元数据的形式,为该服务把提供商注册到根注入器上。如果你看看
HeroService
紧前面的
@Injectable()
语句定义,就会发现
providedIn
元数据的值是 'root',当你在顶层提供该服务时,Angular 就会为
HeroService
创建一个单一的、共享的实例,并把它注入到任何想要它的类上。 在
@Injectable
元数据中注册该提供商,还能让 Angular 可以通过移除那些完全没有用过的服务,来进行优化。
注意:如果需要,可以在不同的层次上注册提供商 —— 在
HeroesComponent
中、在
AppComponent
中,或在
AppModule
中。 比如,可以通过附加
--module=app
参数来告诉 CLI 要自动在模块级提供该服务。
ng generate service hero --module=app
4.修改 HeroesComponent
HeroesComponent
打开
HeroesComponent
类文件。
删除
HEROES
的导入语句,因为你以后不会再用它了。 转而导入
HeroService,
把
heroes
属性的定义改为一句简单的声明。
import {Component, OnInit} from '@angular/core';
import {Hero} from '../hero';
@Component({
selector: 'app-heroes',
templateUrl: './heroes.component.html',
styleUrls: ['./heroes.component.css']
})
export class HeroesComponent implements OnInit {
hero: Hero = {
id: 1,
name: 'Windstorm',
};
heroes: Hero[];
selectedHero: Hero;
constructor() {
}
ngOnInit() {
}
onSelect(hero: Hero): void {
this.selectedHero = hero;
}
}
5.注入 HeroService
HeroService
往构造函数中添加一个私有的
heroService
,其类型为
HeroService
。
constructor(private heroService: HeroService) { }
添加 getHeroes()
getHeroes(): void {
this.heroes = this.heroService.getHeroes();
}
6.在 ngOnInit
中调用getHeroes()
ngOnInit
a.在构造方法中调用(不推荐)
在构造函数调用:getHeroes();
constructor(private heroService: HeroService) {
this.getHeroes();
}
这样也会达到之前应用的效果,如图
b.在 ngOnInit 生命周期钩子中调用
在ngOnInit 调用:getHeroes();
ngOnInit() {
this.getHeroes();
}
这样也会达到之前应用的效果,如图
两者的比较:
让构造函数保持简单,只做初始化操作,比如把构造函数的参数赋值给属性。 构造函数不应该做任何事。 它肯定不能调用某个函数来向远端服务(比如真实的数据服务)发起 HTTP 请求。
而是选择在 ngOnInit 生命周期钩子中调用 getHeroes(),之后交由 Angular 处理,它会在构造出 HeroesComponent 的实例之后的某个合适的时机调用 ngOnInit。