YAAP: Yet another AngularJS Presentation

Presented by Carlos Justiniano / @cjus / Presentation version: 0.5

A few words about this presentation

  • We won't cover all of Angular - it's too big silly!
  • This is built using Reveal.js, featuring AngularJS
  • It's mobile and tablet friendly for your viewing pleasure
  • Entire project available on Github
  • The source code featured is stored within the project
  • Issues with this presentation? Fix it and issue a pull request

Latest presentation at: http://github.com/cjus/AngularPresentation

The changing web

  • The advent of AJAX marked a web shift
  • As a result sites became more responsive
  • And increasingly complex web applications were being built

Handling front-end complexity

  • Code moves into the front-end
  • Developers face increased code complexity
  • Enter: open-source front-end web frameworks with names like: Backbone, Ember, Knockout and Angular

AngualrJS

  • Angular is loosely similar to many of the other frameworks that emerged to address the increasing demands of modern web development
  • However, the similarities quickly give way to an amazing array of cleanly integrated and forward thinking features
  • We'll cover many of Angular's features in this presentation

Why AngularJS?

  • Embraces the spirit and potential of HTML5
  • Offers features which are designed to work together
  • Simplifies building modern web application
  • When used correctly, it makes your code highly testable

The original vision

Originally, Angular was designed as a way of enabling designers to use HTML markup to build application without having to know how to program

The core belief is that the best way to build user interfaces is by using declarative programming

In this approach, designers would be able to declare the behavior or components without knowing how the underlying behaviors were implemented

What can Angular do for you?

  • Make it easier for you to reason about and build your web application - regardless of its size and complexity
  • Applications which embrace Angular's features are inherently more testable

Demo code

Here's the structure of the basic angular enabled page we'll reuse in this presentation.


  <!DOCTYPE html>
  <html ng-app>
  <head>
    <title>Basic Angular App</title>
  </head>
  <body>
    <script src="angular.min.js"></script>
    <script src="app.js"></script>
  </body>
  </html>
    

Before we dig too deeply into code lets examine a few core concepts...

Core Concepts and Features

Embracing best practices

  • Angular borrows heavily from other successful frameworks
  • Angular implements popular idioms while embracing HTML and other web standards
  • It's more about embracing HTML than wrestling with it

It's all about the markup

Declarative programming

Angular embraces HTML, by allowing designers and developers to describe what their HTML should do

HTML5: increasingly declarative

HTML5 data attribute

The HTML5 data attribute allows designers to specify additional properties and behaviors for HTML5 elements.

By querying the DOM, programmers can detect the presence of attributes to augment the element.

In this example, jQueryMobile uses the data-role and data-filter-placeholder attributes to determine how the list should be transformed


  <ul data-role="listview" data-filter="true"
         data-filter-placeholder="Search fruits..."
         data-inset="true">
    <li><a href="#">Apple</a></li>
    <li><a href="#">Banana</a></li>
    <li><a href="#">Cherry</a></li>
    <li><a href="#">Cranberry</a></li>
    <li><a href="#">Grape</a></li>
    <li><a href="#">Orange</a></li>
  </ul>
    

From the above to this...

To this:

Angular calls these directives

Earlier, we saw examples of directives when we looked at HTML for our basic angular app


  <!DOCTYPE html>
  <html ng-app>
  <head>
    <title>Basic Angular App</title>
  </head>
  <body>
    <script src="angular.min.js"></script>
    <script src="app.js"></script>
  </body>
  </html>
    

The ng-app in the markup is an Angular directives used to inform Angular where the Angular application begins


Source: /samplecode/BasicAngularApp

Extending HTML

Other important features include

  • Data Binding: Bidirectional sync between model and views
  • Scope: A shared context used to store data, models
  • Modules: A container for organizing your code
  • Controllers: The C in MVC, business logic behind views
  • Dependency Injection: Just in time access to code
  • Providers: Factories and Service which are dependency injectable
  • Support for testing: Both BDD unit tests and End-to-End
  • And more...

