从另一个指令静态添加AngularJS指令

时间:2015-04-24 01:08:20

标签: javascript angularjs angularjs-directive

我正在尝试使用带有静态但复杂内容的Bootstrap popover(通过ui-bootstrap)。

popover本身可行 - 直接将其应用于特定元素会导致它按预期显示:

<div data-popover="test"></div>

使用范围内的基本动态内容也有效:

<div data-popover="{{key}}"></div>

但是我想以更复杂的方式生成内容,涉及角度服务(尽管它是全局的 - 没有涉及ajax或其他异步代码)。这有效:

<div data-popover="{{getPopoverText(key)}}"></div>

但它会导致每个摘要都进行调用,而在我的情况下,我知道一旦找到该值就永远不会改变。 (遗憾的是我使用的是AngularJS 1.2.23,它早于一次性绑定支持,因此我无法使用它。)

所以我尝试用指令做到这一点:

<div data-generate-popover="key"></div>

module.directive('generatePopover', ['myService', function(myService) {
    return {
        restrict: 'A',
        link: function (scope, element, attrs) {
            var key = scope.$eval(attrs.generatePopover);
            var content = myService.getPopoverText(key);
            element.removeAttr('data-generate-popover');
            element.attr('data-popover', content);
        },
    };
}]);

这确实可以正确运行并交换属性,data-popover属性包含要显示的正确内容。然而,弹出窗口不会出现(可能是AngularJS没有注意到我添加了指令)。

我的最新尝试(受this question启发)是在更改元素后重新编译元素:

module.directive('generatePopover',
    ['myService', '$compile',
function(myService, $compile) {
    return {
        restrict: 'A',
        priority: 10000,
        link: {
            pre: function (scope, element, attrs) {
                var key = scope.$eval(attrs.generatePopover);
                var content = myService.getPopoverText(key);
                element.removeAttr('data-generate-popover');
                element.attr('data-popover', content);
                $compile(element)(scope);
            }
        },
    };
}]);

这似乎按预期工作。我的问题是:这是正确的做法,是否有任何负面影响?除了升级以获得一次性绑定支持之外,还有更好的方法吗?

编辑:显然,一个负面后果是,如果元素具有其他复杂指令(特别是ngRepeat;可能还有ngIf),则此额外编译无法正常工作等等,但我没有测试过。)

3 个答案:

答案 0 :(得分:0)

这里的问题是,弹出窗口的自动初始化只会在dom ready事件中发生一次,之后所做的任何更改都不会初始化插件。

因此,尝试初始化自己的插件

module.directive('generatePopover', ['myService', function (myService) {
    return {
        restrict: 'A',
        link: function (scope, element, attrs) {
            var key = scope.$eval(attrs.generatePopover);
            var content = myService.getPopoverText(key);
            element.removeAttr('data-generate-popover');
            //set the popover content here
            element.attr('data-content', content);
            //here initialize the 
            element.popover();
        },
    };
}]);

答案 1 :(得分:0)

你不应该担心角度会在每个消化周期中执行所有观察者,否则你永远不会担心。即使是像{{key}}这样的简单绑定实际上也是一个在每个摘要周期中解析scope.key的函数。实际上,每个消化周期甚至会做很多次。

Angular是如何工作的(至少在1.3之前,这将完全改变角度2),并且它已经很好地优化了:你唯一关心的应该是制作函数{{1}尽可能快:这里严格禁止jQuery!

现在,如果真的希望这样做,那么您的方法似乎是正确的方法。如果您仍希望getPopoverText更新,但仅当决定而不是每个摘要周期时,您可以添加

key

并在每次需要时播放此事件。但同样,一个典型的角度应用程序可以拥有数千名观察者,并且如果观察者本身编码良好,仍然可以表现得非常流畅,所以我强烈建议不要浪费时间并将代码复杂化为其中一个,而是专注于拥有一个尽可能简单的scope.$on('popover.update', updateThePopover); 方法。只是我自己经验的建议。

PS:为了降低执行的观察者数量,尝试在许多独立的指令中对应用程序进行模块化,并且更喜欢getPopoverText而不是scope.$digest来消化孤立的范围而不是所有这些范围。

答案 2 :(得分:0)

自从发布这个问题以来,我已经简化了一些事情(尽管有一点点处理,我并不完全满意):

<div data-generate-popover="key" data-popover="{{tooltip}}"></div>

module.directive('generatePopover', ['myService', function(myService) {
    return {
        restrict: 'A',
        priority: 10,
        link: function (scope, element, attrs) {
            var key = scope.$eval(attrs.generatePopover);
            element.removeAttr(attrs.$attr.generatePopover);

            scope.tooltip = myService.getPopoverText(key);
        },
    };
}]);

(在这种情况下,我知道每个范围中只有一个被ngRepeat定义为更高,所以我不需要担心多个tooltip被覆盖。)

话虽如此,这仍然是一个黑客,我期待在我升级AngularJS时用一次性绑定替换它。