Extension de la directive angulaire

114

Je souhaite apporter une modification mineure à une directive tierce (en particulier Angular UI Bootstrap ). Je veux simplement ajouter au champ d'application de la panedirective:

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
  };
}]);

Mais je veux aussi garder Angular-Bootstrap à jour avec Bower. Dès que je cours bower update, j'écrase mes modifications.

Alors, comment étendre cette directive séparément de ce composant du bower?

Kyle
la source
2
Le moyen le plus propre serait d'utiliser $provide.decorator(), voir ma réponse ci-dessous.
Eliran Malka

Réponses:

96

Le moyen le plus simple de résoudre ce problème est probablement de créer une directive sur votre application avec le même nom que la directive tierce. Les deux directives s'exécuteront et vous pouvez spécifier leur ordre d'exécution à l'aide de la prioritypropriété (la priorité la plus élevée s'exécute en premier).

Les deux directives partageront la portée et vous pourrez accéder et modifier la portée de la directive tierce via la linkméthode de votre directive .

Option 2: Vous pouvez également accéder à la portée d'une directive tierce en plaçant simplement votre propre directive nommée arbitrairement sur le même élément avec elle (en supposant qu'aucune directive n'utilise la portée isolate). Toutes les directives de portée non isolées sur un élément partageront la portée.

Lectures complémentaires: https://github.com/angular/angular.js/wiki/Dev-Guide%3A-Understanding-Directives

Remarque: Ma réponse précédente concernait la modification d'un service tiers et non d'une directive.

Dan
la source
3
merci @ sh0ber, c'est exactement ce dont j'avais besoin. Et votre réponse précédente m'a également aidé, concernant les services tiers.
Kyle
Hé, cette réponse est vraiment bonne, mais je ne trouve aucune documentation sur la propriété "priority" pour les directives. Tout ce que j'ai trouvé, c'est un texte de présentation qui dit "vous pouvez l'utiliser", mais je ne trouve pas d'exemples concrets.
Ciel
2
@Ciel Les informations sur l'API de la directive ont apparemment été déplacées dans le $compiledocument ici
Dan
60

TL; DR - donne-moi la démo!


     Big Demo Button     
 


Utilisez $provide« s decorator(), eh bien, décorer la directive du tiers.

Dans notre cas, nous pouvons étendre le champ d'application de la directive comme ceci:

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

Tout d'abord, nous demandons de décorer la panedirective en passant son nom, concaténé avec Directivecomme premier argument, puis nous le récupérons à partir du paramètre de rappel (qui est un tableau de directives correspondant à ce nom).

Une fois que nous l'avons obtenu, nous pouvons obtenir son objet scope et l'étendre si nécessaire. Notez que tout cela doit être fait dans le configbloc.

Quelques notes

  • Il a été suggéré d'ajouter simplement une directive portant le même nom, puis de définir son niveau de priorité. En plus d'être non sémantique (ce qui n'est même pas un mot , je sais…), cela pose des problèmes, par exemple que se passe-t-il si le niveau de priorité de la directive tierce change?

  • JeetendraChauhan a affirmé (je ne l'ai pas testé cependant) que cette solution ne fonctionnera pas dans la version 1.13.

Eliran Malka
la source
1
Je vous suggère d'essayer la réponse de @ sh0ber (créez une autre directive juste pour l'émission d'événements).
Eliran Malka
2
Une note rapide sur cette réponse (qui fonctionne très bien), la 'Directive' dans 'paneDirective' a un but ;-) Il m'a fallu un certain temps avant de comprendre cela: stackoverflow.com/questions/19409017/… , voir l'accepté répondre.
Roy Milder
2
salut @EliranMalka vérifier mon plunker plnkr.co/edit/0mvQjHYjQCFS6joYJdwK j'espère que cela aidera quelqu'un
Jeetendra Chauhan
1
Le lien vers decorator()est rompu (mis à jour vers docs.angularjs.org/api/auto/service/$provide#decorator )
Chris Brown
1
@EliranMalka oui, a bindToControllerété introduit dans la v1.3. Mais notez que cela ne doit pas être considéré comme une solution alternative, ce n'est que pour un cas spécifique où la directive d'origine a été configurée avec la bindToControllerpropriété. Bonne idée, je
posterai
8

Bien que ce ne soit pas la réponse directe à votre question, vous voudrez peut-être savoir que la dernière version (en master) de http://angular-ui.github.io/bootstrap/ a ajouté la prise en charge de la désactivation des onglets. Cette fonctionnalité a été ajoutée via: https://github.com/angular-ui/bootstrap/commit/2b78dd16abd7e09846fa484331b5c35ece6619a2

pkozlowski.opensource
la source
+1 pour le heads-up. bon à savoir. Je suppose que le bootstrap angulaire de bower et le composant bootstrap de angular-ui ne sont pas synchronisés.
Kyle
6

Une autre solution où vous créez une nouvelle directive qui l'étend sans modifier la directive d'origine

La solution est similaire à la solution décoratrice:

Créez une nouvelle directive et injectez comme dépendance la directive que vous souhaitez étendre

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)
});

De cette façon, vous pouvez utiliser la directive d'origine et la version étendue dans la même application

kidroca
la source
2
C'est génial, exactement ce dont j'avais besoin pour étendre une directive d'isolat scope avec mes propres variables !! J'ai trouvé que angular.extend ne copiait pas en profondeur les objets, donc cela remplace l'objet de portée de paneDirective par celui-ci. Une alternative est angular.merge qui conservera la portée d'origine de PaneDirective et ajoutera / fusionnera les variables définies ici.
mathewguest
1
oui, angular.mergeaurait dû être utilisé, je vais mettre à jour l'exemple
kidroca
1

Voici une autre solution pour un scénario différent d'extension des liaisons à une directive qui a la bindToControllerpropriété.

Remarque: ce n'est pas une alternative aux autres solutions proposées ici. Elle ne résout qu'un cas spécifique (non couvert dans d'autres réponses) dans lequel la directive d'origine a été élaborée bindToController.

gilad mayani
la source