Pulling it altogether: Angular MVC

Angular support application separation of concerns using MVC and modules such as services and routers

  • M: Model is implemented using the Scope
  • V: Views are real HTML based templates
  • C: Controllers are Constructor functions with access to Model data

Data binding

Data binding

  • Allows data to be bound to markup
  • Updated in real-time as data changes

  !doctype html>
  <html ng-app>
  <head>
  </head>
  <body>
    <div>
      <label>Name:</label>
      <input type="text" ng-model="yourName"
              placeholder="Enter a name here">
      <hr>
      <h1>Hello {{yourName}}!</h1>
      </div>
      <script src="js/angular.min.js"></script>
  </body>
  </html>
    

The ng-model in the <input> markup above is an Angular directive used to bind data an input control

Let's see a live example:


Hello {{yourName}}!

More fun with data binding

Expressions can be evaluated inside of mustache tags


  <!DOCTYPE html>
  <html ng-app>
  <head>
    <title>DataBinding</title>
  </head>
  <body>
    <h2>Live text: {{ data.message }}</h2>
    <h2>{{ "Hello " + "World!" }}</h2>
    <h2>{{ 2 + 4}}</h2>

    <label>Message: </label>
    <input type="text" ng-model="data.message">

    <script src="angular.min.js"></script>
  </body>
  </html>
    

Source: /samplecode/DataBinding

Live text: {{ data.message }}

{{ "Hello " + "World!" }}

{{ 2 + 4}}

Data binding and filters

  • Angular enables data filtering
  • Bound expressions can be further processed through a pipe of filters
  • The format is: {{ expression | filter }}

  <!DOCTYPE html>
  <html ng-app>
  <head>
    <title>Data binding and Filters</title>
  </head>
  <body>
     <p>1) Cost: {{ 11.99 + (11.99 * 0.33) | currency }}</p>
     <p>2) Data: {{ 1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z' }}</p>
     <p>3) Lowercase: {{ "Buy Now!" | lowercase }}</p>
     <p>4) Uppercase: {{ "Buy Now!" | uppercase }}</p>
     <p>5) Here is some JSON data: {{ {'Boy':'Fred', 'Girl':'Jill'} | json }}</p>
     <script src="angular.min.js"></script>
  </body>
  </html>
    

Source: /samplecode/DataBindingWithFilters

Here's the output

1) Cost: {{ 11.99 + (11.99 * 0.33) | currency }}

2) Data: {{ 1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z' }}

3) Lowercase: {{ "Buy Now!" | lowercase }}

4) Uppercase: {{ "Buy Now!" | uppercase }}

5) Here is some JSON data: {{ {'Boy':'Fred', 'Girl':'Jill'} | json }}

Data binding with CSS classes


  <!DOCTYPE html>
  <html ng-app>
  <head>
    <title>DataBindingWithClasses</title>
    <style>
      .red { color: red; }  .green { color: green; }  .blue { color: blue; }
    </style>
  </head>
  <body>
    <h1 class="{{ data.classColor }}">Basic Angular App</h1>
    <label>Title color: </label>
    <input type="text" ng-model="data.classColor">
    <script src="angular.min.js"></script>
  </body>
  </html>
    

Source: /samplecode/DataBindingWithClasses

Let's try it!

Data Binding with Classes

Notice anything missing?

  • The HTML is clean, there are no class or id's on the markup
  • We didn't write any JavaScript
  • There was no need to write event handlers


What is this magic?

Scope

  • A scope is a JavaScript object
  • Angular assigns a default scope to each angular application
  • Like plain old JS objects, it can hold data, functions, watch expressions and fire events

Here's a simple Scope


  <!DOCTYPE html>
  <html ng-app ng-init="data.message='Hello, from your application scope.';">
  <head>
    <title>Simple Scope</title>
  </head>
  <body>
    <h2>Scope says: {{ data.message }}</h2>
    <script src="angular.min.js"></script>
  </body>
  </html>
    

