摘要: 這篇文章給了我們一個用angularjs做前後端分離開發的時候解決數據問題的方案
我們已經習慣用AngularJS來創建前後端分離的應用,同時爲了便於開發也會通過在前端代碼庫中使用mock數據來模擬服務端的接口。使用mock數據具有以下諸多的好處:
* 如果你並不是接口api的開發者,使用mock數據你就不用等待接口api設計完成就可以開始本地開發
* 如果你自己開發接口的api,使用mock數據不僅可以讓你更專注於前端開發,還不比糾結於後端具體的細節實現
* 你可以快速的開發內容而不用管後臺邏輯
* 建立mock服務還可以幫助你設計接口api的結構
* mock數據服務還可以給其他angular組件在對應服務上做單元測試用
本文我們介紹的是ngMockE2E模塊提供的$httpBackend服務。需要注意的是它跟ngMock模塊提供的同名服務是不同的哦~這兩個$httpBackend服務連api都極爲相似,但是ngMock版的$httpBackend主要用來做單元測試,因此它額外增加了很多測試用例和調用的功能在裏面。我們今天要介紹的這個ngMockE2E則是針對angular應用開發中後端數據mock功能。
$httpBackend使用ngResource($resource)的resources給我們提供了一個mock終端。它可以應用到所有的http方法並返回特殊的句柄--例如:GET,POST,DELETE等等。它有效的取代了$http在Angular中的服務棧,因此它具有穿越的特性,無服務的請求可以直接穿越到服務端。它使得我們可以在靜態服務器上創建包涵了所有服務的完美應用。
接下來我們先不急於開發一個完整的Angular應用,先從怎樣開發簡單的後段CRUD功能入手。這裏我們首先開發如下功能:
* List - Resource.query()
* Create - Resource.save() or resource.$save()
* Get - Resource.get()
* Update - Resource.update() or resource.$update()
* Delete/remove - Resource.remove or resource.$remove()
例子中用到的所有代碼都可以從這裏點我啊得到。
多餘的事情不說,我們現在就開始。從Angular的模版開始,先聲明app module並加入項目需要依賴的模塊。
var app = angular.module('app', [
'ngResource',
'ngMockE2E'
]);
然後在定義一個提供Angular組件和後端mock數據的接口服務的Contract resources。
app.factory('Contact', ['$resource', function($resource) {
return $resource(
'/contacts/:id',
{id: '@id'},
{
'update': {
method: 'PUT'
}
}
);
}]);
完成Contact resources的定義我們就可以開始定義mock backend了。$httpBackend其實是通過一個run模塊來定義的,在我們的例子中我們把它添加到app的run模塊中,然後定義一組contacts。
app.run(['$httpBackend', function($httpBackend) {
contacts = [
{
id: 1,
name: 'Ada Lovelace',
phone: '8445551815'
},
{
id: 2,
name: 'Grace Hopper',
phone: '8445551906'
},
{
id: 3,
name: 'Charles Babbage',
phone: '8445556433'
}
];
// $httpBackend interactions are defined here...
}]);
這個contacts數組是mock backend的所有操作都可以訪問的,就像一個數據倉庫,這是創建mock數據倉庫的一種方式。另一種方式是創建一個以resources的id爲下標的關聯數組。另外爲了簡單和條理清晰我們還要定義一些數組查找和操作的基礎方法,不想這麼麻煩就直接用underscore或者lodash。
我們先模擬一個最簡單的通訊錄接口,用get方式請求/contacts就返回整個通訊錄。
// Query; returns all contacts.
$httpBackend.whenGET('/contacts').respond(contacts);
再來實現Contact.save方法。ngResource的對應方法可以發送一個攜帶通訊錄數據的post請求,這樣後端就會增加一條新的紀錄。
// Save; create a new contact.
$httpBackend.whenPOST('/contacts').respond(function(method, url, data) {
var newContact = angular.fromJson(data);
contacts.push(newContact);
return [200, newContact, {}];
});
因爲angular組件會把數據當作json來處理且不會複查,所以一定要注意將序列化的通訊錄數據轉成json。創建一條新的紀錄就像在contacts數組push一條數據一樣簡單。
獲取一條通訊錄數據相對就比較麻煩了,我們必須做兩件事:從請求url中提取ID;從關聯數組中查找到這個ID。代碼如下:
// Get; return a single contact.
$httpBackend.whenGET(/\/contacts\/(\d+)/, undefined, ['id']).respond(function(method, url, data, headers, params) {
var contact = findContactById(params.id);
if (contact == null) {
return [404, undefined, {}];
}
return [200, contact, {}];
});
我們用一個正則匹配url,獲取到id的集合。angular把匹配的到的結果保存到params的id屬性上,也就是whenGET方法的第三個入參。再抓去這個值傳到findContactById方法中,將查詢的結果返回給我們。最後,我們返回查詢結果,如果爲空就返回404。
findContactById方法將ID的值轉成數字,過濾數組找到匹配值並返回。
function findContactById(id) {
// Convert id to a number.
var contactId = Number(id);
var matches = contacts.filter(function(contact) {
return contact.id === contactId;
});
var contact = matches.shift();
return contact;
}
update方法於get方法很相似,區別只在於怎樣更新通訊錄數據。代碼如下:
// Update; change details for an existing contact.
$httpBackend.whenPUT(/\/contacts\/(\d+)/, undefined, undefined, ['id']).respond(function(method, url, data, headers, params) {
var contact = findContactById(params.id),
parsedData = angular.fromJson(data);
if (contact == null) {
return [404, undefined, {}];
}
angular.extend(contact, parsedData);
return [200, contact, {}];
});
成功找到這條數據之後再使用angualr.extend方法將更新的數據拷貝到contact對象的屬性上。這樣我們不用破壞代碼裏相關的任何引用包括contacts數組。
最後是delete方法。仍然跟get和put類似,刪除操作仍然依賴於findContactById方法來查找通訊錄。一旦再contact對象中找到它的位置,就使用array#splice刪除掉contact對象中的這條紀錄:
// Delete; remove existing contact.
$httpBackend.whenDELETE(/\/contacts\/(\d+)/, undefined, ['id']).respond(function(method, url, data, headers, params) {
var contact = findContactById(params.id);
if (contact == null) {
return [404, undefined, {}];
}
// Replace contacts array with filtered results, removing deleted contact.
contacts.splice(contacts.indexOf(contact), 1);
return [200, undefined, {}];
});
完成這些後臺服務和數據的定義,我們就可以把整個應用跑起來了。整個頁面應用的和核心控制器是一個所有服務功能之行時調用的簡單事務。隨着通訊錄的get操作,結果就會呈現在我們面前。然後使用更新功能給Grace Hopper所在列添加她的聯繫人姓名。創建操作是添加Gloria Gordan Bolotsky到通訊錄。刪除操作是將Charls Babbage從通訊錄刪除。增加,刪除,更新的操作是放在一個promise隊列中,結果會返回到成功的回調函數中。這就是整個控制器做的事情。
app.controller('MainCtrl', function($scope, Contact) {
//List
$scope.contacts = Contact.query();
// Get
$scope.ada = Contact.get({id: 1});
// Update
Contact.get({id: 2}).$promise
.then(function(contact2) {
contact2.name = 'Rear Admiral Grace Hopper';
return contact2.$update().$promise;
})
.then(updateContactsList);
// Create
var newContact = new Contact({
name: 'Gloria Gordon Bolotsky',
phone: '8445556433'
});
newContact.$save()
.then(updateContactsList);
// Delete
Contact.remove({id: 3}).$promise
.then(updateContactsList);
function updateContactsList() {
// Refresh contacts list
$scope.contacts = Contact.query();
}
});
最後一步創建這個簡單應用的視圖部分:
<!DOCTYPE html>
<html ng-app="app">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular-resource.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular-mocks.js"></script>
<script>document.write('<base href="' + document.location + '" />');</script>
<link rel="stylesheet" href="style.css" />
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<p>All contacts:</p>
<ul>
<li ng-repeat="contact in contacts">{{ contact.name }}</li>
</ul>
<p>Contact 1: {{ ada.name }}</p>
</body>
</html>
代碼給我們簡單演示了從通訊錄服務檢索一條聯繫人記錄。頁面中需要引入:angular,ngResource和ngMockE2E幾個文件。
終於完成了!雖然簡單但是可以給我們使用mock模擬CRUD操作到現代web應用打下基礎。
原文地址:http://madebymunsters.com/blog/posts/creating-a-mock-backend-in-angular/