AngularJS Intro

Data-binding, dependency injection, directives.

Created by Christian Köberl

Intro

AngularJS in 30 Seconds

AngularJS is more

  • MVC with fully two-way data-binding
  • Declarative form validation
  • Route management (enables deep linking)
  • Dependency Injection - yeah Spring for JS
  • Directives: reusable components, enhancing HTML, integrate 3rd party libs

MVC and Data-Binding

Model View Controller

  • Model
    a plain JavaScript object (POJSO)
  • View
    a HTML template using directives and expressions enclosed in {{ }}
  • Controller
    a JavaScript object that gets the $scope and services injected. Creates and changes the model.

Model View Controller in Code

Controllers and Interaction

Components

Components

  • New in Angular 1.5
  • Simpler form of Angular directive
  • More Angular 2.x style
<my-tabs>
  <my-tab title="This is tab">
    <p>A tab content</p>
  </my-tab>
  <my-tab title="Another tab">
    <p>Some more tabby stuff</p>
  </my-tab>
</my-tabs>

Simple Components

Complex Components

Anatomy of a Component

module.component('my-comp', {
  template: '<div>{{ param1 }}</div>',
  templateUrl: 'template.html',
  require: { // controllers of other components
    directParent: '^parentComponent',
    anyParent: '^^parentComponent'
  },
  bindings: {
    param1: '<', // one-way binding
    param2: '=', // two-way binding
    onEvent: '&' // callbacks/events
  },
  controller: function ComponentController() {
    this.onEvent(this.param1);
    this.directParent.lala(this.param2);
  }
});
              

Components vs. Directives

  • Components should be used as the default (>1.5)
  • Directives only for special DOM manipulation

Component - Exercise

Built-in Directives

Invoking/Using Directives

  • Attributes:
    • Standard: <span ng-bind="[expression]">
    • HTML5 data-attributes: <input data-ng-model="[expression]">
  • Tag: <ng-form>...</ng-form>
  • CSS class: <span class="ng-click: [expression]">
  • Comment: <!-- directive:my-directive -->
  • Hint: The JavaScript name of the directive is camelCase, e.g. ngModel

ng-bind And Expressions

  • This <span>{{ expression }}</span>
  • Is the same as <span ng-bind="expression"></span>
  • Expressions:
    • 27 + 15 ⇒ 42
    • person.name ⇒ get person object form $scope and use name attribute
    • myFunction() ⇒ get result of call to myFunction on $scope

ng-click And Other Events

  • Expressions in action directives should be functions:
    <button ng-click="doSomething()">Click Me</button>
  • Other events: ng-dblclick, ng-submit, ng-mouse* (mouseup, mousemove, ...)
  • ng-change is called on each change of the value
    <input type="checkbox" ng-model="confirmed" ng-change="change()" />

Conditionals - ng-if and ng-show

  • ng-show/ng-hide - set display to none
  • ng-if - remove element from DOM
<div class="error" ng-if="error">{{error}}<div>

Loops And ng-repeat

Selects and ng-options

Using Directives - Excercise

  • Extend component "my-panel" from before
  • Panel opening/closing when clicking on title
  • Parameter "open" (true/false) that sets initial state

Filters

Using filters

Creating filters

The 'filter' Filter

Services and Dependency Injection

Sharing State Between Controllers

Using Other Services ($http)

Value, Factory, Service

  • value - a simple value object, no dependency injection
    module.value('clientId', clientId)
  • factory - a factory method creating objects
    module.factory('client', function clientFactory(clientId) {
      var client = {};
      return client; 
    })
  • service - creating objects via constructor
    module.service('client', function Client(clientId) {
      this.clientId = clientId;
    })

Dependency Injection - Excercise

  • Create a module with a value backendUrl "https://jsontest-derkoe.appspot.com/?service=date"
  • Create a service calling the backendUrl via GET (using $http)
  • Create a component <my-date> displaying the current date

Promises

Promises for backend communication

Promises for modal handling

Promises - Excercise

  • Button to open modal dialog
  • In dialog button to open the same dialog, and so on ...
  • After closing all dialogs count the total number
  • Use modal example as base

Forms and Validation

Forms

Forms & Validation

