Angular Js Demo With ES 6

Hi, I'm passionate about Javascript and recently have been working on Angular Js and Ecmascript 6, It was a great experience and I wanted to share the things I learnt, so here by starting with a simple TODO application using Angular Js, EcmaScript 6 and Webpack.

The concept of selecting webpack was to unify the Javascript code so to avoid referencing all the js and css and other assets in index.html file. Moreover it simplifies the things for development and production environment.

The App demostartes a very basic list/add/edit/delete of contacts, the idea behind this demo is to just to introduce Angular and ES6 I'll be updating with more Articles in coming days to get into whole ES6 complexities.

Note : You can get the whole code used for this Demo at Github

Structure of the App

				|
				|_app
				|	|
				|	|_dist (contains the index.html file and source of running app)
				|	|
				|	|_src (contains all the developemnt files which are referenced in dist)
				|
				|_webpack (contains the desc of unifying src -> dist assests referencing)
				|
				|_package.json (dependencies)
				|
				|_webpack.app (invoking webpack)
				


Bootstrapping the Application

// importing all the libraries needed for development using import keyword					
import 'bootstrap/dist/css/bootstrap.css';
import '../sass/mixins.scss';	
import '../sass/bootstrap-override.scss';
import '../sass/style.scss';
import 'html5shiv/dist/html5shiv.js';
import 'jquery';
import 'bootstrap';
import 'is_js';
import {default as angular} from 'angular';
import {default as angularBootstrap} from './vendor/angular-bootstrap/ui-bootstrap-tpls.js';
import {default as uiRouter} from 'angular-ui-router'
import {default as angularAnimate} from 'angular-animate';
import {default as angularAria} from 'angular-aria';
import {default as angularCookies} from 'angular-cookies';
import {default as angularResource} from 'angular-resource';
import {default as angularTranslate} from 'angular-translate';
import {default as angularSanitize} from 'angular-sanitize';
import {default as appWireup} from './configurations/app-wireup.js';
import {default as servicesWireup} from './services/services';
import {default as routerConfig} from './configurations/router-config.js';
import {setupWindowConstants} from './configurations/application-constants.js';
//import {staticStringConfig} from './configurations/general-config.js';
import {default as setupModuleDefinitions} from './modules.js';

'use strict';
// Setup application wide module definitions
setupModuleDefinitions();

// This is the main application module
var mainModule = angular.module('myApp.base');

// Wire up constants
setupWindowConstants();

// Wire up services
servicesWireup();

// Wire up controllers
appWireup();

// Configure the application
angular.module('myApp.base')
    .config(routerConfig);

// Bootstrap the application and attach it to the document
angular.element(document).ready(function () {
    angular.bootstrap(document, ['myApp.base'], {strictDi: true});
});
					
This is the root entry point of the application where all the references are brought together and the application is bootstrapped key points to note here include


Injecting Module Dependencies

'use strict';
import * as angular from 'angular';

function setupModuleDefinitions() {
    angular.module('myApp.templates', []);
    angular.module('myApp.services', ['ngCookies', 'ngResource']);
    angular.module('myApp.providers', ['ngCookies', 'ngResource']);
    angular.module('myApp.components', [
        'ngAria'
        , 'ngCookies'
        , 'ngResource'
        , 'ngSanitize'
        , 'ui.router'
        , 'myApp.templates'
        , 'myApp.services'
        , 'myApp.providers'
        , 'ui.bootstrap'
    ]);
    angular.module('myApp.base', [
        'ngAria'
        , 'ngCookies'
        , 'ngResource'
        , 'ngAnimate'
        , 'ngSanitize'
        , 'ui.router'
        , 'myApp.templates'
        , 'myApp.services'
        , 'myApp.components'
        , 'myApp.providers'
        , 'ui.bootstrap'
    ]);
}

export default setupModuleDefinitions;
					
I'm using here a modularized way of angular application so as to keep the components singular hence main app being myApp.base and myApp.components being referenced here, Moreover an important point to note here is this is same file being used as modules.js while bootstrapping the application. similary are the more files which are referenced being used as config file for the application, we will have more eg, wiring service, controllers, directives etc..


App Routing


'use strict';

routerConfig.$inject = ['$stateProvider', '$urlRouterProvider'];
function routerConfig($stateProvider, $urlRouterProvider) {
    $urlRouterProvider.when('/contacts', '/contacts/add');
    $urlRouterProvider.otherwise('/home');
    $stateProvider
        .state('home', {
            url    : '/home'
            , views: {
                ''      : {
                    template      : require('../modules/home/home.html')
                    , controller  : 'HomeController'
                    , controllerAs: 'homeController'
                }
            }
        })
        .state('contacts', {
            abstract  : true,
            url       : '/contacts',
            template  : '',
            controller: ['$scope', '$state',
                ($scope, $state) => {
                    $state.go('contacts.add');
                }]
        })
        .state('contacts.add', {
            url    : '/add'
            , views: {
                '': {
                    template      : require('../modules/contacts/contacts-add.html')
                    , controller  : 'AddContactController'
                    , controllerAs: 'addContactController'
                }
            }
        })
        .state('contacts.edit', {
            url    : '/edit/:contactId'
            , views: {
                '': {
                    template      : require('../modules/contacts/contacts-edit.html')
                    , controller  : 'EditContactController'
                    , controllerAs: 'editContactController'
                }
            }
        })
        .state('contacts.list', {
            url    : '/list'
            , views: {
                '': {
                    template      : require('../modules/contacts/contacts-list.html')
                    , controller  : 'ManageContactController'
                    , controllerAs: 'manageContactController'
                }
            }
        })
}

export default routerConfig;
					
