Routing with Angular UI-Router

URL is the essence element of the web. Because of URL, website is sharable and that’s why web is very successful and popular. Any Single Page Application (SPA) framework must take URL very seriously.

With AngularJS, we offen hear about ngRoute in many ebooks and tutorial, but the true is ngRoute has very limit features and only suitable for very simple website. You can’t use it if your website has many nested view. It’s the reason why ngRoute has been removed from the core of AngularJS and Angular-UI team has developed a better solution, it’s ui-router

UI-router uses “state” concept to describe state of website, your website can have many nested state and each different state, ui-router will load different views. With different nested state, ui-router will have different nested view as well. Up-to-date, ui-router is the best solution for routing in AngularJS.

I’ve used UI-router in many projects and I am very please with it.

Here is the example we will use in this tutorial:

Angular UI router

You can grab source code at here:

davidtran/ui-router-tutorial

So our website consist very simple views.

From the index.html we can go to home.html and download.html
In home.html we can go to api.html and features.html
In features, we provide links to features details.

Notice that we use ui-view directive in index.html and home.html

Create state:

In order to create a state, we need to use $stateProvider.state() method.

$stateProvider.state('index', {
    url:'/',
    templateUrl: 'app/index/index.html'
});

First parameter is the state name, second parameter is a object which contain state url and templateUrl. If you want to use inline string template, you can replace templateUrl with template.

So from here, if use go to url: abc/  it matches with state “home” and our ui-view in home.html will load home.html
state “home” can be activated by click on the link:

<a ui-sref=“home’>Home</a>

Create nested state:
In our example, we have some nested view, like api, features, and feature detail. We will create our nested state like this:

$stateProvider.state('index.about', {
    url:'about',
    templateUrl: 'app/index/about.html'
});

In this case, if user go to abc/about, our website will load index.about state. The template will be loaded by ui-view directive in home.html

Create states with parameters:

We have features detail which has a featureId parameter. In order to do it, we need to modify our url when create state:

In our link, we append an object which store our parameter. And our link will look like this.

<a ui-sref=’home.features_detail({featureId: 2})’>Feature 2</a>

After ui-sref parses it’s value, it will create href attribute in the link.

<a ui-sref=”home.features_detail({featureId: 1})” href=”#/features/1″>Feature 1</a>

We can access our state parameters from $stateParams service.

Now, let’s look at the source codes which create our route and state:

var home = angular.module('home', [
    'ui.router'
]);
home.config(function($stateProvider, $urlRouterProvider){
    $urlRouterProvider.otherwise('/');
    $stateProvider.state('home', {
        url:'/',
        templateUrl: 'app/home/home.html'
    });
    $stateProvider.state('home.api', {
        url:'api',
        templateUrl: 'app/home/api.html'
    });
    $stateProvider.state('home.features', {
        url: 'features/',
        templateUrl: 'app/home/features.html'
    });
    $stateProvider.state('home.features_detail', {
        url: 'features/{featureId}',
        templateUrl: 'app/home/features_detail.html',
        controller: function($scope, $stateParams) {
            $scope.featureId = $stateParams.featureId
        }
    });
});

download.js

var download = angular.module('download', [
    'ui.router'
])
download.config(function($stateProvider, $urlRouterProvider) {
    $stateProvider.state('download', {
        url:'download',
        templateUrl: 'app/download/download.html'
    });
});

Summary

At the time of writing, ui router is still the most powerful routing solution for AngularJS. With ui router, we can easily create state, nested state and inject parameter in a state. For more information about ui router, you can read more on their website: UI router

JSON web token authentication with AngularJS and ExpressJS Edit

Authentication is a important part of any web applications. For decades, we follow cookie-based authentication and it works well for most of tradition applications. But today application is changing. We no longer combine presentation layer and logic layer in a single application. We tend to separate it into backend and frontend applications. Backend is used to handle logic and provide APIs for the frontend. Frontend on other hand, can be be complicated. It’s a collection of many different applications, include Single Page Application (app), iOS app, Android app or any other third party applications. All them connect to backend via backend’s APIs. These changes led to a demand of new authentication schema.

