一直想写关于 Angular 1.x 与 Angular 2.x (Angular 4.x 已发布) 区别的文章,方便 Angular 1.x 的用户快速的过渡到 Angular 2.x。在浏览文章的时候,发现 Todd Motto 大神,已经写了相关的系列文章。英文好的同学,建议直接阅读 Creating an Angular 2 Injectable Service 原文哈,因为我并不打算完整地翻译,另外需要注意的是原文的示例是使用ES 2015,本文 Angular 2 示例是使用 TypeScript哈。废话不多说,接下来我们开始进入正题。
目录
-
Angular 1.x
Service definition
Service DI
-
Angular 2
Service setup
@Injectable() and DI
Angular 1.x
在 Angular 1.x 中,我们通过使用 .service()
API 来创建服务。
Service Definition
我们使用 ES 2015 中的 Class
来定义服务,getTodos() 方法只是简单地返回 Todos 列表。在后续的部分,我们会引入 HTTP
模块。
class TodoService {
constructor() {}
getTodos() {
return [{
"id": 1,
"label": "delectus aut autem",
"completed": false
},{
"id": 2,
"label": "quis ut nam facilis et officia qui",
"completed": false
},{
"id": 3,
"label": "fugiat veniam minus",
"completed": false
},{
"id": 4,
"label": "et porro tempora",
"completed": true
},{
"id": 5,
"label": "laboriosam mollitia et enim quasi adipisci quia provident illum",
"completed": false
}];
}
}
angular
.module('app') // 获取已注册的app模块
.service('TodoService', TodoService); // 通过service API注册TodoService服务
接下来我们来注入 $http
服务,在 Angular 1.x 中声明依赖项的方式有3种,分为如下:
// 方式一: 使用 $inject annotation 方式 - 严格DI
var fn = function (a, b) {};
fn.$inject = ['a', 'b'];
// 方式二: 使用 array-style annotations 方式 - 严格DI
var fn = ['a', 'b', function (a, b) {}];
// 方式三: 使用隐式声明方式
var fn = function (a, b) {}; // 不推荐
我们使用第一种方式来声明依赖,具体代码如下:
class TodoService {
constructor($http) {
this.$http = $http;
}
getTodos() {
return [{..},{..},{..},{..},{..}];
}
}
TodoService.$inject = ['$http'];
angular
.module('app')
.service('TodoService', TodoService);
Service DI
const todo = {
template: `
My Todo List:
-
{{ todo.label }}
`,
controller(TodoService) {
$onInit() {
this.todos = TodoService.getTodos();
}
}
};
上面代码中,我们在 controller 中使用 $onInit
生命周期钩子,用于在组件初始化的时候,设置组件的初始数据。示例中的 getTodos() 是同步操作,如果使用 $http
服务从远程服务器获取数据的话,返回的是一个 Promise 对象,我们就需要在 then()
方法中进行 todos 属性的赋值操作。
(备注:有兴趣了解 Angular 1.x DI 内容的话,可以参考我之前的文章 - Angular 2 DI - IoC & DI - 1 )
Angular 2
Service setup
首先定义 TodoService 服务类 (使用TypeScript):
export class TodoService {
getTodos(): Array<{ id: number, label: string, completed: boolean }> {
return [{
"id": 1,
"label": "delectus aut autem",
"completed": false
}, {
"id": 2,
"label": "quis ut nam facilis et officia qui",
"completed": false
}, {
"id": 3,
"label": "fugiat veniam minus",
"completed": false
}, {
"id": 4,
"label": "et porro tempora",
"completed": true
}, {
"id": 5,
"label": "laboriosam mollitia et enim quasi adipisci quia provident illum",
"completed": false
}];
}
}
@Injectable() and DI
接下来我们使用 @Injectable
类装饰器,来装饰 TodoSevice
类:
import {Injectable} from '@angular/core';
@Injectable()
export default class TodoService {
getTodos(): Array<{ id: number, label: string, completed: boolean }> {
return [{..},{..},{..},{..},{..}];
}
}
然后,我们通过 @Component()
装饰器创建 todo 组件,为了跟 Angular 1.x 的示例一样,在组件初始化的时候,设置组件的初始数据,我们需要在组件中引入 OnInit 接口,并在组件类中实现该接口。具体示例如下:
import { Component, OnInit } from '@angular/core';
import { TodoService } from './services/todo.service';
@Component({
selector: 'todo',
template: `
My Todo List:
-
{{ todo.label }}
`
})
export default class TodoComponent implements OnInit {
public todos: Array<{ id: number, label: string, completed: boolean }>;
constructor(public todoService: TodoService) { } // 使用构造方式,注入TodoService
ngOnInit() {
this.todos = this.todoService.getTodos(); // 获取待办事项列表
}
}
app.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'exe-app',
template: `
`
})
export class AppComponent { }
app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import TodoComponent from './todo.component';
import { AppComponent } from './app.component';
import { TodoService } from './services/todo.service';
@NgModule({
imports: [ BrowserModule ],
declarations: [ TodoComponent, AppComponent ],
providers: [TodoService]
bootstrap: [ AppComponent ]
})
export class AppModule { }
以上成功运行后,浏览器中的显示结果如下:
我有话说
1.ngOnit 与 constructor 的区别和应用场景
在 Angular 2 中 constructor 一般用于依赖注入或执行简单的数据初始化操作,ngOnInit 钩子主要用于执行组件的其它初始化操作或获取组件输入的属性值。
详细内容请参考 - Angular 2 constructor & ngOnInit
2.@Injectable装饰器的作用
如果 TodoService 不依赖于其他对象,是可以不用使用 Injectable 类装饰器。当 TodoService 需要在构造函数中注入依赖对象,就需要使用 Injectable 类装饰器。比较推荐的做法不管是否有依赖对象,service 中都使用 Injectable 类装饰器。
详细内容请参考 - Angular 2 Inject