📜 ⬆️ ⬇️

AngularJS. Data organization

As the application grows, the presentation of data as a set of JSON objects becomes less and less convenient. In this article I will tell you about the way to organize the work with data in my applications.

Let's start with a simple example. Create a page with information about the book. Controller:

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

The controller initializes the book model, which is used in the view:

 <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> 

When you need to get information about the book from the back-end server, you can use the $ http service:
')
 app.controller('BookController', ['$scope', '$http', function($scope, $http) { var bookId = 1; $http.get('ourserver/books/' + bookId).success(function(bookData) { $scope.book = bookData; }); }]); 

Note that bookData is still a JSON object.

We will most likely need to manipulate the data. For example,
delete books, update information about them or generate a cover url, in accordance with the size we need. Methods that allow this to be done can be defined in the controller code.

 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; }); }; }]); 

We use these methods in the presentation:

 <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> 

Using the model with multiple controllers

If our methods and book data are used by only one controller, the job is done.
However, as the application grows, it will be necessary to use one model in several controllers. In order to make it available for several controllers, we will create the Book service, which will be a prototype of objects describing the state and behavior of books.

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

Use sevris Book in the controller.

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

Now that all the logic has been moved to the model, there are only two lines left in the controller code: the creation of the book object and the receipt of data from the back-end server. Once the data has been loaded, it will appear in the view, which now looks like this:

 <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> 

So, we have a Book service and several controllers that work with books. The architecture presented has a flaw. What happens if two controllers have the ability to manipulate the same book?
Imagine that there are two pages: one with a list of books, and the other with a form of book management. For each page created by the controller. The first controller receives from the back-end server a list of books, and the second information about one of them. The user enters the second page, changes the name of the book and presses the "save" button. The update is successful, and the title of the book changes. However, if he goes to the first page, he will see the old title in the list of books. This happened because there were two copies of the same book: one for the page with a list of books, and the other for the page for managing the book. The user changed the name only in the instance that was created for the book management page, the second copy remained unchanged.
To solve this problem, all controllers need to use the same copy of the book object. In this case, if you change the name of the book on the second page, it will change both on the first and all other pages that use information about the book.
To implement the solution, we will create a bookManager service (the name of the service is not written with a capital letter, because it will be an object without heirs), which will manage the books and be responsible for receiving data. If the requested book is not loaded, bookManager will load it, otherwise it will return an already downloaded instance. Keep in mind that all methods for retrieving books from a back-end server will be defined only in the bookManager service, since it must be the only component providing this data.

 app.factory('booksManager', ['$http', '$q', 'Book', 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('ourserver/books/' + bookId) .success(function(bookData) { var book = scope._retrieveInstance(bookData.id, bookData); deferred.resolve(book); }) .error(function() { deferred.reject(); }); }, /* */ /*    */ 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; }, /*    */ loadAllBooks: function() { var deferred = $q.defer(); var scope = this; $http.get('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; }, /*  */ 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; }]); 

View code will not change.

Now only one object will be stored for each book, and all changes to this object will be displayed on all pages that use it.

Original: www.webdeveasy.com/angularjs-data-model

Source: https://habr.com/ru/post/225659/


All Articles