Very often, I was asked by my clients to develop a backend less AngularJS app. It happens frequently because the backend usually takes a lot of time to work before it can go production. The frontend, on the other hand, although it can be very complicated and there are a lot of techniques to implement but as soon as we have the first PSD files, we can start working page by page until finish. That’s why it’s common to develop a backend less AngularJS app and integrate real backend API later. Besides that, Single Page Application (SPA) makes it very easy to separate frontend and backend.
This demand is real but I’ve struggled to find a good example on backend less AngularJS development on internet. Most of the articles I found only talk about using $httpBackend to mock your $http service. With me, this is ok but it is not enough. If our app need to update and delete data, if we only use $httpBackend our app can’t reflect changes when we create, update or delete data. So we need another approach. Let’s me show you my completed approach for backend less AngularJS development in this article.
Contents
Introduce backend less AngularJS Todo app
The todo app is very famous for any SPA developer, but this time we want to store todo data on backend server and we need to make our app working with new APIs. Our backend team are working the APIs but they can’t finish anytime soon yet so our frontend team need to create a backend less todo app. When backend team finish the APIs, this todo app should work properly without much changes.
In order to make our todo app simple, this app only has these features:
- Display todo
- Delete todo
- Mark todo as completed
Other features like edit and show completed, deleted todo are not included in this version.
You can look at the demonstration our todo app at here: http://davidtran.github.io/angular-backendless/
Dependencies:
In this todo app, we gonna use these packages/dependencies:
- AngularJS@1.4.8
- Faker@3.0.1: this package is used to generate some fake data
- angular-mocks@1.4.8 this package contains $httpBackend which is used to mock fake data to $http service
- ngstorage@0.3.10 this package contains $localStorage which is used to store data on localStorage of the browser
Let’s use bower to install these packages. Be careful to use correct case of the package names, some packages has same name but different case, eg: faker and Faker are different packages.
Project structure:
We have 3 modules for this app:
- app: this module is use to bootstrap our app
- app.todo: this is the heart of our app. It has responsibility to display, update and delete our todo.
- app.mockdata: mock http request, response request and generate some fake data.
Let’s go through each module in our app.
app:
This module just loads other modules in our app. There is nothing special so let’s take a look at the source code and move forward
app.js:
(function() { 'use strict'; angular .module('app', [ 'app.todo', 'app.mockdata' ]); })();
index.html:
<!doctype html> <html lang="en" ng-app="app"> <head> <meta charset="utf-8"> <title>Backend Less Angular Todo app</title> <meta name="description" content="Backend Less Angular Todo app"> <meta name="author" content="Nam Tran"> <link rel="stylesheet" href="css/style.css"> </head> <body ng-controller="todoCtrl as vm"> <section id="todoapp"> <header id="header"> <h1>todos</h1> <form ng-submit="vm.add()"> <input id="new-todo" placeholder="What needs to be done?" autofocus="" ng-model="vm.todoContent"> </form> </header> <section id="main"> <input id="toggle-all" type="checkbox"> <label for="toggle-all">Mark all as complete</label> <ul id="todo-list"> <li ng-repeat="todo in vm.todoList track by todo.id"> <div class="view"> <input class="toggle" type="checkbox" ng-checked="todo.completed" ng-click="vm.completed(todo)"> <label>{{todo.content}}</label> <button class="destroy" ng-click="vm.remove(todo)"></button> </div> </li> </ul> </section> </section> <script src="public/components/angular/angular.js"></script> <script src="public/components/angular-mocks/angular-mocks.js"></script> <script src="public/components/faker/build/build/faker.js"></script> <script src="public/components/ngstorage/ngStorage.js"></script> <script src="src/app.js"></script> <script src="src/todo/todo.js"></script> <script src="src/todo/todoCtrl.controller.js"></script> <script src="src/todo/todoApi.service.js"></script> <script src="src/mockdata/mockdata.js"></script> <script src="src/mockdata/mockTodoApi.service.js"></script> </body> </html>
app.todo
This is the heart of our application. We have 1 service and 1 controller for this module. We just develop it as it gonna to consume the real APIs. Let’s see each file in this module.
todoApi.service.js:
(function() { 'use strict'; angular .module('app.todo') .service('todoApi', todoApi); function todoApi($q, $http) { this.add = add; this.list = list; this.remove = remove; this.completed = completed; function add(todo) { return $http.post('/todo', todo); } function list() { return $q(function(resolve, reject) { return $http .get('/todo') .then(function(response) { return resolve(response.data); }); }); } function remove(todo) { return $http.delete('/todo/' + todo.id); } function completed(todo) { return $http.patch('/todo/' + todo.id + '/completed'); } } })();
todoCtrl.controller.js:
(function() { 'use strict'; angular .module('app.todo') .controller('todoCtrl', todoCtrl); function todoCtrl(todoApi) { var self = this; self.todoList = []; self.todoContent = null; self.add = add; self.remove = remove; self.completed = completed; refresh(); function refresh() { todoApi .list() .then(function(todoList) { self.todoList = todoList; }); } function add() { if (!self.todoContent) return; todoApi .add(self.todoContent) .then(function() { self.todoContent = null; refresh(); }); } function remove(todo) { return todoApi .remove(todo) .then(function() { refresh(); }); } function completed(todo) { return todoApi .completed(todo) .then(function() { refresh(); }); } } })();
todoApi will consume the APIs just like a real application. By this way, when we run our app in production, it still can make actual request the real APIs.
Let’s go to the app.mockdata to see how the whole backend less app works. This is the key module of this article.
app.mockdata
In this module, we load 2 third-party modules, they are:
- ngMockE2E: this module provides $httpBackend which is required to mock http request.
- ngStorage: this module provides $localStorage which we use to store todo data.
In this module, we use a mockTodoApi service to store, update, remove todo data. It looks like a mini backend but it doesn’t have to be complicated. A real backend may involve many logics behind but in this service we just use some simple function to modify data.
mockdata.js:
(function() { 'use strict'; angular .module('app.mockdata', [ 'ngMockE2E', 'ngStorage' ]) .run(run); function run($httpBackend, mockTodoApi) { $httpBackend.whenGET('/todo').respond(mockTodoApi.list()); $httpBackend.whenPOST('/todo').respond(function(method, url, data) { return [200, mockTodoApi.add(data), {}]; }); $httpBackend.whenDELETE(/\/todo\/\d+/).respond(function(method, url, data) { var id = extractNumberFromUrl(url); return [200, mockTodoApi.remove(id), {}]; }); $httpBackend.whenPATCH(/\/todo\/\d+\/completed/).respond(function(method, url, data) { var id = extractNumberFromUrl(url); console.log(url, id); return [200, mockTodoApi.completed(id), {}]; }); function extractNumberFromUrl(url) { var regex = /(\d+)/; var match = regex.exec(url); if (match !== null) return parseInt(match[1]); return null; } } })();
We mock the http request in this file – the module creation file. We use $httpBackend to mock each http request. For DELETE and PATCH methods, we use a simple regex to extract todo id from url, after that we use mockTodoApi service to return data.
mockTodoApi.service.js:
(function() { 'use strict'; angular .module('app.mockdata') .service('mockTodoApi', mockTodoApi); function mockTodoApi($localStorage) { this.list = list; this.add = add; this.remove = remove; this.completed = completed; $localStorage['list'] = $localStorage['list'] || fakeTodoList(); function add(todoContent) { var todo = makeNewTodo(todoContent); $localStorage['list'].push(todo); return true; } function list() { return $localStorage['list']; } function remove(todoId) { var todos = $localStorage['list']; for (var i = 0; i &amp;lt; todos.length; i++) { if (todos[i].id === todoId) { todos.splice(i, 1); return true; } } return false; } function completed(todoId) { var todos = $localStorage['list']; for (var i = 0; i &amp;lt; todos.length; i++) { if (todos[i].id === todoId) { todos[i].completed = true; return true; } } return false; } function makeNewTodo(todoContent) { return { id: $localStorage['list'].length, content: todoContent, completed: false, deleted: false, createdAt: new Date(), updatedAt: new Date() }; } function fakeTodoList() { var result = []; for (var i = 0; i &amp;lt; 3; i++) { result.push({ id: i, content: faker.lorem.sentence(), completed: faker.random.boolean(), deleted: faker.random.boolean(), createdAt: faker.date.past(), updatedAt: faker.date.recent() }); } return result; } } })();
This service has almost same methods with todoApi service in app.todo. We make the method names similar to easy recognize the mock methods. It does handle some logic but should be very simple.
We use Faker to generate some initial data.
At this moment, our app is almost done. We can run our backend less app. When the backend team have finished the real APIs, you just need to remove app.mockdata module from dependencies of app.js and our app still works as expected. See the demonstration at here: davidtran.github.io/angular-backendless
In summary
When develop a backend less AngularJS app. You just need to develop the main module as it gonna consume real APIs, after that you develop the mock module which use $httpBackend, $localStorage to response the request from the main module. When your backend team finish the APIs, you remove mock module from the app.
Notice, this approach might require you to put some little effort to our application, but it could make your client happy. Happy coding !