JSON Web Token (JWT) is used to send digital information that can be verifed and trusted by mean of digital signature. It comprise a compact and URL-safed JSON object, which is crytographycally signed to verify its authenticity, and which can also be encrypted if payload contains sensitive information.

This token is portable, the same token can be used many different devices. The token also contains some information of the user. JWT leaves it up to the frontend to store and handle the entire session/user object. By using JWT, our backend is more lightweight and focus on APIs only.

In this tutorial, I am going to demonstrate how to implement a basic authentication using JWT in two popular web technologies: ExpressJS for the backend and AngularJS for the Single Page Application. ( You can find the entire code at here)

ExpressJS backend

We will use ExpressJS to handle signin request, generate the token and validate token in /check request. The sigin request is very simple, we validate the data in request against hard-code username and password which are defined in the backend.

Setup project

In this project, we are going to use ExpressJS as our backend web framework. To handle authentication we use PassportJS. Jsonwebtoken library is used to generate and verify token send from client. Because our SPA is separated with the backend. We also use “cors” to enable cross-origin request. Lastly, “body-parser” is used to parse body content of request from SPA.

We can install all of them by NPM:

npm init -y
npm install body-parser@^1.14.1 —save
npm install cors@^2.7.1 —save
npm install express@^4.13.3 —save
npm install jsonwebtoken@^5.4.1 —save
npm install passport@^0.3.2 —save
npm install passport-local^1.0.0 —save

Now we can create our backend starting point.

Content of index.js

var express = require('express');
var passport = require('passport');
var bodyParser = require('body-parser');
var cors = require('cors');
var router = express.Router();
var app = express();
app.use(bodyParser.urlencoded({extended: false}));
app.use(bodyParser.json());
app.use(passport.initialize());
app.use(router);
app.use(cors());
require('./passport.js')(passport);
require('./routes/home.js')(app, passport);
var port = process.env.PORT || 3000;
app.listen(port);

This file is pretty forward, we setup our backend application, body-parse and cors. Router is setup at /routes/home.js and we configure Passport authentication stragegy at passport.js.

Content of passport.js:

var LocalStrategy = require('passport-local');
module.exports = function(passport) {
  var user = {
    id: 1,
    username: 'namtran',
    email: 'nam.trankhanh.vn@gmail.com'
  };
  passport.use('signin', new LocalStrategy({passReqToCallback: true},          function(req, username, password, done) {    
    if (username === 'namtran' && password === '123') {
      return done(null, user);
    }
    return done(new Error('Invalid username or password'));
  }));
}

We use a LocalStrategy to process authentication. The username and password in request payload need to match with our hard code username and password.

We want to guard some of our routes from unrestricted access, in order to do it we create authentication middleware. This middle will verify the JSON web token in request’s header if it unable to verify token or token is not exist, the middleware response 400 status code.

Content of authentication.js:

var jwt = require('jsonwebtoken');
function authentication(req, res, next) {
  if (!req.headers['authorization']) {
    res.status(400).send();
  }
  var token = req.headers['authorization'];
  return jwt.verify(token, 'MYSECRET', function(err, result) {
    if (err) {
      return res.status(400).send();
    }
    return next();
  });
}
module.exports = authentication;

 

The router of our app is setup at /routes/home.js. When our app receives a POST request to /sigin with username and password. We verify it and return a token via JSON request.  Once user has been signed in, he can fetch a restricted resource. So we make /restricted endpoint that simulates a restricted resource that needs an authenticated user. In order to do this, the request header need to provide token for the the backend to verify.

We also add handler for OPTIONS request to deal with pre-flight behavior.

Content of home.js:

var express = require('express');
var router = express.Router();
var authentication = require('../middlewares/authentication.js');
var jwt = require('jsonwebtoken');
var cors = require('cors');
module.exports = function(app, passport) {
	app.use('/', router);
  router.options('/sigin', cors());
  router.post('/signin', function(req, res, next) {
    return passport.authenticate('signin', function(err, user, info) {
      if (!user) {
        return res.status(400).send();
      }
      var token = jwt.sign({id: Page on Page on Page on user.id}, 'MYSECRET', {
        expiresIn: '24h'
      });
      return res.status(200).send({
        token: token
      });
    })(req, res, next);
  });
  router.options('/restricted', cors());
  router.post('/restricted', authentication, function(req, res) {
    return res.status(200).send();
  });
}