Source: /samplecode/SimpleScope

Understanding Scope

  • A Scope starts its life as a JavaScript object
  • Angular then extends it with additional functionality
  • A Scope can be nested within another scope
  • Scopes can be passed around an application and shared

Advanced Scope concepts

  • A Scope can broadcast and receive events
  • A Scope can watch expressions and respond to changes

Directives Revisited

Extending HTML

With Angular we can define our own element as I did below with the my ph-panel directive:


  <ph-panel title="This is the panel's title">
    <p>This is the panel's body.</p>
  </ph-panel>
    

Here is the live code using the ph-panel directive:

This is the panel's body.

A few extra lines for good measure

A few extra lines for good measure

A few extra lines for good measure

A few extra lines for good measure


Source: /samplecode/ph-panel

Angular Modules

  • Angular provide Modules as a way of organizing our code

  <!DOCTYPE html>
  <html ng-app="myApp">
  <head>
    <title>Simple Angular Module</title>
  </head>
  <body>
    <script src="angular.min.js"></script>
    <script src="app.js"></script>
  </body>
  </html>
  

  angular.module('myApp', []);
  

Source: /samplecode/SimpleModule

Controllers


  <!DOCTYPE html>
  <html ng-app="myApp">
  <head>
    <title>Simple Controller</title>
  </head>
  <body >
    <div ng-controller="myFirstController">
      {{data.message}}
    </div>
    <script src="angular.min.js"></script>
    <script src="app.js"></script>
  </body>
  </html>
  

  angular.module('myApp', [])
    .controller('myFirstController', function($scope) {
      $scope.data = {
        message: 'Hello from a simple controller!'
      };
    });
  

Source: /samplecode/SimpleModule

Controllers and Scope

Chaining to modules


  var myApp = angular.module('myApp', []);
  myApp.controller('myFirstController', function($scope) {
    $scope.data = {
      message: 'Hello from a simple controller!'
    };
  });
  

  angular.module('myApp', [])
    .controller('myFirstController', function($scope) {
      $scope.data = {
        message: 'Hello from a simple controller!'
      };
    });
  

Nested controllers and Scope (HTML)


  <div ng-controller="myFirstController">
    <p>FIRST CONTROLLER<br>
    First controllers message: [ {{data.message}} ]<br>
    Second controllers message: [ {{data2.message}} ]<br>
    Third controllers message: [ {{data3.message}} ]</p>

    <div ng-controller="mySecondController">
      <hr>
      <p>SECOND CONTROLLER<br>
      First controllers message: [ {{data.message}} ]<br>
      Second controllers message: [ {{data2.message}} ]<br>
      Third controllers message: [ {{data3.message}} ]</p>
    </div>
  </div>

  <div ng-controller="myThirdController">
    <hr>
    <p>THIRD CONTROLLER<br>
    First controllers message: [ {{data.message}} ]<br>
    Second controllers message: [ {{data2.message}} ]<br>
    Third controllers message: [ {{data3.message}} ]</p>
  </div>
    

Source: /samplecode/NestedControllers

Nested controllers and Scope (JavaScript)


  angular.module('myApp', [])
    .controller('myFirstController', function($scope) {
      $scope.data = {
        message: 'Hello from the first controller!'
      };
    })
    .controller('mySecondController', function($scope) {
      $scope.data2 = {
        message: 'Hello from the second controller!'
      };
    })
    .controller('myThirdController', function($scope) {
      $scope.data3 = {
        message: 'Hello from the third controller!'
      };
    });
    

Source: /samplecode/NestedControllers

Controllers and methods

!Function.prototype

