Ampliación de la directiva angular

114

Me gustaría hacer una modificación menor a una directiva de terceros (específicamente Angular UI Bootstrap ). Simplemente quiero agregar al alcance de la panedirectiva:

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

Pero también quiero mantener Angular-Bootstrap actualizado con Bower. Tan pronto como corra bower update, sobrescribiré mis cambios.

Entonces, ¿cómo hago para extender esta directiva por separado de este componente de bower?

Kyle
fuente
2
La forma más limpia sería usarla $provide.decorator(), vea mi respuesta a continuación.
Eliran Malka

Respuestas:

96

Probablemente, la forma más sencilla de resolver esto es crear una directiva en su aplicación con el mismo nombre que la directiva de terceros. Ambas directivas se ejecutarán y puede especificar su orden de ejecución utilizando la prioritypropiedad (la prioridad más alta se ejecuta primero).

Las dos directivas compartirán el alcance y puede acceder y modificar el alcance de la directiva de terceros a través del linkmétodo de su directiva .

Opción 2: También puede acceder al alcance de una directiva de terceros simplemente colocando su propia directiva nombrada arbitrariamente en el mismo elemento (asumiendo que ninguna directiva usa un alcance aislado). Todas las directivas de alcance no aisladas de un elemento compartirán el alcance.

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

Nota: Mi respuesta anterior fue para modificar un servicio de terceros, no una directiva.

Dan
fuente
3
gracias @ sh0ber, esto es exactamente lo que necesitaba. Y su respuesta anterior también me ayudó, re: servicios de terceros.
Kyle
Oye, esta respuesta es realmente buena, pero no puedo encontrar ninguna documentación sobre la propiedad "prioridad" para las directivas. Todo lo que encontré fue una propaganda que dice "puedes usarlo", pero no puedo encontrar ningún ejemplo real.
Ciel
2
@Ciel La información de la API de la directiva aparentemente se ha movido al $compiledocumento aquí
Dan
60

TL; DR - ¡dame la demostración!


     Big Demo Button     
 


Use $provide's decorator()para, bueno, decorar la directiva del tercero.

En nuestro caso, podemos extender el alcance de la directiva así:

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

Primero, solicitamos decorar la panedirectiva pasando su nombre, concatenado con Directivecomo primer argumento, luego lo recuperamos del parámetro de devolución de llamada (que es una matriz de directivas que coinciden con ese nombre).

Una vez que lo tenemos, podemos obtener su objeto de alcance y extenderlo según sea necesario. Tenga en cuenta que todo esto debe hacerse en el configbloque.

Algunas notas

  • Se ha sugerido simplemente agregar una directiva con el mismo nombre y luego establecer su nivel de prioridad. Además de ser poco semántico (que ni siquiera es una palabra , lo sé ...), plantea problemas, por ejemplo, ¿qué pasa si el nivel de prioridad de la directiva de terceros cambia?

  • JeetendraChauhan ha afirmado (aunque no lo he probado) que esta solución no funcionará en la versión 1.13.

Eliran Malka
fuente
1
Le sugiero que pruebe la respuesta de @ sh0ber (cree otra directiva solo para emitir eventos).
Eliran Malka
2
Una nota rápida sobre esta respuesta (que funciona muy bien), la 'Directiva' en 'paneDirective' tiene un propósito ;-) Me tomó un tiempo antes de darme cuenta: stackoverflow.com/questions/19409017/… , vea el aceptado responder.
Roy Milder
2
hola @EliranMalka revisa mi plunker plnkr.co/edit/0mvQjHYjQCFS6joYJdwK espero que esto ayude a alguien
Jeetendra Chauhan
1
El enlace decorator()está roto (actualizado a docs.angularjs.org/api/auto/service/$provide#decorator )
Chris Brown
1
@EliranMalka sí, bindToControllerse introdujo en la v1.3. Pero tenga en cuenta que esto no debe considerarse una solución alternativa, esto es solo para un caso específico en el que la directiva original se estableció con la bindToControllerpropiedad. Buena idea, publicaré esto como respuesta :)
gilad mayani
8

Si bien esta no es la respuesta directa a su pregunta, es posible que desee saber que la última versión (en master) de http://angular-ui.github.io/bootstrap/ agregó soporte para deshabilitar pestañas. Esta función se agregó a través de: https://github.com/angular-ui/bootstrap/commit/2b78dd16abd7e09846fa484331b5c35ece6619a2

pkozlowski.opensource
fuente
+1 para el mano a mano. bueno saber. Supongo que angular-bootstrap de bower y el componente bootstrap de angular-ui no están sincronizados.
Kyle
6

Otra solución en la que crea una nueva directiva que la extiende sin modificar la directiva original

La solución es similar a la solución del decorador:

Cree una nueva directiva e inyecte como dependencia la directiva que desea extender

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 esta manera, puede usar la directiva original y la versión extendida en la misma aplicación

Kidroca
fuente
2
¡Esto es genial, justo lo que necesitaba para extender una directiva de alcance aislado con mis propias variables! Encontré que angular.extend no copia en profundidad los objetos, por lo que esto reemplaza el objeto de alcance de paneDirective con este. Una alternativa es angular.merge que mantendrá el alcance original de PaneDirective y agregará / fusionará las variables definidas aquí.
mathewguest
1
sí, angular.mergedebería haberse utilizado, actualizaré el ejemplo
kidroca
1

Aquí hay otra solución para un escenario diferente de extender enlaces a una directiva que tiene la bindToControllerpropiedad.

Nota: esta no es una alternativa a otras soluciones que se ofrecen aquí. Resuelve solo un caso específico (no cubierto en otras respuestas) en el que se estableció la directiva original bindToController.

gilad mayani
fuente