前集回顾
上一章里我们在AppComponent
里通过组合InputItem
、 CheckableItem
、 Counter
三个组件,并通过Unidirectional Data Flow
(单向数据流)的方式把她们驱动起来。今天这章,我们讲讲angular2
里的service
。
本章源码:service
本章使用angular2
版本为:2.4.5
,webpack
版本为: 2.2.0
先来看看我们将要完成的效果图:
需求分析@H_403_39@
(注意动画部分),在上一章的基础上我们加入了初始化数据,在数据加载完成前会有一个loading
,数据准备好之后loading
消失,列表显现。
设计use case@H_403_39@
每章都会提一下,先设计使用场景(这种方式,我们称之为"BDD",不了解的朋友参考以BDD手写依赖注入(dependency injection))。
重构ts/app.ts
import {Component,OnInit} from '@angular/core'; import {Item} from './CheckableItem'; //引入本章主题ItemService import {ItemService} from './ItemService'; @Component({ selector: 'my-app',template: ` <h1>My First Angular 2 App</h1> <input-item (onItemAdded)="addItem($event)"></input-item> <checkable-item *ngFor="let itemInfo of items; let i = index" [item]="itemInfo" (onItemClicked)="toggle($event,i)"> </checkable-item> <p *ngIf="loading">Loading</p> <counter *ngIf="!loading" [items]="items"></counter> `,directives: [InputItem,CheckableItem,Counter],//注入ItemService providers: [ItemService] }) export class AppComponent implements OnInit { items: Item[] = []; //声明loading状态,初始值为true loading: boolean = true; //通过构造器自动获取ItemService实例 constructor(private _itemService: ItemService) { } //在组件初始化以后调用ItemService获取初始化数据 ngOnInit() { this._itemService .getItems() .then(data => { //重置loading状态为false this.loading = false; //设置初始值 this.items = data; }); } addItem(item: Item) { this.items = [...this.items,item]; } toggle(item: Item,index: number) { this.items = [ ...this.items.slice(0,index),{ isChecked: !item.isChecked,txt: item.txt },...this.items.slice(index + 1) ]; } }
实现ItemService
@H_403_39@
touch ts/ItemService.ts
向刚创建的ts/ItemService.ts
中,添加如下内容:
import {Injectable} from '@angular/core'; import {Item} from './CheckableItem'; //用Injectable装饰器声明该类可被依赖注入 @Injectable() export class ItemService { //设置一个初始值数组 private items: Item[] = [ { isChecked: true,txt: 'Learn JavaScript' },{ isChecked: false,txt: 'Learn TypeScript' },txt: 'Learn Angular2' } ]; //提供一个方法,返回初始数据的Promise getItems(): Promise<Array<Item>> { return new Promise((resolve,reject) => { //这里手动做延迟是为了模拟网络请求 setTimeout(() => { resolve(this.items); },1500); }); } }
查看效果@H_403_39@
本章内容比较简单,写到这里差不多算结束了(其实还没有哦!),先来跑跑看
npm start
OK,我确信这个代码是可以运行的,那到底什么是service
?我们现在来对着代码讲一讲。
什么是service@H_403_39@
service
是可被替换的
service
必须通过依赖注入使用
service
通常用作数据存取等应用中可公用逻辑部分
如何定义service@H_403_39@
service
是可被替换的
service
必须通过依赖注入使用
service
通常用作数据存取等应用中可公用逻辑部分
必须通过@Injectable
装饰器声明
import {Injectable} from '@angular/core'; @Injectable() export class ItemService { }
使用service@H_403_39@
引入service
import {ItemService} from './ItemService';
切忌不要自作多情的
new
她哦!!!!!
构造器获取实例
constructor(private _itemService: ItemService) { }
自动注入实例
就像directives
那样,添加到@Component
的Metadata中
providers: [ItemService]
就这么简单,so easy 有木有?
重构@H_403_39@
那么我们说,到这里就结束了吗?请看下面,template
里有这么一段:
用了
*ngFor
将items
列表化用了
*ngIf
控制loading
的显示状态
是不是感觉有点儿矬了,如果能有个单独的ItemList
组件该多好?像这样使用:
import {Component,OnInit} from '@angular/core'; import {Item} from './CheckableItem'; import {ItemService} from './ItemService'; @Component({ selector: 'my-app',template: ` <h1>My First Angular 2 App</h1> <input-item (onItemAdded)="addItem($event)"></input-item> <item-list [data]="items" (onItemClicked)="toggle($event)" [showLoading]="loading"> </item-list> <counter *ngIf="!loading" [items]="items"></counter> `,providers: [ItemService] }) export class AppComponent implements OnInit { items: Item[] = []; loading: boolean = true; constructor(private _itemService: ItemService) { } ngOnInit() { this._itemService .getItems() .then(data => { this.loading = false; this.items = data; }); } addItem(item: Item) { this.items = [...this.items,item]; } toggle(e: { item: Item,index: number }) { this.items = [ ...this.items.slice(0,e.index),{ isChecked: !e.item.isChecked,txt: e.item.txt },...this.items.slice(e.index + 1) ]; } }
实现ItemList
touch ts/ItemList.ts
import {Component,Input,Output,EventEmitter,ChangeDetectionStrategy} from '@angular/core'; import { Item } from './CheckableItem'; @Component({ changeDetection: ChangeDetectionStrategy.OnPush,selector: 'item-list',template: ` <checkable-item *ngFor="let item of data; let i=index" [item]="item" (onItemClicked)="clickItem($event,i)"> </checkable-item> <p *ngIf="showLoading">Loading</p> ` }) export class ItemList { @Input() data: Item[]; @Input() showLoading: boolean; @Output() onItemClicked = new EventEmitter(); clickItem(e: Item,i: number) { this.onItemClicked.emit({ item: e,index: i }); } }
一切都结束了,效果仍然没有变,还是很屌的样子!!!!
下回预告:使用Routing