AngularJS frontend

Our frontend is make with AngularJS and it uses API from our ExpressJS backend to handle login request and simulate accessing restricted resource.

In order to get started, we use bower to install the follow dependencies:

bower install angularjs@~1.4.8
bower install bootstrap@~3.3.6

Now we create the starting point for our frontend app

Content of index.html

<html ng-app="app">
  <head>
    <title>JWT Authentication</title>
    <link rel="stylesheet" href="/public/components/bootstrap/dist/css/bootstrap.css">
  </head>
  <body>
    <div class="container">
      <div class="row">
        <div class="col-md-6">
          <h1>JWT Authentication</h1>
          <div ng-controller="appCtrl as vm">
            <div ng-show="false == vm.isAuthenticated">
              <form class="form" ng-submit="vm.submit()">
                <div class="form-group">
                  <label>Username</label>
                  <input type="text" class="form-control" name="username" ng-model="vm.username" />
                </div>
                <div class="form-group">
                  <label>password</label>
                  <input type="password" class="form-control" name="password" ng-model="vm.password" />
                </div>
                <input type="submit" class="btn btn-primary" value="Login"/>
              </form>
            </div>
            <a href="#" ng-click="vm.fetch()">Fetch resources</a>
          </div>
        </div>
      </div>
    </div>
    <script src="/public/components/angular/angular.js"></script>
    <script src="/app/app.js"></script>
    <script src="/app/appCtrl.controller.js"></script>
    <script src="/app/tokenInjector.factory.js"></script>
  </body>
</html>

In our app, we provide a form to input username and password. After login successful this form will be hidden. When click on “Fetch resource” button, our SPA will make a POST request to /restricted endpoint. In order to make this request, user must be authenticated.

Now we create the app itself.

Content of app.js

(function() {
  'use strict';
  angular
    .module('app', [])
    .config(function($httpProvider) {
      $httpProvider.interceptors.push('tokenInjector');
    });
})();

We just created the app with no dependencies at all. In the config function, we push tokenInjector to the http interceptors. We will walkthrough token injector later.

And the appController to handle application logic.

Content of appController.js

(function() {
  'use strict';
  angular
    .module('app')
    .controller('appCtrl', appCtrl);
  function appCtrl($http) {
    var _this = this;
    this.isAuthenticated = false;
    this.submit = function() {
      return $http
        .post('Page on localhost:3000', {
          username: _this.username,
          password: _this.password
        })
        .then(function(response) {
          _this.isAuthenticated = true;
          localStorage['token'] = response.data.token;
          alert('Login successful');
        }, function() {
          _this.isAuthenticated = false	;
          alert('Login fail');
        });
    }
    this.fetch = function() {
      return $http
        .post('Page on localhost:3000')
        .then(function() {
          alert('Fetch resource successful');
        }, function() {
          alert('Can not fetch resource');
        });
    }
  }
})();

Our form is bind with submit function, when this function is called. It will make a POST request to /sigin with username and password. If the username and password is correct we take the token from response and store it in localStorage. After that the form is hidden.

The “Fetch resource” button is bind with fetchResource() function, when click it will make a request to /restricted endpoint.

tokenInjector

Without tokenInjector, when we need to inject our token to our request we need to do like this.

this.fetch = function() {
  var token = localStorage['token'];
  return $http
    .post('Page on localhost:3000', {}, {
      headers: {
        Authorization: token
      }
    })
    .then(function() {
      alert('Fetch resource successful');
    }, function() {
      alert('Can not fetch resource');
    });
}

We don’t want to repeat this code in hundred of requests so we can use a httpInterceptor to automattically do it for us. That’s why we make tokenInjector. Our tokenInjector will look for token in localStorage, if token is exists in localStorage it will take the token and inject to header to http request. Our backend will use this token to authorize our request.

Conclusion

Token-based authentication enables us to construct decoupled system that are not tied to any particular authentication schema. This token might be generated anywhere and consumed on any system that use same secret for sigining the token. They are mobile-ready and do not require us to use cookies.

There are still a lot to cover about JWT, such as how to handle security details and refresh tokens when they expire but the tutorial above should demonstrate the basic usage and more important, advantages of using JSON web token.

 

