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.

Answers:

Answer

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

Answer

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!

Answer

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

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.