AngularJS is really a great open-source framework for creating single page web applications (SPA) based on MVW (Model / View / Whatever) architecture. In this article, I’ll present to you how the Repository design pattern can be used in order to encapsulate into a separate layer all the underlying communication with a back-end remote system for performing CRUD operations. The example next is very simple since the user can load either a list of customers or a list of orders by clicking the appropriate link from a menu.
The structure of the demonstration project is the following :
`-- CMApplication |-- Application | |-- Application.js | |-- Controllers.js | `-- Repositories.js |-- Database | |-- Customers.json | `-- Orders.json |-- index.html |-- Partials | |-- CustomersPartialView.html | `-- OrdersPartialView.html `-- Scripts |-- angular.js `-- angular-route.js
Let’s start with the “index.html” file which acts as a main template view :
<!DOCTYPE html> <html data-ng-app="CMApplication"> <head> <meta charset="utf-8" /> <title>CMApplication</title> <script src="Scripts/angular.js"></script> <script src="Scripts/angular-route.js"></script> <script src="Application/Application.js"></script> <script src="Application/Controllers.js"></script> <script src="Application/Repositories.js"></script> </head> <body> <div> <ul> <li><a href="#/customers">Customers</a></li> <li><a href="#/orders">Orders</a></li> </ul> </div> <div data-ng-view=""></div> </body> </html>
You will notice that generally the AngularJS framework is used.
From the above main template view I will present to you the following files :
- Application.js
- Controllers.js
- Repositories.js
The “Application.js” file contains the following initialization code :
angular.module ('CMApplication.Controllers', []); angular.module ('CMApplication.Repositories', []); angular.module ('CMApplication', ['ngRoute', 'CMApplication.Controllers', 'CMApplication.Repositories']) .config (['$routeProvider', function ($routeProvider) { $routeProvider .when ('/customers', { controller: 'CustomerController', templateUrl: 'Partials/CustomersPartialView.html' }) .when ('/orders', { controller: 'OrderController', templateUrl: 'Partials/OrdersPartialView.html' }) .otherwise ({ redirectTo: '/customers' }); }]);
From the above code you will notice that separate modules are used for both controllers and repositories and that we inject these modules in the main application module with the AngularJS dependency injection mechanism. Moreover, we configure the routes for using the customers and orders views. Both views act as partial views and are rendered inside the main template view.
The partial view “CustomersPartialView.html” for Customers :
<h3>Customers</h3> <div data-ng-repeat="customer in customers | orderBy:'name'"> <div>ID: {{customer.id}} - Name: {{customer.name}}</div> </div>
The partial view “OrdersPartialView.html” for Orders :
<h3>Orders</h3> <div data-ng-repeat="order in orders | orderBy:'name'"> <div>ID: {{order.id}} - Name: {{order.name}}</div> </div>
Next, follows the contents of the “Controllers.js” file :
angular.module ('CMApplication.Controllers') .controller ('CustomerController', ['$scope', 'CustomerRepository', function ($scope, CustomerRepository) { CustomerRepository.getCustomers (function (data) { $scope.customers = data; }); }]); angular.module ('CMApplication.Controllers') .controller ('OrderController', ['$scope', 'OrderRepository', function ($scope, OrderRepository) { OrderRepository.getOrders (function (data) { $scope.orders = data; }); }]);
Inside the controllers (OrderController and CustomerController) there is no direct use of $http service. Controllers have no idea of the data source or the underlying system for retrieving or sending data. We could use JSON files, XML files, a local database or a backend system with a RESful API communication. In this example we just use JSON files since the source of data works as an abstraction for $http. The appropriate repository is injected in each controller. Whenever a controller calls a repository method passes the success callback (you can modify it for passing also the error callback) for handling the request due to fact that the operation is asynchronous.
Next, follows the contents of the “Repositories.js” file :
angular.module ('CMApplication.Repositories') .factory ('CustomerRepository', ['$http', function ($http) { return { getCustomers: function (callback) { $http.get ('Database/Customers.json').success (function (data) { callback (data); }); } }; }]); angular.module ('CMApplication.Repositories') .factory ('OrderRepository', ['$http', function ($http) { return { getOrders: function (callback) { $http.get ('Database/Orders.json').success (function (data) { callback (data); }); } }; }]);
Lastly, the contents of the files “Customers.json” and “Orders.json” :
[ { "id": 1, "name": "Customer Name 1" }, { "id": 2, "name": "Customer Name 2" }, { "id": 3, "name": "Customer Name 3" } ] [ { "id": 1, "name": "Order Name 1" }, { "id": 2, "name": "Order Name 2" }, { "id": 3, "name": "Order Name 3" } ]
In a controller you can inject more than one repositories depending on what data you want to use. Also, you can introduce a middle service layer that can encapsulate any repositories you want and expose a single method with wrapper model parameters for the controller.
My pleasure.
Thank you for this short but compound tutorial for the basics of AngularJS. You helped me understand it better.