Make a Todo Chrome Extension with AngularJS

AngularJS is a powerful tool to make all kind of web application. Chrome Extension is not an exception too. Today, let me show you my experience when using Angular to build an Chrome Extension. Actually, I built another version of this extension using jQuery too. But the AngularJS version is easier to build and maintain.

About this extension, it’s just an very simple Todo Application. You can test our extension on Chrome Store at here: https://chrome.google.com/webstore/detail/ng-todo/aommafigioodamhobgffifgoonmpnmmc

AngularJS todo extension

The final source code is located at here: https://github.com/davidtran/ng-todo

Project structure

This is the structure of our Chrome Extension

AngularJS Chrome Extension project structure

Manifest.json is the JSON formatted file which contains configuration for our configuration. App is the folder contains AngularJS webapp.
We use 3 libraries: jquery, angular and bootstrap for easier styling. In order to help you debug our extension easier, I use the uncompressed AngularJS script.

Prepare manifest.json

When make a Chrome Extensions, manifest.json is the most important file. This JSON formatted file will provide information about our extension, include extension name, extension description, version, locations of UI script, permissions.

{
  "manifest_version": 2,
  "name"            : "ng-Todo",
  "description"     : "Minimal todo app for minimalist",
  "short_name"      : "ng-Todo",
  "version"         : "0.1",
  "browser_action": {
    "default_popup": "index.html",
    "default_icon" : "images/logo.png"
  },
  "permissions": [
    "debugger",
    "storage"
  ],
  "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'"
}

 

In manifest.json, we set index.html is our popup webpage. So next, we examine index.html
**AngularJS Template **

Our template is created in the following code:

<!DOCTYPE html>
<html ng-app='app'>
<head>
    <title>Angular Todo Chrome Extension</title>
    <link rel="stylesheet" type="text/css" href="lib/bootstrap/css/bootstrap.min.css">
    <link rel="stylesheet" type="text/css" href="css/style.css">
</head>
<body ng-controller='todoCtrl'>
    <div class='container'>
        <div class='row'>
            <div class='col-md-12'>
                <h5>{{ (todoList | filter: {completed:false}).length}} pending task(s)</h5>
                <form id='todo-form' ng-submit='add()'>
                    <div class="form-group">
                        <input type='text' id='new-todo' ng-model='newContent'
                            class="form-control" placeholder="What you need to do ?"/>
                    </div>
                </form>
            </div>
        </div>
        <div class='row' id="todo-list">
            <div class='todo-item col-md-12' ng-repeat='todo in todoList track by Page on Page on todo.id| orderBy: createdAt'
                    ng-class='{completed: todo.completed}'>
                <input type='checkbox' ng-model='todo.completed' ng-click='toggleCompleted()'/>
                <span class='todo-content'>{{todo.content}}</span>
                <a class='btn-remove-todo pull-right' ng-click='remove(todo)'>
                    <i class='glyphicon glyphicon-remove'></i></a>
            </div>
        </div>
        <div class='row'>
            <div class='col-md-12'>
                <div id='toolbar'>
                    <a id="btn-remove-all" ng-click='removeAll()'
                        class='btn btn-small btn-default pull-right'
                        ng-show="todoList.length > 0">
                        <i class="glyphicon glyphicon-remove"></i> Clear all
                    </a>
                </div>
            </div>
        </div>
    </div>
    <script type="text/javascript" src="lib/jquery/jquery-2.1.3.min.js"></script>
    <script type="text/javascript" src="lib/angular/angular.js"></script>
    <script type="text/javascript" src="lib/bootstrap/js/bootstrap.min.js"></script>
    <script type="text/javascript" src="app/app.js"></script>
    <script type="text/javascript" src="app/todoCtrl.js"></script>
    <script type="text/javascript" src="app/todoStorage.js"></script>
</body>
</html>

 

Attribute ng-app=”app” is registered in our app at html tag. We also use attribute ng-controller=”todoCtrl” and register todoCtrl at body tag.

Our template is a little bit complex. Let’s break it down:
<h3>Todo Form</h3>

This form only has one input text to add new todo.

