一直想写关于 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",{ "id": 3,"label": "fugiat veniam minus",{ "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: ` <div> My Todo List: <ul> <li ng-repeat="todo in $ctrl.todos"> {{ todo.label }} </li> </ul> </div> `,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,"completed": false },{ "id": 2,{ "id": 3,{ "id": 4,"completed": true },{ "id": 5,"completed": false }]; } }
@Injectable() and DI
接下来我们使用 @Injectable
类装饰器,来装饰 TodoSevice
类:
import {Injectable} from '@angular/core'; @Injectable() export default class TodoService { getTodos(): Array<{ id: number,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: ` <div> My Todo List: <ul> <li *ngFor="let todo of todos"> {{ todo.label }} </li> </ul> </div> ` }) export default class TodoComponent implements OnInit { public todos: Array<{ id: number,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: ` <todo></todo> ` }) 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