使用Redux和ngrx構建更好的Angular2應用(四)
呼叫所有服務
首先,將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);
});
回顧
我們已經在本文中講了很多,如果你已經做到了這一點! 讓我們快速回顧一下我們所做的工作。
- redux的核心特性是統一的狀態,事件向上以及自頂向下的狀態流。
- @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一直是我在一段時間內感受到的那種“新程式設計師”的感覺。非常好玩,對不對??
舉個例子,玩弄一下,思考如何在日常專案中使用這種方法。