* Expression {{ (todoList | filter: {completed:false}).length}} filters all todo which completed attribute value is false in todoList and return total of remaining todos.
* ng-submit=’add()’ works when user submit form by press enter key, it call add() method in todoCtrl which add a new todo.
* ng-model=’newContent’ This directive bind value of input text to newContent variable. In todoCtrl we can access this variable via $scope.newContent

Todo list

This piece of HTML responsible for render our todo list.

<div class='row' id="todo-list">
		<div class='todo-item col-md-12' ng-repeat='todo in todoList | orderBy: createdAt' ng-class='{completed: todo.completed}'>
				<input type='checkbox' ng-model='todo.completed' ng-click='toggleCompleted()'/>
				<span class='todo-content'>{{todo.content}}</span>
				<a class='btn-remove-todo pull-right' ng-click='remove(todo)'><i class='glyphicon glyphicon-remove'></i> </a>
		</div>
</div>

 

The attribute ng-repeat=’todo in todoList | orderBy: createdAt’ will create a div.todo-item for every todo. We have attribute ng-class='{completed:todo.completed}’, this expression toggle completed class for #todo-item based on the completed attribute of todo item.

By binding the value of todo.completed to the checkbox, the checkbox value will be changed based on the value of todo.completed

Expression {{todo.content}} will be replace by actual value of todo.content
Because we bind remove(todo) to btn-remove-todo link, when we click that link, it will call method remove() method of todoCtrl.

We create our app module in the following codes:

angular.module("app", []);

The first parameter of module() function is the module name, we have already used this module name in attribute ng-app in html tag of index.html

The second parameter is the list of dependencies, but in our todo, we don’t need any dependency so it’s just an empty array.

todoStorage service

Our todoStorage service has responsibility of store todo data, and it has some methods to retrieve, remove and filter our todo list. In this service, we use Chrome Storage API to store our todo data.

angular.module('app').service('todoStorage', function ($q) {
    var _this = this;
    this.data = [];

    this.findAll = function(callback) {
        chrome.storage.sync.get('todo', function(keys) {
            if (keys.todo != null) {
                _this.data = keys.todo;
                for (var i=0; i<_this.data.length; i++) {
                    _this.data[i]['id'] = i + 1;
                }
                console.log(_this.data);
                callback(_this.data);
            }
        });
    }

    this.sync = function() {
        chrome.storage.sync.set({todo: this.data}, function() {
            console.log('Data is stored in Chrome storage');
        });
    }

    this.add = function (newContent) {
        var id = this.data.length + 1;
        var todo = {
            id: id,
            content: newContent,
            completed: false,
            createdAt: new Date()
        };
        this.data.push(todo);
        this.sync();
    }

    this.remove = function(todo) {
        this.data.splice(this.data.indexOf(todo), 1);
        this.sync();
    }

    this.removeAll = function() {
        this.data = [];
        this.sync();
    }

});

chrome.storage.sync.get will return data in a callback that’s why we need a callback to return data.

Notice that we call sync() method every time we modify our data. It synchronizes our data to Chrome storage.
todoStorage service will be injected to our TodoCtrl, by that way we keep our controller separated from too much logic.

TodoCtrl Controller

In index.html, we already declare our todoCtrl at the body tag. Now we implement it.

 

angular.module('app').controller('todoCtrl', function ($scope, todoStorage) {

    $scope.todoStorage = todoStorage;

    $scope.$watch('todoStorage.data', function() {
        $scope.todoList = $scope.todoStorage.data;
    });

    $scope.todoStorage.findAll(function(data){
        $scope.todoList = data;
        $scope.$apply();
    });

    $scope.add = function() {
        todoStorage.add($scope.newContent);
        $scope.newContent = '';
    }

    $scope.remove = function(todo) {
        todoStorage.remove(todo);
    }

    $scope.removeAll = function() {
        todoStorage.removeAll();
    }

    $scope.toggleCompleted = function() {
        todoStorage.sync();
    }

});

 

When todoCtrl is created, we call $scope.todoStorage.findAll to populate data in $scope.todoList. Other methods, like add(), remove(), removeAll(), toggleCompleted() are assigned to $scope service. Hence we can use them in the html template.

Run our extension

