Back home

AngularJs

Who am I?

JQuery

You?

Topic

New reality

Traditional web architecture

Application approach

Communication with backend

{
  "firstName": "John",
  "lastName": "Smith",
  "age": 25,
  "address": {
    "streetAddress": "21 2nd Street",
    "city": "New York",
    "state": "NY",
    "postalCode": 10021
  }
}

Changes

Challenges - testability

function PasswordCtrl() {
  // get references to DOM elements
  var msg = $('.ex1 span');
  var input = $('.ex1 input');
  var strength;

  this.grade = function() {
    msg.removeClass(strength);
    var pwd = input.val();
    password.text(pwd);
    if (pwd.length > 8) {
      strength = 'strong';
    }
    else {
      strength = 'weak';
    }
    msg
     .addClass(strength)
     .text(strength);
  }
}

Challenges - boilerplate

initialize: function () {
  this.allCheckbox = this.$('#toggle-all')[0];
  this.$input = this.$('#new-todo');
  this.$footer = this.$('#footer');
  this.$main = this.$('#main');

  this.listenTo(app.Todos, 'add', this.addOne);
  this.listenTo(app.Todos, 'reset', this.addAll);
  this.listenTo(app.Todos, 'change:completed', this.filterOne);
  this.listenTo(app.Todos, 'filter', this.filterAll);
  this.listenTo(app.Todos, 'all', this.render);

  app.Todos.fetch();
}

AngularJs

MV* architecture

View

<ul>
  <li ng-repeat="todo in todos">
    <span></span>
  </li>
</ul>
<form ng-submit="addTodo()">
  <input ng-model="todoText" />
  <input type="submit" value="add" />
</form>

Filters

<ul>
  <li ng-repeat="project in projects | filter:search | orderBy:'name'">
    <a href=""></a>: 
  </li>
</ul>

Controller

$scope

Presenation

  1. Angular ‘Hello world’
  2. Templating with ng-repeat

presentation

Simple js objects

// Backbone
app.Todo = Backbone.Model.extend({
  defaults: {
    title: '',
    completed: false
  }
});

// Ember
Todos.Todo = DS.Model.extend({
  title: DS.attr('string'),
  isCompleted: DS.attr('boolean')
});

// Angular
todos.push({
  title: $scope.newTodo,
  completed: false
});

Two ways binding

Forms support

<!-- Html updated by angular -->
<form name="exampleForm" class="ng-pristine ng-invalid ng-invalid-required">

    Required field:
  <input ng-model="required" required="" class="ng-pristine ng-invalid ng-invalid-required"></label> <br>

    Email field: 
  <input ng-model="email" type="email" class="ng-pristine ng-valid ng-valid-email"></label> <br>

  <button ng-disabled="!exampleForm.$valid" disabled="disabled">Submit</button>
</form>

Presentation

  1. Example of validating code

presentation

Dependecy injection

function HelloCtrl($scope, $window, $log) {
  $scope.message = 'Display me in view';
  $window.alert('Use window via $window service - that improves testability');
  $log.log('We can even test console log calls - thats to $log wrapper');
}

Services

/**
 * Services that persists and retrieves ToDos from localStorage
 */
todomvc.factory('todoStorage', function () {
  var STORAGE_ID = 'todos-angularjs';

  return {
    get: function () {
      return JSON.parse(localStorage.getItem(STORAGE_ID) || '[]');
    },

    put: function (todos) {
      localStorage.setItem(STORAGE_ID, JSON.stringify(todos));
    }
  };
});

Routing - $routeProvider

angular.module('phonecat', [])
  .config(['$routeProvider', function($routeProvider, $locationProvider) {
    // $locationProvider.html5Mode(true);
    $routeProvider
      .when('/phones', {
        templateUrl: 'partials/phone-list.html',
        controller: PhoneListCtrl
      })
      .when('/phones/:phoneId', {
        templateUrl: 'partials/phone-detail.html',
        controller: PhoneDetailCtrl
      })
      .otherwise({
        redirectTo: '/phones'
      });
  }]);

REST - $resource

myApp.factory('ProductService', function($resource) {
  var ProductService = {};

  var ProductResource = $resource('/product/:productId');

  ProductService.getItem = function (index) {
    return ProductResource.get({productId: index});
  }

  ProductService.addItem = function (item) {
    ProductResource.save({}, item));
  }

  ProductService.removeItem = function (item) {
    // Calls method delete on ProductResource - that way, just to make ie8 happy
    return ProductResource['delete']({productId: item.id});
  }

  ProductService.getList = function () {
    return ProductResource.query();
  }

  return ProductService;
});

function ProductCtrl($scope, ProductService) {
  // Take products form ProductService and put it on $scope
}

Directives

<ANY class="ng-show: {expression};"> <ANY ng-show="{expression}">
<ANY class="ng-hide: {expression};"> <ANY ng-hide="{expression}">

<ng-view> <any ng-view>

<ANY ng-class="{expression}"> <ANY class="ng-class: {expression};">

<ANY ng-switch="expression">
  <ANY ng-switch-when="matchValue1">...</ANY>
  <ANY ng-switch-when="matchValue2">...</ANY>
  ...
  <ANY ng-switch-default>...</ANY>
</ANY>

Yeoman

Presentation

  1. Open a server and page in a browser
  2. Change view and show it in browser

Karma (Testacular)

Habits - writing callbacks

Habits - imperative binding

Habits - changing DOM in controller

Gotchas - writing directives

angular.module('blink', [])
  .directive('blink', function() {
    return {
      restrict: 'E',
      link: function(scope, elm, attr) {
        setInterval(function() {
          elm.toggle();
        }, parseInt(attr.interval, 10) || 1000);
      }
    };
  });

Gotchas - ng-model in ng-repeat

// Gotcha!
<ul>
  <li ng-repeat="item in list">
    <input ng-model="item" />
  </li>
</ul>

// Work as expected
<ul>
  <li ng-repeat="item in list">
    <input ng-model="item.name" />
  </li>
</ul>

Gotchas - code minification

syngularApp.controller('ProductCtrl', function($scope, ProductApi) {
  // easy but minification unfriendly
});

syngularApp.controller('ProductCtrl', ['$scope', 'ProductApi', function($scope, ProductApi) {
  // minification resistant
}]);

Gotchas - $resource

Gotchas - filters working only on arrays

Gotchas - e2e testing

Gotchas - updating data from outside angula

Gotchas - $ in service names

Materials

Summary

Contact

Credits