Angular 101

An introduction to AngularJS

By Chris Hourihan

Table of Contents

  1. Angular Fundamentals
  2. Modules
  3. $scope
  4. Controllers
  5. Templating
  6. Providers (Services and Factories)
  7. Directives (core / custom)
  8. Filters (core / custom)
  9. Server Interactions ($http and $q)
  10. Testing (unit and e2e)

Fundamentals

What is Angular?

  • MVW or MV* framework
  • Designed to enhance HTML, making it dynamic
  • Removes DOM manipulation
  • Unopinionated approach

Two-Way Data binding

  • Synchronizes the View with the Model and vice-versa via a $digest cycle and dirty checking
  • The underlying model is a plain-old JavaScript Object
  • The models are easily parsed to and from JSON, making server-side communication simple
  • Binding occurs in the templates via {{}} or ng-bind
  • Models can also be two way data-bound via the ng-model directive

Dependency Injection

  • Software design pattern for resolving dependencies
  • AngularJS provides the $injector service which does this automatically by inspecting the arguments of a function
  • To handle minification/uglification, Angular provides an alernate syntax via an Array and Strings
  • Lazy loaded

Modules

Modules

  • Applications are built from Modules
  • Modules are the containers for a component of an application
  • Modules can depend on other modules
  • Modules make our code reusable, limit scope, and enable testability

Module Syntax

Setter


	angular.module('MyModuleName', [ModuleDependency1, md2, md3, ...]);
							

Getter


	angular.module('MyModuleName')
		.controller('MyController', function() {});
							

Loading the Module in HTML


	<html>
	  <head></head>
	  <body ng-app="MyModuleName">
	    <h1>Hello World</h1>
	  </body>
	</html>
						

$scope

$scope

  • Represents the ViewModel
  • Anything attached to the $scope object is then accessible in the HTML
  • 
    	$scope.heading = 'Hello World!';
    							
    
    	<body ng-app="MyModuleName" ng-app="MyController">
    	  <h1>{{heading}}</h1> <!-- <h1>Hello World!</h1> -->
    	</body>
    							
  • $scope is only used in our Controllers and Directives, where we bind data to a view
  • The $rootScope object is the parent of all $scopes

Controllers

