所在位置:首頁 -- 數據庫 -- 正文

AngularJS 數據建模


發布時間:2017-4-1  來源:admin

 我們知道,AngularJS并沒有自帶立等可用的數據建模方案。而是以相當抽象的方式,讓我們在controller中使用JSON數據作為模型。但是隨著時間的推移和項目的成長,我意識到這種建模的方式不再能滿足我們項目的需求。在這篇文章中我會介紹在我的AngularJS應用中處理數據建模的方式。

為Controller定義模型

讓我們從一個簡單的例子開始。我想要顯示一個書本(book)的頁面。下面是控制器(Controller):

BookController

app.controller('BookController', ['$scope', function($scope) {
$scope.book = {
id: 1,
name: 'Harry Potter',
author: 'J. K. Rowling',
stores: [
{ id: 1, name: 'Barnes & Noble', quantity: 3},
{ id: 2, name: 'Waterstones', quantity: 2},
{ id: 3, name: 'Book Depository', quantity: 5}
]
};
}]);

這個控制器創建了一個書本的模型,我們可以在后面的模板中(templage)中使用它。

template for displaying a book

<div ng-controller="BookController">
Id: <span ng-bind="book.id"></span>
<br/>
Name:<input type="text" ng-model="book.name" />
<br/>
Author: <input type="text" ng-model="book.author" />
</div>

假如我們需要從后臺的api獲取書本的數據,我們需要使用$http:

BookController with $http

app.controller('BookController', ['$scope', '$http', function($scope, $http) {
var bookId = 1;

$http.get('ourserver/books/' + bookId).success(function(bookData) {
$scope.book = bookData;
});
}]);

注意到這里的bookData仍然是一個JSON對象。接下來我們想要使用這些數據做一些事情。比如,更新書本信息,刪除書本,甚至其他的一些不涉及到后臺的操作,比如根據請求的圖片大小生成一個書本圖片的url,或者判斷書本是否有效。這些方法都可以被定義在控制器中。

BookController with several book actions

app.controller('BookController', ['$scope', '$http', function($scope, $http) {
var bookId = 1;

$http.get('ourserver/books/' + bookId).success(function(bookData) {
$scope.book = bookData;
});

$scope.deleteBook = function() {
$http.delete('ourserver/books/' + bookId);
};

$scope.updateBook = function() {
$http.put('ourserver/books/' + bookId, $scope.book);
};

$scope.getBookImageUrl = function(width, height) {
return 'our/image/service/' + bookId + '/width/height';
};

$scope.isAvailable = function() {
if (!$scope.book.stores || $scope.book.stores.length === 0) {
return false;
}
return $scope.book.stores.some(function(store) {
return store.quantity > 0;
});
};
}]);

然后在我們的模板中:

template for displaying a complete book

<div ng-controller="BookController">
<div ng-style="{ backgroundImage: 'url(' + getBookImageUrl(100, 100) + ')' }"></div>
Id: <span ng-bind="book.id"></span>
<br/>
Name:<input type="text" ng-model="book.name" />
<br/>
Author: <input type="text" ng-model="book.author" />
<br/>
Is Available: <span ng-bind="isAvailable() ? 'Yes' : 'No' "></span>
<br/>
<button ng-click="deleteBook()">Delete</button>
<br/>
<button ng-click="updateBook()">Update</button>
</div>

在controllers之間共享Model

如果書本的結構和方法只和一個控制器有關,那我們現在的工作已經可以應付。但是隨著應用的增長,會有其他的控制器也需要和書本打交道。那些控制器很多時候也需要獲取書本,更新它,刪除它,或者獲得它的圖片url以及看它是否有效。因此,我們需要在控制器之間共享這些書本的行為。我們需要使用一個返回書本行為的factory來實現這個目的。在動手寫一個factory之前,我想在這里先提一下,我們創建一個factory來返回帶有這些book輔助方法的對象,但我更傾向于使用prototype來構造一個Book類,我覺得這是更正確的選擇:

