这篇文章我们将会聚焦于服务和依赖注入。同时也会学到lifecycle hook的ngOnInit以及Promise如何进行异步服务的处理。
大概需要10分钟
程序目前的做法是使用一个包含10个数据的对象数组提供数据,但是实际的项目中一般比这要复杂很多,比如数据往往由后端提供,同时除了性能之外,我们还需要测试的方便性,总不能测试的时候伴随着大量的代码修正才能进行吧,所以都放在app.component.ts中显然是不合适的,虽然不能一步到位,我们可以先把提供数据的对象数组给移出来,专门用一个名为mock-heroes.ts文件用于存放此数据,在mock-heroes.ts文件中export HEROES数组,具体代码如下:
/workspace/HelloAngular/src/app # cat mock-heroes.ts
import { Hero } from './hero';
export const HEROES: Hero[] = [
{ id: 11, name: 'Mr. Nice' },
{ id: 12, name: 'Narco' },
{ id: 13, name: 'Bombasto' },
{ id: 14, name: 'Celeritas' },
{ id: 15, name: 'Magneta' },
{ id: 16, name: 'RubberMan' },
{ id: 17, name: 'Dynama' },
{ id: 18, name: 'Dr IQ' },
{ id: 19, name: 'Magma' },
{ id: 20, name: 'Tornado' }
];
/workspace/HelloAngular/src/app #
把数据移出去之后,app.component.ts文件变得轻快了一些:
/workspace/HelloAngular/src/app # cat app.component.ts
import { Component } from '@angular/core';
import { Hero } from './hero'
import { HEROES } from './mock-heroes'
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'Tour of Heroes';
heroes = HEROES;
selectedHero: Hero;
onSelect(hero: Hero): void {
this.selectedHero = hero;
}
}
/workspace/HelloAngular/src/app #
但是这其实只是一个简单的物理位置移动而已,接下来我们把其拆出来一个提供数据的独立的服务HeroService.创建一个文件名为hero.service.ts的文件,我们现在要做的事情是通过这个服务来提供Hero数据,app.component.ts将会不直接去看mock-heroes.ts,内容很简单,具体代码如下:
/workspace/HelloAngular/src/app # cat hero.service.ts
import { Injectable } from '@angular/core';
import { Hero } from './hero';
import { HEROES } from './mock-heroes';
export class HeroService {
getHeroes(): Hero[] {
return HEROES;
}
}
/workspace/HelloAngular/src/app #
如何调用服务,首先用一下官网严重不推荐但最容易理解的方式,直接new一下。你说不好我就不用岂不是很没有面子,看看怎么new
/workspace/HelloAngular/src/app # cat app.component.ts
import { Component } from '@angular/core';
import { Hero } from './hero'
import { HeroService } from './hero.service'
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'Tour of Heroes';
selectedHero: Hero;
heroService: HeroService;
heroes: Hero[];
onSelect(hero: Hero): void {
this.selectedHero = hero;
}
constructor() {
this.heroService = new HeroService();
this.heroes = this.heroService.getHeroes();
}
}
/workspace/HelloAngular/src/app #
我们new出来了一个HeroService,然后通过它成功去到了数据,但是也会得到深度解耦依赖症患者的严重愤怒,我们不能理解他们的愤怒就像他们不能理解我们面对西二旗最后一趟车的焦虑一样,虽然西二旗没有最后一趟车,现在已经能够调用到了之后,我们看看如何使用依赖注入的方式吧。
删除new所在的构建语句,所需要的依赖由Angular来注入吧,删除完毕之后,app.component.ts成了这个样子:
整体还好,构造函数提供一个私有的HeroService的变量,然后我们的程序就不再管构建HeroService的对象,直接用就好,这样还可以省去一个类的成员变量HeroService。
/workspace/HelloAngular/src/app # cat app.component.ts
import { Component } from '@angular/core';
import { Hero } from './hero'
import { HeroService } from './hero.service'
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
providers: [HeroService]
})
export class AppComponent {
title = 'Tour of Heroes';
selectedHero: Hero;
heroes: Hero[];
onSelect(hero: Hero): void {
this.selectedHero = hero;
}
constructor(private heroService: HeroService) {
this.heroes = heroService.getHeroes();
}
}
/workspace/HelloAngular/src/app #
虽然我们不构建HeroService对象,并不意味这没有人去构建,我们需要提供足够信息让Angular去做,所以在@Component装饰器中非常重要的那行”providers: [HeroService]”就起到了这个作用,这样终于没有new了,大家的焦虑都可以稍微缓解一下了,其实也没有那么神奇,无非工厂模式或者变种的实现而已。
我们现在的数据写在构造函数里面也会引起很多不必要的问题,比如至少构建对象很复杂,构建对象很复杂的话就会引起很多重大问题:连个对象都没有,什么时候才能结婚,什么时候才能实现你的中国梦。不写在构造函数里面写在哪里:ngOnInit里面就可以,ngOnInit是个什么鬼,看他的名字你就知道是初期化的嘛。总而言之,没有对象非常严重,赶紧解决。可是现在构造函数里面就只有一句了,也要移走麽,移到什么地方呢?可以创建一个函数,然后再ngOnInit中调用,这里直接写道ngOnInit中以确认执行过程即可:
然后就做如下几件事情:
原来一行代码实现的,显现看起来似乎更麻烦,然后再LifeCycle的OnInit中来调用这一行,让人都不想取数据了。不过大型的项目解耦起来比这要复杂太多了,看似麻烦,其实也是有具体的应用场景的,不再赘述。我们到目前为止已经写成了如下的代码:
/workspace/HelloAngular/src/app # cat app.component.ts
import { Component } from '@angular/core';
import { OnInit } from '@angular/core';
import { Hero } from './hero';
import { HeroService } from './hero.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
providers: [HeroService]
})
export class AppComponent implements OnInit {
title = 'Tour of Heroes';
selectedHero: Hero;
heroes: Hero[];
onSelect(hero: Hero): void {
this.selectedHero = hero;
}
ngOnInit(): void{
this.heroes = this.heroService.getHeroes();
}
constructor(private heroService: HeroService) {
}
}
/workspace/HelloAngular/src/app #
程序的服务很多时候都是异步的,浏览器并不会像阻塞式socket编程那样一直等待而是会立即返回。这种情况下我们可以使用ES6中所支持的Promise方式。
使用Promise方式,HeroService的写法需要进行改变,代码修改如下:
/workspace/HelloAngular/src/app # cat hero.service.ts
import { Injectable } from '@angular/core';
import { Hero } from './hero';
import { HEROES } from './mock-heroes';
@Injectable()
export class HeroService {
getHeroes(): Promise {
return Promise.resolve(HEROES);
}
}
/workspace/HelloAngular/src/app #
与之相对应的,app.component.ts中方式也需要重写,所以改写ngOnInit中的写法:
/workspace/HelloAngular/src/app # cat app.component.ts
import { Component } from '@angular/core';
import { OnInit } from '@angular/core';
import { Hero } from './hero';
import { HeroService } from './hero.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
providers: [HeroService]
})
export class AppComponent implements OnInit {
title = 'Tour of Heroes';
selectedHero: Hero;
heroes: Hero[];
onSelect(hero: Hero): void {
this.selectedHero = hero;
}
ngOnInit(): void{
this.heroService.getHeroes().then(heroes => this.heroes = heroes);
}
constructor(private heroService: HeroService) {
}
}
/workspace/HelloAngular/src/app #
通过这篇文章,我们学习到了Angular的服务依赖注入的方式以及OnInit等Lifehook的使用方式,以及如何使用Promise的方式进行异步方式的服务提供,下篇文章我们将会学习一下Angular的路由。