AngularJS

Agenda

HTML and dynamic content

1
2
3
4
5
6
<label for="name">Your name</label>
<input type="text" name="name" id="name" />

<p>
  Hello <span class="name">{{name}}</span>!
</p>
Hello {{name}}!

jQuery saves the day

1
2
3
4
5
$(function() {
  $("input[name=name]").keydown(function() {
    $('.name').html($(this).val());
  });
});
Hello {{name}}!

You could implement a web app this way...

seems legit

Enter AngularJS

1
2
3
4
5
6
7
<div ng-app>
  <label for="name">Your name</label>
  <input type="text" ng-model="name"
    id="name"/>

  <p>Hello {{name}}!</p>
</div>
Hello {{name}}!

HTML Enhanced for web apps

Another JS MVC framework!?

YAWN

AngularJS is much more than just MVC

Customize HTML

1
2
3
4
5
6
7
8
<tabs>
  <pane title="First Tab">
    First tab content
  </pane>
  <pane title="Second Tab">
    Second tab content
  </pane>
</tabs>

2-Way Data Binding

Dependency Injection

1
2
3
4
5
function FooController($scope, $window) {
  $scope.greet = function() {
    $window.alert("Hello " + $scope.name);
  };
}
1
2
3
4
<div ng-controller="FooController">
  <input type="text" ng-model="name" />
  <a ng-click="greet()">Greet</a>
</div>
Greet me

Testability

1
2
3
4
5
6
7
8
9
10
11
12
13
14
describe("FooController", function() {
  it("greets the user", function() {
    var scope = { name: "Bob" };
    var win = {
      alert: jasmine.createSpy()
    };
    var ctrl = new FooController(scope, win);

    scope.greet();

    expect(win.alert).toHaveBeenCalledWith(
      "Hello Bob");
  });
});

Plain Old JS Objects

Framework for your app

The framework

Library for common use cases

The library

MVC in Angular

The View

HTML/DOM based

Customizable using directives and filters

The Model

$scope

1
2
3
4
5
6
7
8
9
10
function TodosController($scope) {
  $scope.todos = ["Learn Angular"];

  $scope.add = function() {
    if($scope.newTodo) {
      $scope.todos.push($scope.newTodo);
      $scope.newTodo = '';
    }
  };
}
1
2
3
4
5
6
7
<div ng-controller="TodosController">
  <input type="text" ng-model="newTodo" />
  <a href ng-click="add()">Add</a>
  <ul>
    <li ng-repeat="todo in todos">{{todo}}</li>
  </ul>
</div>
Add
Todos:
* {{todo}}

Scopes are hierarchical

1
2
3
4
5
6
7
8
9
function OuterCtrl($scope) {
  $scope.foo = "outer foo";
  $scope.bar = "outer bar";
}

function InnerCtrl($scope) {
  $scope.bar = "inner bar";
  $scope.baz = $scope.foo + " o_O";
}
1
2
3
4
5
6
7
8
<div ng-controller="OuterCtrl">
  Outer Controller:<br>
  foo: {{foo}} | bar: {{bar}} | baz: {{baz}}
  <div ng-controller="InnerCtrl">
    Inner Controller:<br>
    foo: {{foo}} | bar: {{bar}} | baz: {{baz}}
  </div>
</div>
Outer Controller:
foo: {{foo}} | bar: {{bar}} | baz: {{baz}}
Inner Controller:
foo: {{foo}} | bar: {{bar}} | baz: {{baz}}

The controller

Multiple controllers per page

Directives can have controllers as well

1
2
3
4
5
6
7
8
9
10
11
12
function PeopleCtrl($scope) {
  $scope.people = [{name: "adam"},
                   {name: "bob"}];
  // add / remove / reorder
}

function PersonCtrl($scope) {
  $scope.makeUpper = function() {
    $scope.person.name =
      $scope.person.name.toUpperCase();
  }
}
1
2
3
4
5
6
7
8
9
<div ng-controller="PeopleCtrl">
  {{people | json}}

  <div ng-repeat="person in people"
       ng-controller="PersonCtrl">
    {{person}}
    <a href ng-click="makeUpper()">Upcase!</a>
  </div>
</div>
{{people | json}}
{{person}} Upcase!

Some examples

Directive

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var datepicker = function() {
  return {
    require: 'ngModel',
    link: function(scope, el, attrs, ctrl) {
      jQuery(el).datepicker({
        onSelect: function(dateText) {
          scope.$apply(function() {
            ctrl.$setViewValue(dateText);
          });
        }
      });

      ctrl.render = function() {
        jQuery(el).datepicker('setDate',
          ctrl.$viewValue);
      };
    }
  };
};
1
2
angular.module('samples').
  directive('datepicker', datepicker);

Date: {{date}}

Filter

1
2
3
4
5
6
7
var sum = function() {
  return function(values) {
    return values.reduce(function(a, b) {
      return a + b; });
  };
};
angular.module('samples').filter('sum', sum);
1
2
3
4
<div ng-init="numbers = [1, 2, 3]">
  numbers: {{numbers | json}}
  sum: {{numbers | sum }}
</div>
numbers: {{numbers | json}} sum: {{numbers | sum }}

Service

1
2
3
4
5
6
7
8
var notifyUser = function($window) {
  return function(msg) {
    $window.alert(msg);
  };
};
angular.module('samples')
  .factory('notifyUser',
    ['$window', notifyUser]);

Module

1
2
3
4
5
  angular.module('myModule', ['myServices',
    'myDirectives']).
  directive('foo', foo).
  value('bar', 'the bar').
  factory('myAlert', ['baz', myAlert]);

Forms & validation

1
2
3
4
5
6
7
8
<form name="myForm">
  Foo*: <input type="text" required
    ng-model="foo" /><br>
  Bar*: <input type="text" required
    ng-model="bar" /><br>
  <button ng-disabled="myForm.$pristine
      || myForm.$invalid">Save</button>
</form>
Foo*:
Bar*:

Testing

Unit

Extremely easy

Mocks for non-testable stuff like setTimeout/console.log/XHR

E2E

Possible to mock HTTP

More reliable than Selenium

Much harder to use than Selenium

What sucks in Angular

A year ago:

All my complaints were addressed

Plus stuff I didn't know I needed

Big improvements => lots of breaking changes

1.0 should be stable though

Problems now:

Highlights

Easy setup

Unmatched testability

Cleaner code with less callbacks an boilerplate

Build your own HTML

you shall ask
questions now

Thank you