Book model service

app.factory(&#039;Book&#039;, [&#039;$http&#039;, function($http) {
function Book(bookData) {
if (bookData) {
this.setData(bookData):
}
// Some other initializations related to book
};
Book.prototype = {
setData: function(bookData) {
angular.extend(this, bookData);
},
load: function(id) {
var scope = this;
$http.get(&#039;ourserver/books/&#039; + bookId).success(function(bookData) {
scope.setData(bookData);
});
},
delete: function() {
$http.delete(&#039;ourserver/books/&#039; + bookId);
},
update: function() {
$http.put(&#039;ourserver/books/&#039; + bookId, this);
},
getImageUrl: function(width, height) {
return &#039;our/image/service/&#039; + this.book.id + &#039;/width/height&#039;;
},
isAvailable: function() {
if (!this.book.stores || this.book.stores.length === 0) {
return false;
}
return this.book.stores.some(function(store) {
return store.quantity &gt; 0;
});
}
};
return Book;
}]);

 

這種方式下,書本相關的所有行為都被封裝在Book服務內。現在,我們在BookController中來使用這個亮眼的Book服務。

BookController that uses Book model

app.controller(&#039;BookController&#039;, [&#039;$scope&#039;, &#039;Book&#039;, function($scope, Book) {
$scope.book = new Book();
$scope.book.load(1);
}]);

正如你看到的,控制器變得非常簡單。它創建一個Book實例,指派給scope,并從后臺加載。當書本被加載成功時,它的屬性會被改變,模板也隨著被更新。記住其他的控制器想要使用書本功能,只要簡單地注入Book服務即可。此外,我們還要改變template使用book的方法。

template that uses book instance

<div ng-controller="BookController">
<div ng-style="{ backgroundImage: 'url(' + book.getImageUrl(100, 100) + ')' }"></div>
Id: <span ng-bind="book.id"></span>
<br/>
Name:<input type="text" ng-model="book.name" />
<br/>
Author: <input type="text" ng-model="book.author" />
<br/>
Is Available: <span ng-bind="book.isAvailable() ? 'Yes' : 'No' "></span>
<br/>
<button ng-click="book.delete()">Delete</button>
<br/>
<button ng-click="book.update()">Update</button>
</div>

到這里,我們知道了如何建模一個數據,把他的方法封裝到一個類中,并且在多個控制器中共享它,而不需要寫重復代碼。

在多個控制器中使用相同的書本模型

我們定義了一個書本模型,并且在多個控制器中使用了它。在使用了這種建模架構之后你會注意到有一個嚴重的問題。到目前為止,我們假設多個控制器對書本進行操作,但如果有兩個控制器同時處理同一本書會是什么情況呢?

假設我們頁面的一塊區域我們所有書本的名稱,另一塊區域可以更新某一本書。對應這兩塊區域,我們有兩個不同的控制器。第一個加載書本列表,第二個加載特定的一本書。我們的用戶在第二塊區域中修改了書本的名稱并且點擊“更新”按鈕。更新操作成功后,書本的名稱會被改變。但是在書本列表中,這個用戶始終看到的是修改之前的名稱!真實的情況是我們對同一本書創建了兩個不同的書本實例——一個在書本列表中使用,而另一個在修改書本時使用。當用戶修改書本名稱的時候,它實際上只修改了后一個實例中的屬性。然而書本列表中的書本實例并未得到改變。

解決這個問題的辦法是在所有的控制器中使用相同的書本實例。在這種方式下,書本列表和書本修改的頁面和控制器都持有相同的書本實例,一旦這個實例發生變化,就會被立刻反映到所有的視圖中。那么按這種方式行動起來,我們需要創建一個booksManager服務(我們沒有大寫開頭的b字母,是因為這是一個對象而不是一個類)來管理所有的書本實例池,并且富足返回這些書本實例。如果被請求的書本實例不在實例池中,這個服務會創建它。如果已經在池中,那么就直接返回它。請牢記,所有的加載書本的方法最終都會被定義在booksManager服務中,因為它是唯一的提供書本實例的組件。