Angular state provider is being used here, Here we are injecting the references $stateProvider and $urlRouterProvider which is then exported and was used earlier as angular.module('myApp.base').config(routerConfig); while bootstrapping the app, Some key points to note here are
  1. Here we use the ControllerAs syntax so to diminish the use of $scope by doing so from the DOM we will be using the same name to resolve controller variable and controller method which will be seen later in this demo
  2. This state defination is same as a normal state defination where you can add/modify states, menu and add resolve dependencies


Intermediate service where the controller communicates


import {default as angular} from 'angular';
'use strict';

const moduleName = 'myApp.services';

class ContactsService{
    constructor($log) {
        this.contactData = [];
    }

    getContactData(){
        return this.contactData;
    }

    saveContact(obj){
        this.contactData.push(obj);
    }

    editContact(id,obj){
        this.contactData.every((ele)=>{
            if(ele.id == id){
                ele.name = obj.name;
                ele.address = obj.address;
                return false;
            }else{
                return true;
            }
        });
    }

    deleteContact(obj){
        if(confirm("Are you sure You want to delete this Contact ?"))
            this.contactData.splice(this.contactData.indexOf(obj),1);
    }

    getContact(id){
        var arrData = this.contactData.filter((ele)=>{
           return(ele.id == id);
        });

        if(arrData.length == 1)
            return arrData[0];
        return 0;
    }

    toString() {
        return 'ContactsService';
    }

    static contactsServiceFactory($log) {
        return new ContactsService($log);
    }
}

ContactsService.serviceName = 'contactsService';
ContactsService.contactsServiceFactory.$inject = ['$log'];

export default ContactsService;
					
This is a simple Demo kind of service layers where the controllers communicate for Data manipulation, In real world it makes more sense to have API'or any server side methods to be called here via http protocols, some key points to note here include.
  • You can use a $http method here by injecting the $http dependency as here $log is used
  • Unlike any class in ES you can extend base classes like this could also be class ContactsService extends BaseService and using a super constructor to call base constructor and using methods in the same Oops fashion
  • Whenever you create a service its important that its being referenced to app I have accumulated all the referencing of the services in a common file services which is then used as servicesWireup while bootstrapping the application, This is how the file looks like
    								'use strict';
    								import {default as angular} from 'angular';
    								import {default as ContactsService} from './contacts/contact-service.js';
    
    								/**
    								 * Wire-up services
    								 */
    								function servicesWireup() {
    								    angular.module('myApp.services')
    								        .factory(ContactsService.serviceName, ContactsService.contactsServiceFactory);
    								        //Add more services here
    
    								}
    
    								export default servicesWireup;
    							


Manage Controller - Responsible for listing and routing to add/edit/delete contact


'use strict';

class ManageContactController {
    constructor($log,contactsService,$state) {
        this.$log = $log;
        this.contactsService = contactsService;
        this.arrContactData = [];
        this.$state = $state;
        $log.info('ManageContactController...');
        this.initData();
    }

    initData(){
        this.arrContactData = this.contactsService.getContactData();
    }

    addContact(){
        this.$state.go('contacts.add');
    }

    editData(id){
        this.$state.go('contacts.edit',{contactId:id});
    }

    deleteData(contact){
        this.contactsService.deleteContact(contact);
        this.initData();
    }
}
ManageContactController.$inject = ['$log','contactsService','$state'];

export default ManageContactController;
					
Controller responsible for display of the contact available, the dependencies are injected such as ManageContactController.$inject = ['$log','contactsService','$state']; and used inside constructor as we follow a strict class rule we define a constructor and then use with this ref in the native methods. Some importants points to consider here are as follows :
  • You can inject 'n' number of dependencies
  • Unlike common class this class can be extented by a base class or implemented by interface such as class ManageContactController extends BaseController or class ManageContactController implements BaseInterface where BaseInteface is an iterface in Oops philosophy
  • You can see here that we are not using $scope anywhere as I mentioned previously we will be following the ControllerAs syntax thus here in view variable arrContactData will be used as manageContactController.arrContactData which removes the use of $scope and code becomes more organized and clear.
  • Whenever you create a controller its important that its being referenced to app I have accumulated all the referencing of the controllers also in a common file app-wireup which is then used as appWireup while bootstrapping the application, This is how the file looks like
    								'use strict';
    								import {default as angular} from 'angular';
    								/**
    								 * Wire up controllers and all other stuff to angular for app here
    								 */
    								function appWireup() {
    								    angular.module('myApp.base')
    								        .controller('HomeController', require('../modules/home/home-controller'))
    								        .controller('AddContactController', require('../modules/contacts/add-contact-controller'))
    								        .controller('EditContactController', require('../modules/contacts/edit-contact-controller'))
    								        .controller('ManageContactController', require('../modules/contacts/manage-contact-controller'));
    
    								}
    
    								export default appWireup;
    							


Manage View - Responsible for display of the contacts

Contact List

No. Name Address Edit Delete
{{$index + 1}} {{eachContact.name}} {{eachContact.address}} Edit Delete
No Contacts Data Available, Please Add Contacts

The view here is pretty much straightforward and understandable to anyone who has used angular before just a noticable difference is instead of using the controller variable directly we're using the controllerAs pattern hence used as manageContactController.arrContactData and manageContactController.editData(eachContact.id)
  • Note : When webpack complies this for production all the views are converted to template cache which resides in build-js, that's the reason why dist folder is enough to run the application

I'm leaving the other code as for self understanding, the code however is simple for add and edit contact which you can get from the Github repository you can go through the Read me file explaning how to run the app.

Click Here to get the code used for this Demo.



I dedicate this to My Teachers and friends who constantly encouraged me in my every learning curve.

Feel free to write me at : vinodlouis@hotmail.com