7

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.

5
  • what type of listeners your are talking?Can you please elaborate. Commented Mar 12, 2014 at 13:11
  • possible duplicate of Extending Angular Directive Commented Mar 12, 2014 at 13:17
  • I added some code with my existing directive Commented Mar 12, 2014 at 13:17
  • thx for you comments. @KhanhTO but, does this overwrite the existing input directive? Commented Mar 12, 2014 at 13:27
  • @23tux Have a look at my answer for the correct way of decorating the built-in input directive on an app-wide level, without the need to add behaviour through secondary directives. Commented Jul 23, 2014 at 8:50

3 Answers 3

18

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!

Sign up to request clarification or add additional context in comments.

4 Comments

This is the way to go but since Angular 1.3 you need to use pre link: var link = directive.link.pre;
I ran into an issue using angular-bootstrap 1.1.1, with datepickers and I applied a custom decorator using this code. Instead of saving/calling the old pre-link function, I think a better solution would be to use the old compile function. It fixed my issues anyway.
@JosephHelfert That wholly depends on how far you want to deviate from the original directive. If it's something simple, you can probably go with link/controller. If you need more fine-grain control, then using the old compile function works just dandy!
@KasperLewau Can you show an example of how to do this with the ng-submit directive?
3

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

Comments

1

You can easily extend any directive - including the input directive. Here is a quick example of adding a class to a directive.

1 Comment

You are not extending anything, you are adding a seperate directive. Effectively creating the code OP is trying to avoid.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.