booksManager service

app.factory(&#039;booksManager&#039;, [&#039;$http&#039;, &#039;$q&#039;, &#039;
Book&#039;, function($http, $q, Book) {
var booksManager = {
_pool: {},
_retrieveInstance: function(bookId, bookData) {
var instance = this._pool[bookId];

if (instance) {
instance.setData(bookData);
} else {
instance = new Book(bookData);
this._pool[bookId] = instance;
}

return instance;
},
_search: function(bookId) {
return this._pool[bookId];
},
_load: function(bookId, deferred) {
var scope = this;

$http.get(&#039;ourserver/books/&#039; + bookId)
.success(function(bookData) {
var book = scope._retrieveInstance(bookData.id, bookData);
deferred.resolve(book);
})
.error(function() {
deferred.reject();
});
},
/* Public Methods */
/* Use this function in order to get a book instance by it&#039;s id */
getBook: function(bookId) {
var deferred = $q.defer();
var book = this._search(bookId);
if (book) {
deferred.resolve(book);
} else {
this._load(bookId, deferred);
}
return deferred.promise;
},
/* Use this function in order to get instances of all the books */
loadAllBooks: function() {
var deferred = $q.defer();
var scope = this;
$http.get(&#039;ourserver/books)
.success(function(booksArray) {
var books = [];
booksArray.forEach(function(bookData) {
var book = scope._retrieveInstance(bookData.id, bookData);
books.push(book);
});

deferred.resolve(books);
})
.error(function() {
deferred.reject();
});
return deferred.promise;
},
/* This function is useful when we got somehow the book data
and we wish to store it or update the pool and get a book instance in return */
setBook: function(bookData) {
var scope = this;
var book = this._search(bookData.id);
if (book) {
book.setData(bookData);
} else {
book = scope._retrieveInstance(bookData);
}
return book;
},

};
return booksManager;
}]);

下面是我們的EditableBookController和BooksListController兩個控制器的代碼:

EditableBookController and BooksListController that uses booksManager

app.factory(&#039;Book&#039;, [&#039;$http&#039;, function($http) {
function Book(bookData) {
if (bookData) {
this.setData(bookData):
}
// Some other initializations related to book
};
Book.prototype = {
setData: function(bookData) {
angular.extend(this, bookData);
},
delete: function() {
$http.delete(&#039;ourserver/books/&#039; + bookId);
},
update: function() {
$http.put(&#039;ourserver/books/&#039; + bookId, this);
},
getImageUrl: function(width, height) {
return &#039;our/image/service/&#039; + this.book.id + &#039;/width/height&#039;;
},
isAvailable: function() {
if (!this.book.stores || this.book.stores.length === 0) {
return false;
}
return this.book.stores.some(function(store) {
return store.quantity &gt; 0;
});
}
};
return Book;
}]);

需要注意的是,模塊(template)中還是保持原來使用book實例的方式。現在應用中只持有一個id為1的book實例,它發生的所有改變都會被反映到使用它的各個頁面上。

總結

在這片文章中,我建議了AngularJS中建模數據的一種架構。首先,我展示了AngularJS默認的數據模型綁定,然后講了如何封裝模型的方法和操作從而可以在不同的控制其中重用它們,最后我解釋了如何管理模型實例從而使得所有的改變都能被反映到應用中各個相關的視圖上。

希望這篇文章能在如何實現數據建模上給你一些啟示。

中国比特币暴涨 北京pk赛车 欢乐生肖开奖官网走势图 湖南幸运赛车走势图 时时缩水app 四肖中特期期准 安徽快3开奖网站 中国福老时时网 快乐手机助手 四川快乐12交流群 新疆时时开奖号500 重庆快乐十分开奖时间 老时时后二技巧 湖南快乐十分基本走势图50期 四川快乐12最大遗漏 重庆福彩欢乐生肖28期开奖 广东快乐十分走势图开奖