原文链接:hacking-with-angular
这篇文章我们来讲解如何使用service(服务)
,谈及服务我们就要了解什么是服务;在Angular
中,我们所说的服务是指那些能够被其它的组件或者指令调用的单一的,可共享的代码块.服务能够使我们提高代码的利用率,方便组件之间共享数据和方法,方便测试和维护.
如果你看了上一篇文章Step 5 - Dependency Injection,你就会发现我们这一部分讲解的内容和上一篇有很多的相似之处;当然也有一些新的知识点,温故而知新嘛;好了让我们来开始今天的旅行吧.
首先我们还是切换回之前的quickstart版本,然后运行:
npm run start
可以看到My First Angular2 Travel
,然后继续我们的铺垫工作;主要是三个部分:
1.将我们的组件模板使用单一的html
模板来替代;
2.构建User
类方便我们后面的使用;
3.构建我们的模拟数据,方便我们使用服务来获取这些数据;
首先我们来完成第一部分,修改app.component.ts
中@Component
的元数据:改为:template: '
My First Angular2 Travel
'templateUrl: 'app/templates/main.html'
,然后我们在main.html
中书写我们的模板代码:
My First Angular2 Travel
然后我们来进行第二个工作,创建我们的User
类;为了展示方便,我们就给User
两个属性吧,一个是id(number)
,一个是name(string)
;文件的路径是:app/classes/User.ts
,具体的代码如下:
export class User {
//id: number;
//name: string;
constructor(
private id: number,
private name: string
){}
}
上面注释的部分是这个User
类的简写,这个根据个人的喜好;你喜欢写就怎么写.
最后一项工作就是来创造我们的模拟数据,我们现在还没有学习如何在Angular2
中使用HTTP
,所以我们暂时将这些数据记录在一个文件中,然后导出这些数据,供我们接下来使用.我们的模拟数据文件路径是:app/mock/user.data.ts
;下面是代码部分:
import {User} from "../classes/User";
export const USERS: User[] = [
{id: 1, name: 'dreamapple1'},
{id: 2, name: 'dreamapple2'},
{id: 3, name: 'dreamapple3'},
{id: 4, name: 'dreamapple4'},
{id: 5, name: 'dreamapple5'},
{id: 6, name: 'dreamapple6'},
{id: 7, name: 'dreamapple7'},
{id: 8, name: 'dreamapple8'}
];
可以看到,我们导出了一个数组,这个数组的每一个元素都是一个User
类的实例.
接下来,我们就要步入今天的主题了;构建一个服务,这个服务能够获取我们刚刚书写的模拟数据;首先不要忘记的是,service
是一个类,然后这个类可以注入到别的组件
或者指令中去,还有一点就是我们知道,获取数据的服务往往都是异步的,所以我们使用了Promise
去封装我们获取到的数据,来模拟异步请求;文件的路径是:app/service/user.service.ts
,我们也遵循一个约定,服务的文件后缀是*.service.ts
,前面的单词如果是多个的话就使用短横线来连接,比如SpecialUserService
我们就写成special-user.service.ts
,详细的部分请看代码:
import {User} from "../classes/User";
import {USERS} from "../mock/user.data";
export class UserService {
getUsers(): User[] {
return Promise.resolve(USERS);
}
}
关于上面代码的一些解释:因为我们的getUsers
函数是有返回值的,它的返回值是一个数组,数组的每一个元素都是User
类的实例,所以我们使用了getUsers(): User[]
,还有因为我们要模拟异步请求获取数据,所以我们使用了Promise
,如果你对Promise
有什么不懂的地方,可以看看这里.
接下来我们就要使用这个服务了,如何使用这个服务呢?在上一章节中我们已经讲解了许多种使用服务的方法;现在我们使用最简单的一种方式,直接使用providers
来注入我们的服务,然后我们还要把我们获取到的数据展示到我们的模板中,具体的代码如下所示:
import {Component} from '@angular/core';
import {User} from "./classes/User";
import {UserService} from "./services/user.service";
/*
* 别忘记了使用@前缀
* 这里相当于组件视图
*/
@Component({
selector: 'my-app',
//template: 'My First Angular2 Travel
',
templateUrl: 'app/templates/main.html',
providers: [UserService]
})
/*
* 导出这个组件,也就是一个类
* 这里相当于组件控制器
*/
export class AppComponent {
users: User[];
constructor(private userService: UserService){
//noinspection TypeScriptUnresolvedFunction
this.userService.getUsers().then(
users => this.users = users
)
}
}
我们也要改动main.html
的内容:
My First Angular2 Travel
- {{user.name}}
上面的写法是有一些问题的,构造函数是为了简单的初始化工作而设计的,比如把构造函数的参数赋值给属性.它的负担不应该过于沉重.所以我们把数据的获取放在了组件的生命周期的钩子函数中去,如果你不了解组件的生命周期的话,那么你可以看看这里,在这里我们使用了ngOnInit
;我们修改一下上面的代码:
import {Component, OnInit} from '@angular/core';
import {User} from "./classes/User";
import {UserService} from "./services/user.service";
/*
* 别忘记了使用@前缀
* 这里相当于组件视图
*/
@Component({
selector: 'my-app',
//template: 'My First Angular2 Travel
',
templateUrl: 'app/templates/main.html',
providers: [UserService]
})
/*
* 导出这个组件,也就是一个类
* 这里相当于组件控制器
*/
export class AppComponent implements OnInit{
users: User[];
constructor(
private userService: UserService
){}
getUsersData() {
this.userService.getUsers()
.then(users => this.users = users);
}
ngOnInit() {
this.getUsersData();
}
}
上面代码的一些解释,首先我们在@angular/core
中导出了OnInit
这个接口,然后我们又通过组件中的ngOnInit
方法实现了这个接口;将构造函数中的获取数据的业务提取了出来,这种做法是组件初始化的时候获取数据比较好的一种方案.我们在后面的文章中也会讲解关于组件或者指令生命周期的文章.
最后,我们还可以更真实的的去模拟从服务器读取数据的操作;我们可以通过使用setTimeout
来延时获取我们的数据,这就很好地模拟了我们从服务器获取数据的操作;具体的代码部分看下面:
import {User} from "../classes/User";
import {USERS} from "../mock/user.data";
export class UserService {
getUsers(): Promise {
return Promise.resolve(USERS);
}
getMockUsers(): Promise {
return new Promise(resolve => setTimeout(resolve(USERS), 2000))
.then(() => this.getUsers());
}
}
首先需要注意的一点是,我们之前写的代码把getUsers()
函数的返回值定义为User[]
,其实更准确的应该是Promise
;我们接下来写的函数getMockUsers()
利用setTimeout
延时返回了我们的模拟数据.
其实我们的程序里还有一个小错误,不容易被发现;当我在构建的时候我发现了下面的错误:
它提醒我们说,Property 'id' is private in type 'User' but not in type '{ id: number; name: string; }'.
这是因为我们把id
作为User
的私有属性了,但是在{ id: number; name: string; }
对象中,id
不是私有的属性;要解决这个问题有多种思路,你可以将我们的模拟数据使用User
类来创建,或者将id
和name
作为public
属性.那我们就取一个简单的方法,将属性定义为public
.User
类的代码修改如下:
export class User {
id: number;
name: string;
//constructor(
// private id: number,
// private name: string
//){}
}
在TypeScript里,每个成员默认为是public的.所以上面的写法是很简便的.
到这里我们要说的内容已经说完了,源代码可以参考这里angular2-travel,当然欢迎批评指正.