Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJS: extend input directive

I was wondering, if it is possible, to extend Angular's input directive? I want to attach some listeners to all input fields on a page. I think you can decorate existing modules with $provide.decorate, but I have no idea how to do this with a directive (and more precisely the input directive).

So, can anyone push me in the right direction? Some examples?

EDIT

Here is my directive that I have so far:

angular.module('onFocusBlur').
directive('onFocusBlur', ["$rootScope", function($rootScope) {
  return {
    restrict: "A",
    link: function(scope, elem, attrs) {
        elem.bind('focus',function() {
          scope.$apply(function() {
            $rootScope[attrs.onFocusBlur] = true;
          });
        });
        elem.bind('blur',function() {
          scope.$apply(function() {
            $rootScope[attrs.onFocusBlur] = false;
          });
        });
    }
  };
}]);

In my view, I can add this to an input field like this:

<input type="email" ng-model="email" on-focus-blur="repositionNavBar">

The downside is, that for every input field, I have to attach this listener manually in my code. Therefore, it would be useful, to alter the existing input directive, to include this listeners.

like image 392
23tux Avatar asked Mar 12 '14 13:03

23tux


2 Answers

Here's a short gist of how you could decorate the built in Angular input directive, with $provide.decorator as you yourself suggested.

app.config(function ($provide) {

  // Suffix is "Directive" for decorating directives.
  // $delegate refers to the original directive definition. 
  $provide.decorator('inputDirective', function ($delegate) {

    // When decorating a directive, $delegate is an array containing 
    // all of the directives of the same name. 
    // We're interested in the first in this case.
    var directive = $delegate[0];

    // Store a reference to the original directive linking function. 
    var link = directive.link;

    // Hook into the compile phase of the directive.
    directive.compile = function () {

      // The function returned by compile is your new link function.
      // Or 'postLink', to be exact. 
      return function (scope, el, attrs) {

        // Run the original link function. 
        link.apply(this, arguments);

        // Your new listener function
        function listener (e) {
          console.log(e);
        }

        // Set up your listener(s).
        el.bind('blur', listener);
        el.bind('focus', listener);
      };
    };

    // Return the decorated original ($delegate).
    return $delegate;
  });

});

Benefits of this approach, as I see it:

  1. It's more DRY than adding another directive to augment behaviour.
  2. It is an actual extension of third-party behaviour.
  3. For the most part, you don't need to know about the internal implementation of the directive (or service, for that matter) that you are decorating.

Here's a jsBin: http://jsbin.com/yafecinu/2/edit

I'd recommend you take a look at this article, that goes in-depth on decorating directives. The article covers extending not just linking phases, but controllers and pre-linking DOM manipulation aswell. A very good read!

like image 196
Kasper Lewau Avatar answered Oct 19 '22 23:10

Kasper Lewau


See answer to this question. They talk about a couple different options to extend a directive from using $provide to making a directive with the same name.

Also, this link explains a few techniques (under "Extending Directives"): https://github.com/angular/angular.js/wiki/Understanding-Directives

like image 40
Patrick Avatar answered Oct 19 '22 23:10

Patrick