Google Chrome makes it very easy to develop and test extensions. From Google Chrome browser, go to address: chrome://extensions. On this page, click “Developer mode” to reveal “Load unpacked extension…” button. Click on that button and locate the location of our extension. Click “Select” to load our extension, after that you can see our Todo extension button on the right side of toolbar.
Anything if you want to debug popup script, right click “Inspect popup”. It will show our powerful inspector to debug our extension.
If you make changes to our extension, you can click on the “Reload” button of our extension in Extension page.

Conclusion

In this tutorial, you learn about developing a simple Todo chrome extension with AngularJS. By using Angular controller, service and template, developing an AngularJS app is easy and fun.
We learn how to create manifest.json to declare our extension and use Chrome Storage API to store todo data. In the end, you learn to test and debug our extension by tools provided by Google Chrome.

Backend Less development with AngularJS – A practical approach

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.

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:

backend less angular app 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;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;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;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 !

Gulp vs Grunt – Choose your build tool

Think about a typical project today. We usually involve a lot of tools and third-party languages likes Typescript, CoffeeScript, Sass, Less that we have to run compiler to compile them to Javascript and Css. We also need to run some tasks like: minify Css/Javascript, run unit test, setup development server, concentrate scripts… Because these tasks are repeated frequently, if we do these tasks manually, we will waste a lot of time. That’s why we need build tools to automate these tasks for us.

Build tools aren’t new kids on block, developers already use them from the very beginning of web development. Those famous tools are Phing, Ant, Makefile… But about Javascript-based build tools, the most popular are Grunt and Gulp. If you are a new Javascript developer, chance that you have to struggle to choose the best tools. Let’s reveal some insight for these two build tools:

Grunt

Grunt logo

Grunt is introduced on 2012 is it’s one of the first Javascript build tool. Grunt favors configuration and a Gruntfile – the Grunt’s task definition file – is made from a JSON-like file. Some of very popular projects which are using Grunt are: jQuery, Bootstrap, WordPress, Ghost. Below is an example of a Gruntfile:

[javascript]
grunt.initConfig({
clean: {
src: [‘build/app.js’, ‘build/vendor.js’]
},

copy: {
files: [{
src: ‘build/app.js’,
dest: ‘build/dist/app.js’
}]
}

concat: {
‘build/app.js’: [‘build/vendors.js’, ‘build/app.js’]
}

// … other task configurations …

});

grunt.registerTask(‘build’, [‘clean’, ‘bower’, ‘browserify’, ‘concat’, ‘copy’]);
[/javascript]

Because of simple configuration, Grunt is very easy to use, it’s adopted by many developer.

Gulp

gulp

Gulp on the opposite, favors code over configuration. Gulp use Node’s Stream API which is faster. As a Javascript developer, I feel that Gulp approach is suitable for me. Since Gulp is introduced, many big projects has switch Gulp, for example: Ionic, Yeomen. Let’s see an example of a Gulpfile:

[javascript]

var gulp = require(‘gulp’);
var sass = require(‘gulp-sass’);
var minifyCss = require(‘gulp-minify-css’);
var rename = require(‘gulp-rename’);

//declare the task
gulp.task(‘sass’, function(done) {
gulp.src(‘./scss/ionic.app.scss’)
.pipe(sass())
.pipe(gulp.dest(‘./www/css/’))
.pipe(minifyCss({
keepSpecialComments: 0
}))
.pipe(rename({ extname: ‘.min.css’ }))
.pipe(gulp.dest(‘./www/css/’))
.on(‘end’, done);
});
[/javascript]

A Gulpfile is also a NodeJS script. Because it uses Stream api, we only need to define source files once and after that we pipe next tasks in sequence. Look back at the Gruntfile example, you can notice that it’s required us to declare source and destination files for each task, so it take more time to define a Gruntfile.

The interesting part is each Gulp plugin is also a Node module which we can reuse in our Node application.

What are the best tools ?

When choose a tools for a new project. We have to take the following options into considerations, they are:

  • Stability
  • Community
  • Performance
  • Usability

About stability, both Gulp and Grunt are battle tested in many big projects so we can trust them.

We only use them at development phase so they are good as long as their performance is acceptable, it’s hard to notice the speed different between these tools too.

