AngularJS - Javascript framework for superheroes

Sponsors



Media Partners

Back in time...

... when the dinosaurs rule the Earth

  • Websites instead of webapps
  • Client seen as a dumb interface
  • All the workload handled by the server
  • No client-side logic
  • Javascript coders seen as web designers

Getting into present days...


Javascript: the answer!


  • Javascript framework
  • MVC Architecture
  • Big web apps

A current problem


  • Too much time
  • Too much code
  • Too much stress
  • Building client-side webapp is still hard
  • DOM Manipulation
  • Data validation

Angular for the win!


Bootstrapping


Conceptual Overview


  1. The browser loads the HTML and parses it into a DOM
  2. The browser loads angular.js script
  3. Angular waits for DOMContentLoaded event
  4. Angular looks for ng-app directive, which designates the application boundary
  5. The Module specified in ng-app (if any) is used to configure the $injector
  6. The $injector is used to create the $compile service as well as $rootScope
  7. The $compile service is used to compile the DOM and link it with $rootScope

HTML Compiler

Compiler is an angular service which traverses the DOM looking for attributes

Conceptual Overview

Also knows as Model View - View Model (MVVM)


Scope & View


The scope is responsible for detecting changes to the model section and provides the execution context for expressions

The browser parses the HTML into the DOM, and the DOM becomes the input to the template engine known as the compiler. The compiler looks for directives which in turn set up watches on the model. The result is a continuously updating view which does not need template model re-merging. Your model becomes the single source-of-truth for your view.

Example Code


<input type="text" ng-model="yourName" placeholder="Enter a name here">
<h1>Hello {{yourName}}!</h1>

Hello {{yourName}}!

Directives


A directive is a behavior or DOM transformation which is triggered by the presence of a custom attribute, element name, or a class name. A directive allows you to extend the HTML vocabulary in a declarative fashion

Directives Example


angular.module('Presentation', [])
    .controller('DirectiveController', ['$scope', function ($scope) {
        $scope.customer = {
            name: 'Naomi',
            address: '1600 Amphitheatre'
        };
    }])
    .directive('myCustomer', function () {
        return {
            template: '<b>Name</b>: {{customer.name}} <b>Address</b>: {{customer.address}}'
        };
    });
    
// HTML
<div ng-controller="DirectiveController">
    <div my-customer></div>
</div>

Filters


Filters perform data transformation. Typically they are used in conjunction with the locale to format the data in locale specific output. They follow the spirit of UNIX filters and use similar syntax | (pipe)

Filters Example


<div>Formatted number: {{val | number:2}}</div>
<div>Your name (in lowercase): {{user.name | lowercase}}</div>
Formatted number: {{val | number:2}}
Your name (in lowercase): {{user.name | lowercase}}

Client-Side Routing


$route is used for deep-linking URLs to controllers and views (HTML partials).

It watches $location.url() and tries to map the path to an existing route definition. You can define routes through $routeProvider's API.

The $route service is typically used in conjunction with the ngView directive and the $routeParams service.

Routing Example


angular.module('MyApp', ['ngRoute'])
    .config(['$routeProvider', function ($routeProvider) {
        $routeProvider
            .when('/user/:id', {
                templateUrl: 'views/user.html',
                controller: 'UserController'
            });
    })
    .controller('UserController', ['$scope', '$route', function ($scope, $route) {
        $scope.userId = $route.current.params.id;
    });
    

Services


Angular services are substitutable objects that are wired together using dependency injection (DI). You can use services to organize and share code across your app.

Angular services are:

Lazily instantiated: Angular only instantiates a service when an application component depends on it

Singletons: Each component dependent on a service gets a reference to the single instance generated by the service factory

Angular offers several useful services (like $http), but for most applications you'll also want to create your own.

Services Example


angular.module('Presentation', [])
    .service('notify', ['$window', function (window) {
        var msgs = [];
        return function (msg) {
            msgs.push(msg);
            if (msgs.length > 2) {
                window.alert(msgs);
                msgs = [];
            }
        };
    })
    .controller('ServiceController', ['$scope', 'notify',
        function ($scope, notify) {
            $scope.notify = notify;
    });
    
// HTML
<div ng-controller="ServiceController">
    <input type="text" ng-init="message='Developers'" ng-model="message"/>
    <button ng-click="notify(message)") Notify</button>
</div>

Form


Form is simply a group of controls. Angular gives state to each of them, such as pristine, dirty, valid, invalid

Angular form creates an instance of FormController. FormController itself has methods and properties:

Form States


It is important to understand flow of the form states in order to use angular form properly. This flow gives you visualization of the form state from the very first time the form is rendered until the user has finished filling the form

Form Example (HTML)


<div ng-controller="FormController">
  <form name="form" class="css-form" novalidate>
    Name:
    <input type="text" ng-model="user.name" name="uName" required/>
    <br/>E-mail:
    <input type="email" ng-model="user.email" name="uEmail" required/>
    <br/>
    <div ng-show="form.uEmail.$dirty && form.uEmail.$invalid">Invalid:
      <span ng-show="form.uEmail.$error.required">
          Tell us your email.
      </span>
      <span ng-show="form.uEmail.$error.email">
          This is not a valid email.
      </span>
    </div>
    <input type="checkbox" ng-model="user.agree"
           name="userAgree" required/>
    I agree<br/>
    <div ng-show="!user.agree">
      Please agree.
    </div>
    <button ng-click="reset()" ng-disabled="isUnchanged(user)">
      RESET
    </button>
    <button ng-click="update(user)"
            ng-disabled="form.$invalid || isUnchanged(user)">
    SAVE
    </button>
  </form>
</div>

Form Example (Javascript)


function FormController ($scope) {
  $scope.master = {};
  $scope.update = function (user) {
    $scope.master = angular.copy(user);
  };
  $scope.reset = function () {
    $scope.user = angular.copy($scope.master);
  };
  $scope.isUnchanged = function (user) {
    return angular.equals(user, $scope.master);
  };
  $scope.reset();
}

Form Live Example


Name:
E-mail:
Invalid:Tell us your email.This is not a valid email.
I agree
Please agree.

Unit Testing


Unit testing as the name implies is about testing individual units of code. What makes each application unique is its logic, and the logic is what we would like to test. If the logic for your application contains DOM manipulation, it will be hard to test. In angular the controllers are strictly separated from the DOM manipulation logic and this results in a much easier testability story.

AngularJS uses Jasmine as unit testing and E2E framework

Unit testing example


function PasswordCtrl ($scope) {
  $scope.password = '';
  $scope.grade = function () {
    var size = $scope.password.length;
    if (size > 8) {
      $scope.strength = 'strong';
    } else if (size > 3) {
      $scope.strength = 'medium';
    } else {
      $scope.strength = 'weak';
    }
  };
}

var $scope = {};
var pc = $controller('PasswordCtrl', {$scope: $scope});
$scope.password = 'abc';
$scope.grade();
expect($scope.strength).toEqual('weak');

E2E testing example


It's quite easy to test services, directives, filters, controllers, etc with AngularJS and Jasmine

describe('service', function () {
  beforeEach(module('myApp.services'));
  describe('version', function () {
    it('should return current version', inject(function (version) {
      expect(version).toEqual('0.1');
    }));
  });
});