Extending AngularJs Directive

50,852

Solution 1

Probably the simplest way to solve this is to create a directive on your app with the same name as the third party directive. Both directives will run and you can specify their run order using the priority property (higher priority runs first).

The two directives will share scope and you can access and modify the scope of the third party directive via your directive's link method.

Option 2: You can also access a third party directive's scope by simply putting your own arbitrarily named directive on the same element with it (assuming neither directive uses isolate scope). All non-isolate scope directives on an element will share scope.

Further Reading: https://github.com/angular/angular.js/wiki/Dev-Guide%3A-Understanding-Directives

Note: My previous answer was for modifying a third party service, not a directive.

Solution 2

TL;DR - gimme tha demo!


     Big Demo Button     
 


Use $provide's decorator() to, well, decorate the third party's directive.

In our case, we can extend the directive's scope like so:

app.config(function($provide) {
    $provide.decorator('paneDirective', function($delegate) {
        var directive = $delegate[0];
        angular.extend(directive.scope, {
            disabled:'@'
        });
        return $delegate;
    });
});

First, we request to decorate the pane directive by passing its name, concatenated with Directive as the first argument, then we retrieve it from the callback parameter (which is an array of directives matching that name).

Once we got it, we can obtain its scope object and extend it as needed. Notice that all of this has to be done in the config block.

Some notes

  • It has been suggested to simply add a directive with the same name, then set its priority level. Aside from being unsemantic (which's not even a word, I know…), it poses issues, e.g. what if the third-party directive's priority level changes?

  • JeetendraChauhan has claimed (I haven't tested it though) that this solution will not work in version 1.13.

Solution 3

While this is not the direct answer to your question you might want to know that the latest version (in master) of http://angular-ui.github.io/bootstrap/ added support for disabling tabs. This feature was added through: https://github.com/angular-ui/bootstrap/commit/2b78dd16abd7e09846fa484331b5c35ece6619a2

Solution 4

Another solution where you create a new directive that extends it without modifying the original directive

The solution is similar to the decorator solution:

Create a new directive and inject as dependency the directive you wish to extend

app.directive('extendedPane', function (paneDirective) {

  // to inject a directive as a service append "Directive" to the directive name
  // you will receive an array of directive configurations that match this 
  // directive (usually only one) ordered by priority

  var configExtension = {
     scope: {
       disabled: '@'
     }
  }

  return angular.merge({}, paneDirective[0], configExtension)
});

This way you can use the original directive and the extended version in the same app

Solution 5

Here is another solution for a different scenario of extending bindings to a directive that has the bindToController property.

Note: this is not an alternative to other solutions that were offered here. It solves only a specific case (not covered in other answers) where the original directive was set-up with bindToController.

Share:
50,852

Related videos on Youtube

Kyle Cureau
Author by

Kyle Cureau

Currently building crone.ai and hakeema. Reach me on Twitter

Updated on July 08, 2022

Comments

  • Kyle Cureau
    Kyle Cureau almost 2 years

    I'd like to make a minor modification to a 3rd party directive (specifically Angular UI Bootstrap). I simply want to add to the scope of the pane directive:

    angular.module('ui.bootstrap.tabs', [])
    .controller('TabsController', ['$scope', '$element', function($scope, $element) {
      // various methods
    }])
    .directive('tabs', function() {
      return {
        // etc...
      };
    })
    .directive('pane', ['$parse', function($parse) {
      return {
        require: '^tabs',
        restrict: 'EA',
        transclude: true,
        scope:{
          heading:'@',
          disabled:'@' // <- ADDED SCOPE PROPERTY HERE
        },
        link: function(scope, element, attrs, tabsCtrl) {
          // link function
        },
        templateUrl: 'template/tabs/pane.html',
        replace: true
      };
    }]);
    

    But I also want to keep Angular-Bootstrap up to date with Bower. As soon as I run bower update, I'll overwrite my changes.

    So how do I go about extending this directive separately from this bower component?

  • Kyle Cureau
    Kyle Cureau almost 11 years
    thanks @sh0ber, this is exactly what I needed. And your previous answer helped me as well, re: 3rd party services.
  • Kyle Cureau
    Kyle Cureau almost 11 years
    +1 for the heads up. good to know. i guess bower's angular-bootstrap and the bootstrap component of angular-ui are out of sync.
  • Ciel
    Ciel over 9 years
    Hey, this answer is really good, but I cannot find any documentation about the "priority" property for directives. All I found was a blurb that says "you can use it", but cannot find any actual examples of it.
  • Dan
    Dan over 9 years
    @Ciel The directive API info has apparently been moved to the $compile doc here
  • Faraz Hassan
    Faraz Hassan over 9 years
    This works perfectly. But what I'm interested is to add functionality to the original directive itself like add an event emitter to it. Like I have this directive(from a library) that provides feedback about a movie that is being played e.g the currentTime of the video. Now whenever the currentTime is updated, I want to emit and event, any idea how do i do that?
  • Eliran Malka
    Eliran Malka over 9 years
    i suggest you give @sh0ber's answer a go (create another directive just for emitting events).
  • Roy Milder
    Roy Milder about 9 years
    A quick note about this answer (which works great), the 'Directive' in 'paneDirective' does have a purpose ;-) It took me a while before figuring that out: stackoverflow.com/questions/19409017/…, see the accepted answer.
  • Eliran Malka
    Eliran Malka almost 9 years
    thanx, @JeetendraChauhan, i've updated the answer accordingly.
  • Jeetendra Chauhan
    Jeetendra Chauhan almost 9 years
    hi @EliranMalka check my plunker plnkr.co/edit/0mvQjHYjQCFS6joYJdwK hope this will help someone
  • Eliran Malka
    Eliran Malka almost 9 years
    great, i'll include a link to it in the answer, provided that you tell me what it proves :)
  • Chris Brown
    Chris Brown over 8 years
    The link to decorator() is broken (updated to docs.angularjs.org/api/auto/service/$provide#decorator)
  • uesports135
    uesports135 about 8 years
    How can this be done to create a new directive? For instance, I would like access to both the original directive on one page AND my custom directive on a different page?
  • Eliran Malka
    Eliran Malka about 8 years
    @uesports135 - see @sh0ber's answer above.
  • gilad905
    gilad905 over 7 years
    Here is another solution for a different scenario of extending bindings to a directive with the bindToController property.
  • Eliran Malka
    Eliran Malka over 7 years
    thank you @giladmayani! was this introduced in a later version? (i see v1.5.x is included in the demo). post this as another answer so others may benefit from an alternative solution :)
  • gilad905
    gilad905 over 7 years
    @EliranMalka yes, bindToController was introduced in v1.3. But note that this is not to be considered an alternative solution, this is only for a specific case where the original directive was set-up with the bindToController property. Good idea, I will post this as an answer :)
  • mathewguest
    mathewguest about 7 years
    This is great, just what I needed to extend an isolate scope directive with my own variables!! I did find that angular.extend does not deep-copy objects, so this replaces paneDirective's scope object with this one. An alternative is angular.merge which will keep the original scope from PaneDirective and add/merge variables defined here.
  • kidroca
    kidroca about 7 years
    yes, angular.merge should have been used, I'll update the example
  • Nebulosar
    Nebulosar over 3 years
    angualr.merge is DEPRECATED, see docs.angularjs.org/api/ng/function/angular.merge. You should use something like Lodash (recommended by AnguarJs) lodash.com/docs/4.17.15#merge