Both of Gulp and Grunt have large community. Actually, Grunt community is bigger but Gulp community is growing fast. Gulp and Grunt have plugins that can do same tasks. Beside that a lot of their plugins are just wrapper for a node module.

The only really different are usability. As a long time Javascript/Nodejs developer, I prefer Gulp over Grunt because I like their coding approach. I have known Grunt for a long time but I never has a strong feeling for it. I use Gulp in most of my project. I think Grunt is suitable for amateur developers, designers.

What do you think ? Discuss with me at the comment section.

Speed up your AngularJS app development with Gulp

Manually injecting Bower packages and AngularJS app files into index.html is a tedious job that a lot of us already do when developing an AngularJS app. We even waste more time when we need to remove or change name of those files. Fortunately, we can use Gulp to do task job for us. Just put some placeholders into our index.html and run a Gulp task and then you get the result similar to picture below:

Before and after inject files to index.html

Imagine that we have hundred of files, this tiny Gulp task will save us a lot of time.

Here are somethings we’re going to do with Gulp in this article:

  • Automatic inject bower components.
  • Automatic inject AngularJS files.
  • Setup a development server and reload browser when file changes detected.

Our demo project is very simple, it’s just have 1 module and display a welcome text on website. You can quickly grab the source code of this project at here: https://github.com/davidtran/davidtran.github.io/tree/master/gulp_practice. In order to run demo, you need to install gulp and bower first.

Let’s get started

Automatic inject bower components:

We can use wiredep to automatic locate files from bower’s packages and inject them to index.html. Wiredep does it by gather files from “main” and “dependencies” properties in bower.json of each packages in bower_componets folder, after that replace with those files with placeholder in index.html. So in order to use wiredep, we need to put a placeholder in our index.html:

[html]
<!– bower:js –>
<!– endbower –>
[/html]

And configure wiredep in gulpfile:

[javascript]
‘use strict’;

