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});
});
- All the references are imported using the
importkeyword - For all the files you need to provide absolute path other than the one which are referenced by node_modules
- Wiring of common files are done into groups which are then referenced here as a single file, You will become more clear as we progress here
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;
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;
$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
- 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
- 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;
- 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 BaseServiceand 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
servicesWireupwhile 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;
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 BaseControllerorclass ManageContactController implements BaseInterfacewhere BaseInteface is an iterface in Oops philosophy - You can see here that we are not using
$scopeanywhere as I mentioned previously we will be following the ControllerAs syntax thus here in view variablearrContactDatawill be used asmanageContactController.arrContactDatawhich removes the use of$scopeand 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
appWireupwhile 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
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.