Apr 21, 2016

Unit test for Directive with template - AngularJs

As we are adding unit test for directive, its implementation can vary from simple to very complex.
The complex implementation require more knowledge about how to write deeper test in order to get better tested coverage.

To start testing you'll need to start with $compile of DOM element, link function, to angular's controller or anonymous function controller, to template...  

Here are the sources to learn about writing directive's unit test.

  • very interactive and straight forward Directive Unit Testing Guide
  • covered topics
    • "setup karma.config.js"
    • "how to start testing directive", 
    • "working with template" (ng-html2js processor)
  • has more thorough coverage of possible solutions required for Directive unit testing

2) Testing AngularJS directive controllers with Jasmine and Karma (1st tutorial) (2nd tutorial
  • Covered
    • setup sample karma.config.js
    • unit test of anonymous function as controller
    • make use of ng-html2js process
  • The best part of this tutorial is in "2nd tutorial" link, it provided sample of Directive using anonymous function as its controller
    • Testing with anonymous function as controller
      
      angular.directive('myDirective', function() {
        return {
          restrict: 'a',
          controller: function() {} // <--- anonymous controller
        };
      });
      

    • Alternative beside anonymous function as controller
      
      angular
      .controller('myCtrl', function() {})
      .directive('myDirective', function() {
        return {
          restrict: 'a',
          controller: 'myCtrl'
        };
      });
      
3) Testing AngularJS Directives

  • covered simple test of directive on
    • utilise jQlite 
    • unit test of directive's controller from one of the defined angular controller


Possible issue to overcome:

  1. To test a custom validation angular directive
    • unit test of form changes detection

Apr 8, 2016

AngularJs 2 Component: manually bind event listener to element

Sample code of binding element to an onchange event.

import {Directive, ElementRef, Output, EventEmitter, Renderer} from 'angular2/core';

@Directive({
  selector: 'my-comp'
})
export class myComp {
  constructor(el: ElementRef, renderer: Renderer) {
    renderer.listen(el.nativeElement, 'change', event => console.log(event));
  }
}

Stuff you need:
  • directive decorator
  • ElementRef.nativeElement
  • Renderer.listen()

Directive decorator
For my case here, I use directive decorator, because I do not need any template update.
If you are familiar with AngularJs 1.x, this is same as the directive with empty template like short example below:
eg.

angular.directive('myApp', function() {
  return {
    restrict: 'A',
    template: '',
    link: function() { ... }
  };
);


And of course you can choose to import Page, and use it with template like:

import {Page, ElementRef, Output, EventEmitter, Renderer} from 'angular2/core';

@Page({
  selector: 'my-app',
  template: 'Your template goes here'
})

// more code...


ElementRef.nativeElement
The similarity of ElementRef.nativeElement in AngularJs 2 with element parameter in Link() in AngularJs 1.x directive.


angular.directive('myApp', function() {
  return {
    restrict: 'A',
    template: '',
    link: function(scope, element, attributes) {
      // `ElementRef.nativeElement` is almost similar with
      // the `element` parameter in this link() function
    }
  };
);

Renderer.listen() - Angular2 beta 13
Add custom listener to keep track of an event fired, this helps at emitting or initiating additional action.
It works just like how AngularJs 1.x bind an event and initiate its parent's controller function in the scope.
Example AngularJs 1.x below:

angular
.controller('myCtrl', function() {
  scope.onClick = function(event) {
    console.log(event);
  };
})
.directive('myApp', function() {
  return {
    restrict: 'A',
    link: function(scope, element, attributes) {
      // it bind event to element as in Angular 1.x
      $(element).bind('click', function(event) {
        scope.onClick(event);
      });
    }
  };
);


In AngularJs 2, more complete version of the sample code from top which indicate the use of Renderer.listen()

import {
  Directive, 
  ElementRef, 
  Output, 
  EventEmitter, 
  Renderer, 
  Component,
  bootstrap
} from 'angular2/core';

@Directive({
  selector: '[my-comp]'
})
class myComp {
  @Output() click = new EventEmitter();
  constructor(el: ElementRef, renderer: Renderer) { 
    renderer.listen(el.nativeElement, 'click', (event) => {
      this.onClick(event);
    });
  }

  onClick(event){
    this.click.emit({
      value: event
    });
  }
}

@Component({
  template: '<button (click)="showclick($event)" my-comp></button>',
  directives: [myComp]
})
class myApp {
  showClick($event) {
   console.log('changes::', $event);
  }
}
bootstrap(myApp);



Reference/learning source: Dynamically add event listener in Angular 2