Controllers

  • Controllers are responsible for setting up the link between your View and Model (i.e., $scope)

	angular.module('MyModuleName')
		.controller('MyController', function($scope, dependency) {
	                 $scope.heading = 'Hello World!';
		});
							

	// Syntax for minification/uglification
	angular.module('MyModuleName')
		.controller('MyController', ['$scope', 'dependency', function($scope, dependency) {
			$scope.heading = 'Hello World!';
		});
							

Templating

Expressions

  • "JavaScript-like" pieces of code that conditionally change the View (DOM) to match the Model
  • Live in {{}} or ng-bind and evaluated against $scope
  • Angular re-evaluates these expressions during each $digest cycle, making updates based on any changes

Example


	$scope.heading = 'Hello World!';
	$scope.updateHeading = function(newHeading) {
		$scope.heading = newHeading;
	}
						

	<h1>{{heading}}</h1> <!-- <h1>Hello World!</h1>-->
	<button ng-click="updateHeading('Foobar')">Update</button> <!-- Click -->
						

	<h1>{{heading}}</h1> <!-- <h1>Foobar</h1>-->
						

Providers

Services and Factories

  • Containers for our application's Model, where the data and business logic should live
  • Services are invoked with new, thus all data/logic should be bound to this
  • Factories are not invoked with new and can return anything (e.g., Functions, Objects, Arrays, simple values)
  • Everything returned from either a Service or Factory is a singleton

Service Syntax


angular.module('MyModule')
	.service('MyService', [myService]);

function myService() {
	this.bar = 'bar';
	this.doSomething = function doSomething() {
		return 'foo' + this.bar;
	}
}
						

angular.module('MyModule')
	.controller('MyController', ['$scope', 'MyService', myController]);

function myController($scope, myService) {
	$scope.heading = myService.doSomething();
}
						

<h1>{{heading}}</h1> <!-- <h1>foobar</h1>-->
						

Factory Syntax


angular.module('MyModule')
	.factory('MyService', [myService]);

function myService() {
	var bar = 'bar';

	return {
			doSomething: doSomething
	}

	function doSomething() {
		return 'foo' + bar;
	}
}
						

angular.module('MyModule')
	.controller('MyController', ['$scope', 'MyService', myController]);

function myController($scope, myService) {
	$scope.heading = myService.doSomething();
}
						

Providers

  • Used throughout your application just like Factories or Services, however they can also be passed into the .config() of your Module

Provider Syntax


angular.module('MyModule')
    .provider('MyService', function() {
        var bar = 'bar';

    return {
        setBar: function(newBar) { bar = newBar; },
        $get: function() {
            return {
                doSomething: function() { return 'foo' + bar; }
            };
        }
    };
  });
						

angular.module('MyModule')
    .config(function(MyServiceProvider) {
        MyServiceProvider.setBar('baz');
    })
    .controller('MyController', function($scope, MyService) {
        $scope.heading = MyService.doSomething();
    });
						

Directives

What are Directives?

  • Powerhouses of AngularJS and are the underlying components that make a rich client-side application
  • Can enhance existing elements with functionality (e.g., ng-required, ng-click)
  • Can extend functionality to previously non-functional elements (e.g., ng-repeat, ng-class, ng-show/hide)
  • Can be brand-new elements with their own functionality (e.g., ng-include)
  • Directives can be set via custom element tags, attributes, classes, or uncommonly as comments

Common Examples

  • ng-repeat provides a repeater for displaying the same template over a collection of like elements
  • ng-model allows us to bind HTML inputs to Models on our $scope
  • ng-click allows us to handle click events on buttons, anchors or any other element, with the function bound only to the local $scope
  • ng-href/ng-src allow us to dynamically set href and src attributes. Important since we want angular to resolve the href/src before it's clicked or the image downloaded
  • ng-class allows us to dynamically add/remove classes to elements

Common Examples Cont.

  • ng-show/ng-hide allow us to toggle the display property of elements based on a boolean (or truthy/falsy) value
  • ng-if will actually remove the element from the DOM and the $scope it creates
  • ng-switch removes elements from the DOM based on a value (doesn't have to be boolean) and can apply to multiple elements like a switch statement
  • ng-bind is an alternative to {{}} that prevents edge-cases where there is a brief flicker of the {{}} before Angular resolves the value
  • ng-include allows you to pull in a template of HTML from the server or pre-loaded in the template cache

Custom Directives - Usage

  • Enable us to create and encapsulate new functionality and views in reusable and injectable way
  • Can be used as tags, attributes, classes, or comments as specified via the restrict property on the Directive Definition Object (DDO)
  • E for Element/Tag, A for Attribute, C for Class, M for Comment, Default to EA
  • Follow a kebab-case convention for use within the DOM (in JavaScript, use camelCase, Angular handles the conversion)

<my-directive></my-directive>
<div my-directive></div>
<div class="my-directive"></div>
<!-- directive: my-directive -->
						

Custom Directives - Templates

  • Directives can be used as templating engines, so you not only extend an element with functionality, but also extend the DOM with content
  • template and templateUrl are the two ways of associating DOM content with a Directive

Custom Directives - DDO

  • The Directive Definition Object is the API for creating Directives
  • Simple JavaScript Object with certain properties

function myDirective() {
    return {
        restrict: 'EA', // how the directive can be used
        templateUrl: 'myDirective.html', // template HTML
        scope: true, // how we setup the directive's scope
                     // true => new scope prototypally inherited from parent
                     // false => parent scope
                     // object literal => isolate scope where custom properties can be injected
        link: function link(scope, elem, attrs) {}, // provides DOM access
        controller: function myController($scope) {}, // like other controllers, except specific to the directive's scope

    }
}
							

Filters

What are Filters?

  • Provide a means of processing data and returning a transformed version of it (e.g., filtered list, specific date format)
  • Filters can be used in templates via the | character
  • 
    {{ object_expression | filterName : expression : comparator}}
    							
  • Filters can be used in JavaScript via the $filter service
  • 
    $filter('filterName')(object, expression, comparator)
    							
  • Filters do not alter the underlying data

Common Examples

  • date converts a date to a specified format and timezone
  • limitTo limits the elements returned in the collection to the specified number
  • orderBy orders the elements returned in the collection based on the predicate
  • linky finds links in a snippet of text and converts them to actual Anchor links

Custom Filters - Usage

  • Use the filter function and return a function used to filter the object
  • Angular will pass the value or collection to be run through the filter function
  • 
    angular.module('MyModule')
        .filter('capitalize', function() {
            return function capitalizer(text) {
                return text.charAt(0).toUpperCase() + text.slice(1);
            };
        })
        .controller('MyController', function($scope) {
            $scope.heading = 'foobar';
        });
    							
    
    <h1>{{heading | capitalize}}</h1> <!-- <h1>Foobar</h1>-->
    						

Server Interactions

$http

  • $http is a service that enables server-side communication via AJAX requests
  • $http is both a function, whereby an AJAX configuration object is passed in, and an object with convenience GET, POST, DELETE, and PUT methods (also exposes HEAD, JSONP, and PATCH)
  • Returns a Promise object based on the $q service, with additional success and error convenience functions

$q

  • $q is a simplified Promise library implementation that supports then, catch, finally, and all methods
  • Exposes a deferred API

function asyncGreet(name) {
    var deferred = $q.defer();
    setTimeout(function() {
        if (okToGreet(name)) { deferred.resolve('Hello, ' + name + '!'); }
        else { deferred.reject('Greeting ' + name + ' is not allowed.'); }
    }, 1000);
    return deferred.promise;
}
							

Testing

Tech Overview

  • Karma - Unit Test Runner
  • Protractor - E2E Test Runner
  • Jasmine - Test Framework and Assertion Library
  • Mocha, Chai, and Sinon - Test Framework and Assertion Library
  • ngMock - AngularJS Module to Mock and Inject Services

Unit Testing - Setting Up Tests

  1. Load the module of the component being tested
    
    describe('MyController', function() {
    	beforeEach(module('ch.Main'));
    });
    							
  2. Set stubbed out or mocked out services
    
    beforeEach(module(function($provide) {
      stubbedService = {
      	mockMethod: jasmine.createSpy('mockedMethod')
      };
    
      $provide.value('MyService', stubbedService);
    }));
    							

Unit Testing - Setting Up Tests Cont.

  1. Inject any other needed services and load your component
    
    beforeEach(inject(function($rootScope, $injector, $controller) {
      anotherService = $injector.get('anotherService');
      scope = $rootScope.$new();
    
      myController = $controller('MyController', {
      	$scope: scope,
      	anotherService: anotherService
      })
    }));
    							
  2. Write your tests!
    
    it('should exist', function() {
      expect(myController).toBeDefined();
    });
    								

E2E Testing

  • Protractor is built on top of WebDriverJS, which wraps a Selenium server for doing browser-based automation testing
  • Code-based API for interacting with elements on the page
  • Works with both Jasmine and Mocha/Chai/Sinon testing frameworks

it('should default to the correct page', function() {
  expect(browser.getCurrentUrl()).toContain('/home');
});
						

it('should set the correct heading', function() {
  expect(element(by.css('.heading')).getText()).toEqual('Hello World!');
});
						

Additional Resources and References