使用Redux和ngrx构建更好的Angular2应用(四)

我的Angular2项目:http://git.oschina.net/zt_zhong/CodeBe

原文地址:http://onehungrymind.com/build-better-angular-2-application-redux-ngrx/

调用所有服务

首先,将Http和Headers从angular2/http模块导入到应用中,准备Http调用。

import {Http, Headers} from 'angular2/http';

然后,我们定义一个BASE_URL常量,所以我们只需要键入一次,我们也将创建一个HEADER常量来告诉我们服务器我们如何与它进行通信。这可能不是必需的,这取决于你正在使用的后端,但是要让json-server工作,我不得不添加它。

const BASE_URL = 'http://localhost:3000/items/';
const HEADER = { headers: new Headers({ 'Content-Type': 'application/json' }) };

我们修改ItemsService的构造器,将Http对象作为类的一个http属性注入进来。

constructor(private http: Http, private store: Store<AppStore>) {
  this.items = store.select('items');
}

从这里,让我们修改我们的CRUD方法来处理远程服务器调用,从loadItems开始。我们正在调用this.http.get(BASE_URL)来获取我们的远程items,并且因为Http返回一个observable,所以我们可以通过额外的操作来管理结果。我们将调用map来解析返回结果然后再调用map来创建一个对象然后分发给对应的reducer。map操作的返回结果是一个Observable对象,因为每一个结果都是通过它来操作序列的。为了“捆绑”序列,我们将订阅它,然后通过调度我们的转换结果将控制权交给我们的reducer。

loadItems() {
  // Retrieves the items collection, parses the JSON, creates an event with the JSON as a payload,
  // and dispatches that event
  this.http.get(BASE_URL)
    .map(res => res.json())
    .map(payload => ({ type: 'ADD_ITEMS', payload }))
    .subscribe(action => this.store.dispatch(action));
}

当我们更新createItem时,我们将会遵循类似的模式。唯一的不同是,我们使用http.post和格式化了的请求体以及HEADER常量来访问服务器。一旦我们有了返回结果,我们将所有内容映射到我们可以在我们的订阅方法中发送的对象。

createItem(item: Item) {
  this.http.post(BASE_URL, JSON.stringify(item), HEADER)
    .map(res => res.json())
    .map(payload => ({ type: 'CREATE_ITEM', payload }))
    .subscribe(action => this.store.dispatch(action));
}

更新和删除有点简单,因为我们不依赖于从服务器返回的对象。我们只需要关注操作是否成功。因为这些,我们将使用http.put和http.delete,然后跳过返回结果的映射这一步。我们可以从subscribe中分发一个动作到reducer。看下面的代码:

updateItem(item: Item) {
  this.http.put(`${BASE_URL}${item.id}`, JSON.stringify(item), HEADER)
    .subscribe(action => this.store.dispatch({ type: 'UPDATE_ITEM', payload: item }));
}
deleteItem(item: Item) {
  this.http.delete(`${BASE_URL}${item.id}`)
    .subscribe(action => this.store.dispatch({ type: 'DELETE_ITEM', payload: item }));
}

测试

redux的最重要的方面之一是测试reducer是非常容易的,因为它们是具有明确意图的纯函数。关于我们的应用,包含可测试逻辑的表面积已大大减少。当我写这些的时候我并没有想搞笑,但事后看来,那就是!

配置

我不会进入整个测试工具,而是快速浏览我们的测试规范。我们需要做的第一步是导入items和selectedItems以及从angular2/testing中导入it,describe和expect。稍等。这不是Jasmine的方法吗??是的,Angular2默认使用的就是Jasmine。

import {items, selectedItem} from './items';
import {
  it,
  describe,
  expect
} from 'angular2/testing';

作为参考,我们的规格的骨架看起来像这样。

describe('Items', () => {
  describe('selectedItem store', () => {
    it('returns null by default', () => {});
    it('SELECT_ITEM returns the provided payload', () => {});
  });
  describe('items store', () => {
    let initialState = [
      { id: 0, name: 'First Item' },
      { id: 1, name: 'Second Item' }
    ];
    it('returns an empty array by default', () => {});
    it('ADD_ITEMS', () => {});
    it('CREATE_ITEM', () => {});
    it('UPDATE_ITEM', () => {});
    it('DELETE_ITEM', () => {});
  });
});

测试真的很容易写,因为我们从一个初始状态开始,当我们向我们的reducer发送一个动作时,我们知道我们应该得到什么。我们知道,如果我们发出ADD_ITEMS的动作,我们将收回我们在有效载荷(payload)中的任何内容,我们在下面的断言中看到。

it('ADD_ITEMS', () => {
  let payload = initialState,
      stateItems = items([], {type: 'ADD_ITEMS', payload: payload}); // Don't forget to include an initial state
expect(stateItems).toEqual(payload);
});

如果我们使用CREATE_ITEM的动作类型调用items reducer,那么我们预期结果将是初始数组加上新的记录。

it('CREATE_ITEM', () => {
  let payload = {id: 2, name: 'added item'},
      result = [...initialState, payload],
      stateItems = items(initialState, {type: 'CREATE_ITEM', payload: payload});
expect(stateItems).toEqual(result);
});

我们可以轻松地阐述剩下的两个reducer的方法的预期结果,然后为我们写下如下所示的断言。

it('UPDATE_ITEM', () => {
  let payload = { id: 1, name: 'Updated Item' },
      result = [ initialState[0], { id: 1, name: 'Updated Item' } ],
      stateItems = items(initialState, {type: 'UPDATE_ITEM', payload: payload});
expect(stateItems).toEqual(result);
});
it('DELETE_ITEM', () => {
  let payload = { id: 0 },
      result = [ initialState[1] ],
      stateItems = items(initialState, {type: 'DELETE_ITEM', payload: payload});
expect(stateItems).toEqual(result);
});

回顾

我们已经在本文中讲了很多,如果你已经做到了这一点! 让我们快速回顾一下我们所做的工作。

  1. redux的核心特性是统一的状态,事件向上以及自顶向下的状态流。

  2. @ngrx/store实现使用observables可以让我们使用异步管道填充我们的模板。

3.我们创建了reducers,它接收一个动作和一个状态,并且返回一个新的状态对象。

4.我们的reducer功能必须是纯粹的,所以我们看到了如何创建它们而不会使我们的集合改变。

5.一个store是一个基本的键值对map,和一些处理事件和发射状态的机制。

6.通过使用store.emit来广播事件。

7.我们使用store.select来订阅数据。

8.使用表单创建本地副本以避免更高级别的改变。

9.使用异步调用,我们通过Observable序列传递我们的结果,然后在完成时将该事件发送到reducer。

10.reducer非常易于测试,因为方法非常纯净。

通过@ngrx/store学习redux一直是我在一段时间内感受到的那种“新程序员”的感觉。非常好玩,对不对?? 举个例子,玩弄一下,思考如何在日常项目中使用这种方法。

转载于:https://my.oschina.net/zhongzhong5/blog/903197

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值