var gulp = require(‘gulp’);
var $ = require(‘gulp-load-plugins’)();
var wiredep = require(‘wiredep’).stream;
gulp.task(‘inject’, function() {

var wiredepOptions = {
directory: ‘public/components’,
exclude: [/bootstrap-sass-official/, /bootstrap\.css/]
};

return gulp.src(‘src/*.html’)
.pipe(wiredep(wiredepOptions))
.pipe(gulp.dest(‘build’));
});

[/javascript]

Upon gather files from bower_components, wiredep will replace the placeholder by reference to js files.

That’s need to said that wiredep will fail to work if a package doesn’t have “dependencies” and “main” property in its bower.json, in this case, you need to override these property. See how to do it in this article: https://github.com/taptapship/wiredep#bower-overrides. Also because of this behavior wiredep can’t not inject css files from bower packages, you need to do it by yourself.

Automatic inject AngularJS app files:

An AngularJS project can easily have hundred of files and it’s boring to manually inject all of them to our index.html by yourself. Fortunately, we can use gulp-inject and gulp-angular-filesort to automatic inject them to index.html.

  • Gulp-inject take a stream of files and replace the placeholder in index.html with a list reference to our files. We can use gulp-inject for many other files files as well, depend on filetype, it will generate the corresponding tags. For example, if we provide css file, it will generate <link> tag.
  • Gulp-angular-filesort help us sort the Angular files depend on module definitions and usage before gulp-inject put them into index.html.

Here is our we do it in our project. Put this gulp-inject’s placeholder in index.html:

[html]
<!– inject:js –>
<!– endinject –>
[/html]

Configure gulp-inject and gulp-angular-filesort in gulpfile.js.

[javascript]
var inject = require(‘gulp-inject’);
var angularFilesort = require(‘gulp-angular-filesort’);

gulp.task(‘inject’, function() {
var injectFiles = gulp.src(‘src/app/**/*.js’).pipe(angularFilesort());
var injectOptions = {
ignorePath: [‘src’]
};

//—–

return gulp.src(‘src/*.html’)
.pipe(inject(injectFiles))
.pipe(wiredep(wiredepOptions))
.pipe(gulp.dest(‘build’));
});

[/javascript]

At this point, gulp is ready to automatic inject bower components and our AngularJS app files into index.html. Let’s take a further step by reloading our browser upon file changes.

Create a web server to serve our AngularJS project and reload browser when file changes detected:

We can use browser-sync to create a development server for our app. There are many alternatives like livereload, but I choose browser-sync because it supports multiple browsers at once.

Now, we create a new gulp task to use brower-sync:

[javascript]

var browserSync = require(‘browser-sync’);
var browserSyncSpa = require(‘browser-sync-spa’);

gulp.task(‘live’, function() {

browserSync.use(browserSyncSpa({
selector: ‘[ng-app]’
}));

browserSync.init({
server: {
baseDir: [‘build’],
routes: {
‘/public/components’: ‘public/components’,
‘/src’: ‘src’
}
},
browser: ‘src’,
startPath: ‘/’
});
});

Because “public/components” and “src” folders stay outside of the base “build” folder, we have to use baseDir.routes to tell browser-sync the folder to serve files.

[/javascript]

Next, we use gulp.watch to watch for files changes and reload browser when file changes detected.

[javascript]

gulp.task(‘live’, function() {
browserSync.use(browserSyncSpa({
selector: ‘[ng-app]’
}));

gulp.watch(‘src/app/**/*.js’, function(event) {
gulp.start(‘inject’);
});

gulp.watch(‘src/app/**/*.html’, function(event) {
browserSync.reload(event.path);
});

gulp.watch([‘src/*.html’, ‘bower.json’], [‘inject’]);
browserSync.init({
server: {
baseDir: [‘build’],
routes: {
‘/public/components’: ‘public/components’,
‘/src’: ‘src’
}
},
browser: ‘src’,
startPath: ‘/’
});
});

[/javascript]

At the “inject” task, we also need to add browserSync.stream() when this task is going to finish.

[javascript]

gulp.task(‘inject’, function() {

return gulp.src(‘src/*.html’)
.pipe(inject(injectFiles))
.pipe(wiredep(wiredepOptions))
.pipe(gulp.dest(‘build’))
.pipe(browserSync.stream());
});

[/javascript]

Put it all together:

This is our index.html and gulpfile.js look like after we have finish configuring our plugins:

Content of index.html

[html]
<html ng-app=”app”>
<head>
<title>Pratice Gulp</title>

<!– inject:css –>
<!– endinject –>
</head>
<body ng-controller=”appCtrl”>

<div class=”well”>{{welcome}}</div>

<!– bower:js –>
<!– endbower –>

<!– inject:js –>
<!– endinject –>

</body>
</html>
[/html]

Content of gulpfile.js:

[javascript]

‘use strict’;

var gulp = require(‘gulp’);
var inject = require(‘gulp-inject’);
var angularFilesort = require(‘gulp-angular-filesort’);
var wiredep = require(‘wiredep’).stream;
var browserSync = require(‘browser-sync’);
var browserSyncSpa = require(‘browser-sync-spa’);

gulp.task(‘inject’, function() {
var injectFiles = gulp.src(‘src/app/**/*.js’).pipe(angularFilesort());
var injectOptions = {
ignorePath: [‘src’]
};

var wiredepOptions = {
directory: ‘public/components’,
exclude: [/bootstrap-sass-official/, /bootstrap\.css/]
};

return gulp.src(‘src/*.html’)
.pipe(inject(injectFiles))
.pipe(wiredep(wiredepOptions))
.pipe(gulp.dest(‘build’))
.pipe(browserSync.stream());
});

gulp.task(‘live’, function() {

browserSync.use(browserSyncSpa({
selector: ‘[ng-app]’
}));

gulp.watch(‘src/app/**/*.js’, function(event) {
if (event.type === ‘changed’) {
gulp.start(‘inject’);
}
});

gulp.watch(‘src/app/**/*.html’, function(event) {
browserSync.reload(event.path);
});

gulp.watch([‘src/*.html’, ‘bower.json’], [‘inject’]);

browserSync.init({
server: {
baseDir: [‘build’],
routes: {
‘/public/components’: ‘public/components’,
‘/src’: ‘src’
}
},
browser: ‘src’,
startPath: ‘/’
});
});

[/javascript]

From now we no longer waste time for inject javascript files into our project by ourselves anymore.

Happy coding !

See more premium WordPress themes by TommusRhodus