Dependency Injection


  angular.module('myCoolApp', []);
  

  angular.module('myCoolApp', ['ngRoute']);
  

  angular.module('myCoolApp', ['ngRoute']);
    .config(function($routeProvider) {
      'use strict';
      $routeProvider
        .when('/', {
          templateUrl: 'views/main.html',
          controller: 'MainCtrl'
        })
        .otherwise({
          redirectTo: '/'
        });
    });
  

Providers

  • Angular has a concept called providers which is also used to implement factories and services
  • Using them promotes cleaner and more testable code
  • And better yet, they're injectable!

Simple Service and Factory

HTML


  <!DOCTYPE html>
  <html ng-app="myApp">
  <head>
    <title>Simple Service and Factory</title>
  </head>
  <body >
    <div ng-controller="myController">
      <p>Get User from Service: {{ userFromService }}</p>
      <p>Create new user from Factory: {{ createUserWithFactory('Anatoly', 'Karpov') }}</p>
      <p>Create another new user from Factory: {{ createUserWithFactory('Magnus','Carlsen') }}</p>
    </div>
    <script src="angular.min.js"></script>
    <script src="app.js"></script>
  </body>
  </html>
    

Source: /samplecode/SimpleServiceAndFactory

Simple Service and Factory

JavaScript


  angular.module('myApp', [])
    .controller('myController', ['$scope', 'FirstService', 'FirstFactory',
      function($scope, myFirstService, myFirstFactory) {
        $scope.userFromService = myFirstService.getUser();
        $scope.createUserWithFactory = function(firstName, lastName) {
          var user = myFirstFactory.createUser(firstName, lastName);
          return JSON.stringify(user);
        };
    }])
    .service('FirstService', function() {
      var user = {
        "firstName": "Garry",
        "lastName": " Kasparov"
      };
      this.getUser = function() {
        return JSON.stringify(user);
      };
    })
    .factory('FirstFactory', function() {
      return {
        createUser: function(firstName, lastName) {
          return {
            "firstName": firstName,
            "lastName": lastName
          };
        }
      };
    });
    

Source: /samplecode/SimpleServiceAndFactory

Simple Service and Factory

Output


  Get User from Service: {"firstName":"Garry","lastName":" Kasparov"}

  Create new user from Factory: {"firstName":"Anatoly","lastName":"Karpov"}

  Create another new user from Factory: {"firstName":"Magnus","lastName":"Carlsen"}
    

So what then is a provider?

  • A service is a module which returns a single instance of an object
  • A factory is a module which returns a new object each time it's referenced
  • So what then is a provider?
  • A provider is a module which is more comprehensive than a service or factory and can be configured before it's made available

StartDate Provider

HTML


  <!DOCTYPE html>
  <html ng-app="myApp">
  <head>
    <title>Provider</title>
  </head>
  <body >
    <div ng-controller="myController">
      <p>Get start Date: {{ startDate }}</p>
    </div>
    <script src="angular.min.js"></script>
    <script src="app.js"></script>
  </body>
  </html>
    

Source: /samplecode/Provider

StartDate Provider

JavaScript


  angular.module('myApp', [])
    // controller
    .controller('myController', ['$scope', 'StartDate',
      function($scope, myDateProvider) {
        $scope.startDate = myDateProvider.getStartDate();
      }])

    // provider
    .provider('StartDate', function() {
      this.startDate = new Date();
      this.$get = function() {
        var startDate = this.startDate;
        return {
          getStartDate: function() {
            return startDate;
          }
        };
      };
      this.setStartDate = function(newDate) {
         this.startDate = newDate;
      };
    })

    // config
    .config(function(StartDateProvider) {
      //StartDateProvider.setStartDate(new Date("October 13, 1975 11:13:00"));
    });
    

Source: /samplecode/Provider

Which should I use?

So when do I use a service, factory or provider?

  • Services: login and user profile management services
  • Factories: birds for your angry birds clone
  • Providers: when services or factories just won't do

Best practices

  • Review online style guides and best practices
  • Project structure
  • HTML markup

So this:


    <input class="ipt" type="text" placeholder="name" require ng-model="user.name">
      

