Angular中的服务被用来定义与特定视图无关的数据和逻辑,这些数据和逻辑可以跨组件共享。在本教程中,我们将学习如何创建一个服务,然后利用依赖注入来在一个特定的组件或类中使用该服务。如果你在其他面向对象的编程语言中使用过依赖注入,你就会感到宾至如归。
创建服务类
服务只是一个有特定用途的类。你可以将服务类用于那些应该可以跨组件重用的功能。它们的行为是独立于任何一个单一组件的。它们的作用还在于允许你在任何特定的组件类中拥有更少的代码,因为你只需注入你需要的东西作为一个依赖。这里,我们将创建一个服务,为我们的应用程序获取游戏列表。

game.service.ts
服务的格式与创建一个组件的Typescript文件非常相似,不同的是我们使用了@Injectable()装饰器。
import {Injectable} from '@angular/core';
import {IGame} from './game';
@Injectable()
export class GameService {
getGames(): IGame[] {
return [
{
'gameId': 1,
'gameName': 'Castlevania',
'gameCode': 'AAA-0101',
'releaseDate': 'September 26, 1986',
'description': 'Action-adventure game series created and developed by Konami.',
'price': 19.99,
'thumbRating': 5.0,
'imageUrl': './assets/images/castlevania.png'
},
{
'gameId': 2,
'gameName': 'Dr Mario',
'gameCode': 'AAA-1100',
'releaseDate': 'July 27, 1990',
'description': 'Action puzzle game produced by Gunpei Yokoi and published by Nintendo.',
'price': 15.99,
'thumbRating': 3,
'imageUrl': './assets/images/drmario.png'
},
{
'gameId': 3,
'gameName': 'Kid Icarus',
'gameCode': 'AAA-0048',
'releaseDate': 'December 19, 1986',
'description': 'Kid Icarus revolves around protagonist Pits quest for three sacred treasures.',
'price': 20.99,
'thumbRating': 4,
'imageUrl': './assets/images/kidicarus.png'
},
{
'gameId': 4,
'gameName': 'Legend Of Zelda',
'gameCode': 'AAA-0049',
'releaseDate': 'February 21, 1986',
'description': 'Link is often given the task of rescuing Princess Zelda and the kingdom of Hyrule from Ganon.',
'price': 29.95,
'thumbRating': 5,
'imageUrl': './assets/images/legendofzelda.png'
},
{
'gameId': 7,
'gameName': 'Super Mario Bros',
'gameCode': 'AAA-3325',
'releaseDate': 'September 13, 1985',
'description': 'Mario finds power-ups and items that give him special magic powers such as fireball-throwing and size-changing into giant and miniature sizes.',
'price': 40.95,
'thumbRating': 5,
'imageUrl': './assets/images/supermariobros.png'
}
];
}
}
注册服务
为了使用服务,它必须在Angular上注册。这将创建一个服务类的实例,然后由Angular Injector管理。为了注册我们的服务,我们可以将*{ providedIn: 'root' }对象传递给@Injectable()*装饰器。
import {Injectable} from '@angular/core';
import {IGame} from './game';
@Injectable({
providedIn: 'root'
})
export class GameService {
getGames(): IGame[] {
return [
{
'gameId': 1,
'gameName': 'Castlevania',
'gameCode': 'AAA-0101',
'releaseDate': 'September 26, 1986',
'description': 'Action-adventure game series created and developed by Konami.',
'price': 19.99,
'thumbRating': 5.0,
'imageUrl': './assets/images/castlevania.png'
},
{
'gameId': 2,
'gameName': 'Dr Mario',
'gameCode': 'AAA-1100',
'releaseDate': 'July 27, 1990',
'description': 'Action puzzle game produced by Gunpei Yokoi and published by Nintendo.',
'price': 15.99,
'thumbRating': 3,
'imageUrl': './assets/images/drmario.png'
},
{
'gameId': 3,
'gameName': 'Kid Icarus',
'gameCode': 'AAA-0048',
'releaseDate': 'December 19, 1986',
'description': 'Kid Icarus revolves around protagonist Pits quest for three sacred treasures.',
'price': 20.99,
'thumbRating': 4,
'imageUrl': './assets/images/kidicarus.png'
},
{
'gameId': 4,
'gameName': 'Legend Of Zelda',
'gameCode': 'AAA-0049',
'releaseDate': 'February 21, 1986',
'description': 'Link is often given the task of rescuing Princess Zelda and the kingdom of Hyrule from Ganon.',
'price': 29.95,
'thumbRating': 5,
'imageUrl': './assets/images/legendofzelda.png'
},
{
'gameId': 7,
'gameName': 'Super Mario Bros',
'gameCode': 'AAA-3325',
'releaseDate': 'September 13, 1985',
'description': 'Mario finds power-ups and items that give him special magic powers such as fireball-throwing and size-changing into giant and miniature sizes.',
'price': 40.95,
'thumbRating': 5,
'imageUrl': './assets/images/supermariobros.png'
}
];
}
}
将服务注入到一个组件中
为了使用现在已经注册的服务,我们可以使用依赖注入,将服务类的一个实例传递到组件类中。这是在构造函数中完成的,下面突出显示。
game-list.component.ts
import {Component, OnInit} from '@angular/core';
import {IGame} from './game';
import {GameService} from './game.service';
@Component({
selector: 'game-list',
templateUrl: './game-list.component.html',
styleUrls: ['./game-list.component.css']
})
export class GameListComponent implements OnInit {
pageTitle = 'Dynamic! Game List';
imageWidth = 45;
imageMargin = 1;
showImage = true;
_listFilter = '';
get listFilter(): string {
return this._listFilter;
}
set listFilter(value: string) {
this._listFilter = value;
this.filteredGames = this.listFilter ? this.doFilter(this.listFilter) : this.games;
}
filteredGames: IGame[] = [];
games: IGame[] = [];
constructor(private gameService: GameService) {
this.listFilter = '';
}
onRatingClicked(message: string): void {
this.pageTitle = 'Game List: ' + message;
}
doFilter(filterBy: string): IGame[] {
filterBy = filterBy.toLocaleLowerCase();
return this.games.filter((game: IGame) =>
game.gameName.toLocaleLowerCase().indexOf(filterBy) !== -1);
}
toggleImage(): void {
this.showImage = !this.showImage;
}
ngOnInit(): void {
this.games = this.gameService.getGames();
this.filteredGames = this.games;
}
}
注意在上面的ngOnInit()生命周期钩子的片段中,我们能够利用存在于外部服务类中的一个方法(getGames())。这是因为服务在之前被注入到这个组件类中。很好!
现在,在查看应用程序时,游戏列表仍然存在。我们并没有改变任何功能,但我们确实完成了代码重构。通过将获取游戏的责任从 game-list.component.ts转移到 game.service.ts 中--我们使游戏列表组件自己做了更少的工作,因此意味着更少的代码和更干净的语法。此外,如果有任何其他组件想获取游戏列表,它们也可以简单地注入一个服务类的实例并这样做。这在几乎所有的面向对象的编程风格中都是一个非常流行的模式。

总结
在本教程中,我们学习了如何在Angular中创建一个新的服务类,然后使用依赖注入来在任何我们喜欢的地方使用它。
- 构建一个服务的过程与构建一个组件的过程大致相同
- 为类命名,以便代表它所做的事情
- 按照惯例,使用PascalCasing
- 在类的名字后面加上 "服务"。
- 当组件使用@Component()装饰器时,服务将使用@Injectable()装饰器。
- 通过向@Injectable()装饰器传递一个对象并设置providedIn属性来注册服务。
- 要在一个组件中使用服务类,通过类的构造函数将该服务注入组件中。