扩展AngularJs指令

114

我想对第三方指令(特别是 Angular UI Bootstrap)进行小修改。我只需要在pane指令的作用域中添加内容:

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

但是我也希望使用Bower时保持Angular-Bootstrap的最新版本。一旦运行bower update,我的更改就会被覆盖。

那么我该如何单独扩展这个指令,而不影响这个Bower组件呢?

5个回答

97

解决这个问题最简单的方法可能是在您的应用程序上创建一个与第三方指令同名的指令。两个指令都会运行,您可以使用 priority 属性指定它们的运行顺序(优先级越高的先运行)。

两个指令将共享作用域,您可以通过您的指令的 link 方法访问和修改第三方指令的作用域。

选项2:您还可以通过在同一元素上放置一个任意命名的指令(假设两个指令都不使用隔离作用域),来访问第三方指令的作用域。在一个元素上所有非隔离作用域指令将共享作用域。

进一步阅读:https://github.com/angular/angular.js/wiki/Dev-Guide%3A-Understanding-Directives

注意:我的先前回答是针对修改第三方服务而非指令。


3
谢谢 @sh0ber,这正是我所需要的。你之前的回答也帮了我,关于第三方服务的问题。 - Kyle Cureau
嘿,这个答案真的很好,但是我找不到有关指令“priority”属性的任何文档。我只找到了一句话说“你可以使用它”,但是找不到任何实际的例子。 - Ciel
2
@Ciel 指令 API 信息显然已经移动到 $compile 文档 这里 - Dan

59

简而言之 - 给我演示!


     大型演示按钮     
 


使用$providedecorator()来装饰第三方指令。

在我们的例子中,我们可以像这样扩展指令的作用域:

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

首先,我们请求通过传递其名称与 Directive 拼接作为第一个参数来装饰 pane 指令,然后我们从回调参数中检索它(这是一个匹配该名称的指令数组)。
一旦我们得到它,我们就可以获取它的作用域对象并根据需要扩展它。请注意,所有这些都必须在 config 块中完成。
一些注释:
  • 有人建议只需添加具有相同名称的指令,然后设置其优先级级别。除了不符合语义(甚至不是一个词,not even a word,我知道...),还存在问题,例如,如果第三方指令的优先级级别发生更改怎么办?

  • JeetendraChauhan声称(我没有测试过)这个解决方案在 1.13 版本中不起作用。


1
我建议你试一下@sh0ber的答案(创建另一个指令来发出事件)。 - Eliran Malka
2
关于这个答案(非常有效),“paneDirective”中的“Directive”确实有一个目的;-) 我花了一段时间才弄清楚:https://dev59.com/SWIk5IYBdhLWcg3wTccr,请参见被接受的答案。 - Roy Milder
2
嗨@EliranMalka,请查看我的plunker http://plnkr.co/edit/0mvQjHYjQCFS6joYJdwK 希望这能帮助到某些人。 - Jeetendra Chauhan
1
decorator() 的链接已经失效(更新为 https://docs.angularjs.org/api/auto/service/$provide#decorator)。 - Chris Brown
1
@EliranMalka 是的,bindToController 是在 v1.3 中引入的。但请注意,这不应被视为替代解决方案,它仅适用于原始指令使用 bindToController 属性设置的特定情况。 好主意,我会将其发布为答案 :) - gilad905
显示剩余7条评论

8

谢谢提醒。好知道。我猜测Bower的Angular-Bootstrap和Angular-UI的Bootstrap组件不同步。 - Kyle Cureau

6

有另一种解决方案,您可以创建一个新指令来扩展原有指令而不对其进行修改。

这个解决方案与装饰器解决方案类似:

创建一个新的指令并将您希望扩展的指令作为依赖项注入。

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

这样你就可以在同一个应用程序中使用原始指令和扩展版本。


2
这太棒了,正是我需要的来扩展一个隔离作用域指令与我的自定义变量!我发现angular.extend不会深度复制对象,所以它将paneDirective的作用域对象替换为此对象。另一种选择是使用angular.merge,它将保留PaneDirective的原始作用域并添加/合并在此处定义的变量。 - mathewguest
1
是的,应该使用angular.merge,我会更新示例。 - kidroca
angualr.merge已过时,请参见https://docs.angularjs.org/api/ng/function/angular.merge。您应该使用类似Lodash的东西(由AnguarJs推荐) https://lodash.com/docs/4.17.15#merge - Nebulosar

1

这里有另一种解决方案,用于将绑定扩展到具有bindToController属性的指令的不同情况。

注意:这不是其他提供的解决方案的替代品。它只解决一个特定情况(其他答案中未涉及的情况),即原始指令使用bindToController设置。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接