Rather than this:


    <input require class="ipt" ng-model="user.name" placeholder="name">
      

Best practices (cont)

  • Angular uses the ng- prefix as a namespace
  • It also prefixes the $ symbol in front of its providers, modules and services
  • Best to avoid using those in your own code

Best practices (cont)

  • Use a consistent naming convention
  • Use JavaScript style naming conventions, after all this isn't Python or Ruby
  • Modules, Directives, Filers should be named lowerCamelCase
  • Controllers should be named UpperCamelCase
  • Factories should be named UpperCamelCase, but Services should be lowerCamelCase

Best practices (cont)

  • Automate your workflow using tools like Yeoman
  • Don't use globals. Resolve all dependencies using Dependency Injection
  • Do not pollute your $scope. Only add functions and variables that are being used in the templates
  • Do not pollute your controllers. Consider what functionality should be moved to services, factories and providers

Best practices (cont)

  • Don't manipulate the DOM inside of your controllers
  • DOM manipulation should be done in directives
  • Don't scatter backend (AJAX) calls throughout your controllers, put those in services.
  • Prefer Angular providers over external ones.

  $timeout instead of setTimeout
  $interval instead of setInterval
  $window instead of window
  $document instead of document
  $http instead of $.ajax
    

Debugging an Angular application

Testing with Angular

  • We can use Karma to test our Angular code
  • Install Karma from: http://karma-runner.github.io/
  • Karma can execute Jasmine BDD style tests
  • For end-to-end tests Angular has a tool called Protractor

Structure of a
Jasmine test


  describe('Simple Test Spec', function() {
    it('should just pass', function() {
      expect(true).toBe(true);
    });
  });
    
  • Just JavaScript which calls Jasmine test framework code
  • Each test has three main blocks: describe, it and expect
  • Learn more about Jasmine at http://jasmine.github.io

Source: samplecode/SimpleTest/simpleTestSpec.js

Running our test

  • Run npm install on this project's root folder
  • At the terminal prompt just run: ./karma_start.sh

  ~/AngularPresentation$ ./karma_start.sh
  INFO [karma]: Karma v0.12.16 server started at http://localhost:9876/
  INFO [launcher]: Starting browser Chrome
  INFO [Chrome 35.0.1916 (Mac OS X 10.9.3)]: Connected on socket Q47fUfs2QdHA5DNhAjXi with id 33541008
  Chrome 35.0.1916 (Mac OS X 10.9.3): Executed 1 of 1 SUCCESS (0.01 secs / 0.008 secs)
    

Broken tests

  • Normally in start with tests that fail
  • Let's make sure we can detect failed tests

  describe('Simple Test Spec', function() {
    it('should just pass', function() {
      expect(true).toBe(false);
    });
  });
    

  INFO [watcher]: Changed file "/Users/cjus/dev/cjus/AngularPresentation/samplecode/SimpleTest/simpleTestSpec.js".
  Chrome 35.0.1916 (Mac OS X 10.9.3) Simple Test Spec should just pass FAILED
          Expected true to be false.
          Error: Expected true to be false.
              at null. (/Users/cjus/dev/cjus/AngularPresentation/samplecode/SimpleTest/simpleTestSpec.js:3:18)
  Chrome 35.0.1916 (Mac OS X 10.9.3): Executed 2 of 2 (1 FAILED) (0.032 secs / 0.028 secs)
    

Dependency injection and testing

  • DI makes testing Angular possible

Testing Angular


  describe('Angular element and expression test', function() {
    var element
      , $scope;

    beforeEach(module("myApp"));
    beforeEach(inject(function($compile, $rootScope) {
      $scope = $rootScope;
      element = angular.element("<div>{{2 + 2}}</div>");
      element = $compile(element)($rootScope);
      $scope.$digest();
    }));

    it('should equal 4', function() {
      expect(element.html()).toBe("4");
    });
  });
    

