AngularJS – A Brief Introduction



AngularJS – A Brief Introduction

2 0


angular-js-presentation


On Github mb-dev / angular-js-presentation

AngularJS

A Brief Introduction

by Moshe Bergman

What we'll cover

  • Setup the app
  • Controllers, Scopes, View binding
  • Services
  • Directives & Filters
  • Testing

JQuery

  • Simple API and works great
  • Tightly couples AJAX calls, DOM Manipulation and event listening in the app code
  • Does not provide structure recommendation
  • Does not provide two way binding

Backbone.js / Handlebars

  • Provide some structure and binding
  • Re-renders the whole DOM on every change
  • Does not provide two way binding

It's

FAST

(Except for large amount of bindings)

It brings

QUALITY

(Both in terms of code structure and robustness)

It's

FREE

AngularJS

puts a heavy emphasis on

Separation of Concerns

HTML

STRUCTURE

JS

BEHAVIOR

CSS

PRESENTATION

The Eval JavaScript Empire

HTML

INTERFACE

JS

LOGIC

CSS

PRESENTATION

The New Angular Republic

AngularJS

returns things to where they belong

in their natural forms

  • HTML? Build UI Declaratively!
  • CSS? Animations!
  • JavaScript? Use it the plain old way!

Intriguingly,

the uprising of AngularJS in 2013

is coincident with...

  • The Extensible Web Movement
  • Increasing Visibility in Web Components
  • The Release of Polymer

What we'll cover

  • Setup the app
  • Controllers, Scopes, View binding
  • Services
  • Directives & Filters
  • Testing

The App

To get started:

<div ng-app="app">
						  <div ng-view=""></div>
						</div>

  • Setup the app
  • Define container for view templates

Module Definition

angular.module('app', ['app.services', 'app.directives', 'app.controllers', 
  'ngRoute']).config(function($routeProvider, $locationProvider) {
    $routeProvider.when("/accounts/?:userIdOrScreenName?", {
      templateUrl: "/accounts/partials/main_page",
      controller: 'UserDetailsIndexCtrl'
    });
    $routeProvider.otherwise({
      redirectTo: "/accounts/"
    });
    $('.navbar').removeClass('navbar-inverse');
    return $locationProvider.html5Mode(true);
  });

  • Modules encapsulate the app code into a namespace
  • Dependency inject dependencies, by variable name.
  • No Global Variables
  • Routing logic, controller per route, Templates
  • Templates are not limited by mustache :))

Controllers

angular.module('app.controllers', ['app.services']).controller('UserDetailsIndexCtrl', 
  function($scope, userQuery, $location, $routeParams) {
    var onUserDataArrived = function(result) {
      $scope.user = {id: result.data.id, name: result.data.profile.name, screen_name: result.data.profile.screen_name, image: result.data.profile.profile_image_urls.normal.http_url};
    }
    if ($routeParams.userIdOrScreenName) {
      $scope.userIdOrAccount = $routeParams.userIdOrScreenName;
      userQuery.queryUser($routeParams.userIdOrScreenName).then(onUserDataArrived);
    }
    $scope.onSelectUser = function(e) {
      $location.path('/accounts/' + $scope.userIdOrAccount);
    };
}
        
  • Controllers only prepare the model for the view and respond to events
  • Controllers do NOT manipulate the DOM
  • The $scope variable is where the view consumes variables from
  • Event handlers
  • Controllers are re-created on every request

Scopes are hierarchical

  • There is only one $rootScope and every scope inherits from it.
  • Some AngularJS constructs create a new scope, that inherit from the controller's $scope.
  • Scope is a regular POJO. Your models are regular Javascript objects
  • All AngularJS specific variables and functions start with $ to not get serialized

Views & Binding

<form ng-submit="onSelectUser()">
  <input type="text" ng-model="userIdOrAccount" placeholder="User ID or @SN">
</form>
<span class="name">{{user.name}}</span>
        
  • Custom attribute names
  • Custom element names
  • Data binding ({{}} and ng-model)
  • Some attributes/elements also change HTML, such as: ng-repeat

Services

angular.module('app.services', []).factory('userQuery', function($http, $q) {
  return {
    queryUser: function(userIdOrScreenName) {
      return $http.get('/accounts/user_stats/' + userIdOrScreenName.toString());
    }
  };
});
        
  • Services are where the http get/post/put/delete calls go
  • Services are created using factory, since they have only one instance
  • Promises $q - are Angular library like jQuery Deferred
  • $http.get is almost equivalent to $.get

Advanced Topics

To try after mastering the basics

Directives

Directives can be confusing at first. Many ways to construct them.

  • They encapsulate an element with related logic
  • They encauslate 3rd party components

directive('ngConfirmClick', function() {
  return {
    link: function (scope, element, attr) {
      var msg = attr.ngConfirmClick || "Are you sure?";
      var clickAction = attr.confirmedClick;
      element.bind('click',function (event) {
          if ( window.confirm(msg) ) {
              scope.$eval(clickAction)
          }
      });
    }
  };
})
        

Filters

Used for transforming data (pipe).

  • They encapsulate an element with related logic
  • They encauslate 3rd party components

.filter('multiple', function () {
   return function (input) {
       return '' + input + 'X';
   };
})
        
<tr ng-repeat="friend in friends | filter:searchText">
</tr>
<span>{{amount | currency}}</span>
        

Testing

describe('app.controllers::UserDetailsIndexCtrl', function() {
  var $controller, $location, $scope, userQuery, $q, deferred;
  beforeEach(module('app.controllers'));
  beforeEach(inject(function($rootScope, _$location_, _$controller_, _$q_) {
    $scope = $rootScope.$new();
    $location = _$location_;
    return userQuery = {
      queryUser: function() {
        deferred = $q.defer();
        return deferred.promise;
      }
    };
  }));
      

Testing

it('should query user information when submitted with /userid=1', function() {
    spyOn(userQuery, 'queryUser').andCallThrough();
    $controller('UserDetailsIndexCtrl', {
      $scope: $scope,
      $location: $location,
      $routeParams: {
        userIdOrScreenName: 3
      },
      userQuery: userQuery
    });
    expect(userQuery.queryUser).toHaveBeenCalled();
  });
});
        

Testing

  • Testing is best done using Jasmine and run by NodeJS and Karma.
  • Karma live monitors your files and runs tests!

Thank You