Jan Amoyo

on software development and possibly other things

Changing Tab Focus Behavior Using Angular

No comments
The example below uses 2 Angular directives to change the focus behavior when pressing the tab key.

The first directive is used to assign a name to a 'focusable' element.
module.exports = function (_module) {
  _module.directive('focusName', function () {
    return {
      restrict: 'A',
      link: function ($scope, element, attributes) {
        $scope.focusRegistry = $scope.focusRegistry || {};
        $scope.focusRegistry[attributes.focusName] = element[0];
      }
    };
  });
};
Line 6 lazily creates a scope object called focusRegistry. This object will be used to store all 'focusable' elements within the scope.

Line 7 registers the element using the value of the attribute focusName (focus-name) as the key.

The second directive is used declare the which element will be focused when the tab key is pressed. It will also be responsible for handling the events triggered by pressing the tab key.
module.exports = function (_module) {
  _module.directive('nextFocus', function () {
    return {
      restrict: 'A',
      link: function ($scope, element, attributes) {
        element.bind('keydown keypress', function (event) {
          if (event.which === 9) { // Tab
            var focusElement = $scope.focusRegistry[attributes.nextFocus];
            if (focusElement) {
              if (!focusElement.hidden && !focusElement.disabled) {
                focusElement.focus();
                event.preventDefault();
                return;
              }
            }

            console.log('Unable to focus on target: ' + attributes.nextFocus);
          }
        });
      }
    };
  });
};
Line 7 captures the keydown or keypress event on the tab key (9).

Line 8 retrieves the focus element from the registry using the value of the attribute nextFocus (next-focus).

Lines 10 to 14, moves the focus to the element if it is possible.

If the focus cannot be moved to the specified element, the default behavior of tab will take effect.

Below is an example on how to use both directives:
<input name="1" focus-name="1" next-focus="2" value="next focus goes to 2" />
<input name="2" focus-name="2" next-focus="3" value="next focus goes to 3" />
<input name="3" focus-name="3" next-focus="1" value="next focus goes back to 1" />
<input name="4" value="will not be focused" />
A working example is available at Plunker.

It is important to note that this approach will only work on elements within the same scope.

No comments :

Post a Comment

Testing Angular Directives with Templates on Karma and Browserify

No comments
Directives are the cornerstone of every Angular application. And templates help keep their behavior separate from the presentation.

Karma works well with Angular and is an essential tool for running tests against a number of supported browsers.

Lastly, Browserify helps preserve sanity while maintaining JavaScript modules (similar to Node.js i.e. CommonJS spec).

Sadly, integrating all four concepts is not a straightforward process. Below is a rough guide on how to achieve this.

1. Install karma-browserifast and karma-ng-html2js-preprocessor
$ npm install karma-browserifast --save-dev
$ npm install karma-ng-html2js-preprocessor --save-dev
The package karma-browserifast enables Browserify support on Karma. While karma-ng-html2js-preprocessor converts HTML templates to JavaScript and loads them as an Angular module.

2. Configure Karma
module.exports = function (config) {
  config.set({
    files: [
      'node_modules/angular/angular.js',
      'src/**/*.html'
    ],

    browserify: {
      files: [
        'test/unit/**/*.js',
      ],
      debug: true
    },

    preprocessors: {
      '/**/*.browserify': ['browserify'],
      'src/**/*.html':    ['ng-html2js'],
    },

    ngHtml2JsPreprocessor: {
      moduleName: 'karma.templates'
    },

    frameworks: ['jasmine', 'browserify'],
    browsers:   ['Chrome'],
    reporters:  ['spec'],
    logLevel:   'info',
    autoWatch:  true,
    colors:     true,
  });
};
Lines 4 and 5 loads the HTML templates into Karma. Because they will be pre-processed by karma-ng-html2js-preprocessor, they will eventually get loaded as JavaScript files. Note that even if Angular will be included as part of the Browserify bundle, it is important to load it explicitly. Otherwise, the templates cannot be made available to Angular.

Line 10 tells karma-browserifast to include all unit tests as a Browserify bundle. This makes it possible for unit tests to execute JavaScript modules using the "require" keyword.

Line 21 tells karma-ng-html2js-preprocessor to load the JavaScript templates as an Angular module named "karma.templates". This will later be used in unit tests to allow testing of directives that use templates.

3. Write the unit tests
require('../src/app');
require('angular-mocks/angular-mocks');
describe('myDirective', function () {
  var scope, element;

  beforeEach(function () {
      angular.mock.module('karma.templates');
      angular.mock.module('myModule');
      angular.mock.inject(function ($rootScope, $compile) {
          scope   = $rootScope.$new();
          element = $compile('<div my-directive></div>')(scope);

          scope.$digest();
      });
  });

  it('does something', function () {
    expect(1).toBe(1);
  });
});
Lines 1 and 2 demonstrates the capability of a Karma-based unit test to load modules via "require".

Line 7 loads the generated JavaScript templates as an Angular module; without this, the directive will not compile because the template cannot be fetched.

No comments :

Post a Comment