Complex Validation

Routes and Views

Routes? Views? What?

  • Navigable: back-button, bookmarks
  • Structure application in parts
  • Lazy load templates from server

Routing - Options

  • Angular ngRoute - simple standard router for AngularJS
  • Angular UI Router - flexible router add-on module, state oriented
  • Angular Component Router (>1.5) - "new" router from Angular based on components

Routes and Redirects

Routes and Controllers

Routes and Resolving

Animations

Animations - ngAnimate

  • Add "ngAnimate" to your module angular.module('my-app', ['ngAnimate'])
  • Add CSS fo animation:
    • .ng-hide - for ng-show/ng-hide
    • .ng-enter .ng-leave - for ng-if/ng-switch/ng-view
    • .ng-enter .ng-leave .ng-move - for ng-repeat
    • "-active" is added after full enter/leave

Animations - Example

Testing

Unit Testing services, filters, etc.

  • Test-Runner: Karma
  • angular-mocks.js to mock services
  • Jasmine for test specs

Testing a Weather Widget

Testing Controllers

  describe('Controller: WeatherCtrl', function() {

    it('calls weatherService to get weather', function() {
      inject(function($rootScope, $controller) {
        var weather = { temp : { current : 10 } },
            weatherService = {
              getWeather : function() {
                return weather;
              }
            },
            scope = $rootScope.$new;

        $controller('WeatherCtrl', {
          $scope : scope,
          weatherService : weatherService
        });
        expect(scope.weather).toBe(weather);
      });
    });
  });

Testing Services

describe('Service: weatherService', function() {

    var url = 'http://api.openweathermap.org/data/2.5/weather?q=Salzburg,at&units=metric&callback=JSON_CALLBACK';
    var $httpBackend, weatherService;

    beforeEach(inject(function($injector) {
      $httpBackend = $injector.get('$httpBackend');
      weatherService = $injector.get('weatherService');
    }));

    it('maps the correct current temp', function() {
      $httpBackend.whenJSONP(url).respond({
        main : {
          temp : 10
        }
      });
      $httpBackend.expectJSONP(url);
      var weather = weatherService.getWeather();
      $httpBackend.flush();

      expect(weather.temp.current).toBe(10);
    });

  });

Testing Filters

  describe('Filter: temp', function() {
    var tempFilter;
    beforeEach(inject(function($filter) {
      tempFilter = $filter('temp');
    }));

    it('adds °C', function() {
      expect(tempFilter(3)).toBe('3.0°C');
    });

    it('handles precision correctly', function() {
      expect(tempFilter(3, 10)).toBe('3.0000000000°C');
    });

    it('handles non-numbers', function() {
      expect(tempFilter('a')).toBe('°C');
      expect(tempFilter({})).toBe('°C');
    });
  });

Best Practices

Views ...

  • may read from the $scope: {{ data.name }}
  • may write on objects on the $scope
    ng-model="data.name"
  • may call methods on $scope: ng-click="save()"

  • should never write directly to $scope
    ng-model="name"
  • should not reference DOM

Controllers ...

  • should delegate work to services

  • should not reference DOM
  • should not reference other controllers

Services ...

  • should handle server calls ($http)
  • should not reference DOM, controllers and views

 

Directives ...

  • may reference the DOM
  • may reference services
  • should not directly handle server calls
  • should not reference controllers and views

Angular Style Guide

Angular Style Guide by John Papa

News

Angular 2.0

  • Complete rewrite of AngularJS 1.x
  • No two-way binding → a lot faster
  • Lazy-loading of code and templates
  • Typically coded in TypeScript/AtScript
  • Angular 1 and 2 integration

Angular 2.0 in Typescript

@Component({
  selector: 'todos',
  templateUrl: 'todos.html'
})
class Todos {
  todos = ["Things to do", "Another thing"];
  addTodo(todo) {
    this.todos.push(todo);
  }
}
<form (submit)="addTodo(todotext.value)">
  <input placeholder="Enter your todo" #todotext>
  <button type="submit">Add Todo</button>
</form>
<ul><li *ngFor="#todo of todos">{{ todo }}</li></ul>

Angular 2.0 Example

THX

Questions?

Sources