Testing controllers


  describe('Angular controller test', function() {
    var myController
      , scope;

    beforeEach(module("myApp"));
    beforeEach(inject(function($controller, $rootScope) {
      scope = $rootScope.$new();
      myController = $controller('myController', {
        $scope: scope
      });
    }));

    it('should have a getDate function which returns a date', function() {
      expect(scope.getDate()).not.toBeNull();
    });
  });
    

Source: samplecode/ControllerTest

Testing services


  describe('Angular service test', function() {
    var Base64Service;

    beforeEach(module("myApp"));
    beforeEach(inject(function(_Base64Service_) {
      Base64Service = _Base64Service_;
    }));

    it('should correctly encode a known message', function() {
      expect(Base64Service.encode('Hello from a simple controller!')).toBe('SGVsbG8lMjBmcm9tJTIwYSUyMHNpbXBsZSUyMGNvbnRyb2xsZXIlMjE=');
    });
  });
    

Source: samplecode/ServiceTest

End to end testing using Protractor

Tools for Angular

Angular and Friends

Google members have written a number of excellent tools which support AngularJS development

  • Karma: A test runner for Angular
  • Yeoman: A set of tools which provide code generation, package management and build support (tasks execution)
  • Protractor: An end to end testing framework for Angular which uses Selenium and WebDriverJS
  • Batarang: A chrome plugin which allows you to inspect Angular as it runs

How does Angular work?

TODO: Angular life cycle

Thinking about your application in terms of Angular

Breaking down your application

  • Separate your app into modules, models, controllers, directives, and services

The Angular Story

  • Created in 2008 and 2009 by Miško Hevery and Adam Abrons
  • Goal: to simplify building web applications using an online JSON storage service
  • Business scrapped and code released as open source project
  • At Google, Misko joined the Google Feedback project
  • At one point Misko claimed he could rewrite the product in two weeks using Angular
  • Google Feedback had 17,000 lines of code developed
  • Using Angular, Misko reduce the code from 17,000 lines to 1500 lines of code
  • This success lead to open sourcing the early Angular project
  • External developers start to take notice of Angular
  • Marc Jacobs from the Google DoubleClick team takes notice
  • He decides to compare Angular and Google's own GWT during one of his team's sprints
  • Angular proved easier to use

2013, the Angular team hosted an internal conference at Google offices

Initially, 50 people were expected to attend

197 attendees showed up

  • Angular gained traction inside and outside of Google
  • The Angular team compares Angular's emerging popularity relative to other popular frameworks

The team announced that by mid-2013 the popularity of Angular skyrocketed in comparison to other frameworks

A few Angular tidbits

Why is it called Angular?

Named after the HTML angled bracket

Why is the logo a shield with a letter "A" on it?

  • Logo chosen because the shield resembles the HTML5
  • Misko felt that the shield represents Angular shielding developers from the low-level differences among web browsers

How large is Angular?

You might think all of Angular's features result in a large and bloated framework

Actually, it weighs in at only...

  • 104K minified
  • 38K minified and gzipped
  • To put this in context, jQuery 2.1.1 is 84K minified

The Core Team

  • By early 2014 Angular had 14 core members
  • Google funds Angular development, the top ten contributors on Angular work at Google
  • However, many of the top contributors didn't start off as Google developers but were instead hired based on their contributions to Angular

The future of Angular

  • ES6-based Lazy loading dependency injection
  • Use of ES6 Object.observe
  • Polyfills for features which are not natively supported on all web browsers
  • Better support for mobile application development

The future of Angular (cont)

  • Built-in offline support - so apps work completely offine
  • AngularDart - Angular for Google's Dart programming language and platform
  • Angular 2.0 includes support for emerging browser technologies such as web components and the shadow DOM Angular does this in cooperation with the Polymer project
  • Improved debugging and instrumentation tools. Zone.js for improved stack traces, and diary.js for improved logging

Select Resources