dom完成渲染后如何运行指令?


115

我有一个看似简单的问题,没有明显的解决方案(通过阅读Angular JS文档)

我有一个Angular JS指令,该指令根据其他DOM元素的高度进行一些计算,以定义DOM中容器的高度。

指令内部发生了类似的事情:

return function(scope, element, attrs) {
    $('.main').height( $('.site-header').height() -  $('.site-footer').height() );
}

问题是当指令运行时,$('site-header')找不到它,返回一个空数组而不是我需要的jQuery包装的DOM元素。

我是否可以在指令中使用仅在加载DOM之后才能运行的回调,并且可以通过常规jQuery选择器样式查询访问其他DOM元素?


1
您可以使用scope。$ on()和scope。$ emit()来使用自定义事件。不确定这是否是正确/推荐的方法。
Tosh 2012年

Answers:


137

这取决于$('site-header')的构造方式。

您可以尝试使用$ timeout,延迟为0。就像是:

return function(scope, element, attrs) {
    $timeout(function(){
        $('.main').height( $('.site-header').height() -  $('.site-footer').height() );
    });        
}

解释其工作方式:

不要忘记$timeout在指令中注入:

.directive('sticky', function($timeout)

5
谢谢,我尝试使它工作了很长时间,直到我意识到自己还没有通过$timeout该指令。h 现在一切正常,欢呼。
Jannis 2012年

5
是的,您需要$timeout像这样传递给指令:.directive('sticky', function($timeout) { return function (scope, element, attrs, controller) { $timeout(function(){ }); }); };
Vladimir Starkov 2012年

19
您的链接说明解释了为何超时技巧在JavaScript中有效,但在AngularJS上下文中无效的原因。来自官方文档:“ [...] 4. $ evalAsync队列用于安排需要在当前堆栈框架之外但在浏览器视图呈现之前进行的工作。通常使用setTimeout(0)完成,但是所述的setTimeout(0)从缓慢的方法患有并且可能导致闪烁视图因为浏览器呈现的每个事件后视图[...]。 “(重点煤矿)
阿尔贝托

12
我遇到了类似的问题,并且发现执行指令之前我需要大约300ms的时间来允许DOM加载。我真的不喜欢这样插入看似随意的数字。我确定DOM加载速度会因用户而异。那么,如何确定300ms适用于使用我的应用程序的任何人?
keepitreal 2014年

5
对这个答案不太满意..虽然似乎可以回答OP的问题..它非常针对他们的情况,并且与问题的更一般形式(即在dom加载后运行指令)的相关性不明显+太hacky ..根本没有关于角度的任何具体信息
2014年

44

这是我的方法:

app.directive('example', function() {

    return function(scope, element, attrs) {
        angular.element(document).ready(function() {
                //MANIPULATE THE DOM
        });
    };

});

1
甚至都不需要angular.element,因为element已经在那里可用:element.ready(function(){
timhc22 '16

1
@ timhc22元素是对触发指令的DOMElement的引用,您的建议不会导致对页面Document对象的DOMElement引用。
tobius

那不能正常工作。通过这种方法,我得到offsetWidth = 0
Alexey Sh。

37

可能作者不再需要我的回答。不过,出于完整性考虑,我觉得其他用户可能会觉得有用。最好和最简单的解决方案是$(window).load()在返回函数的主体内部使用。(或者,您可以使用document.ready。这实际上取决于您是否需要所有图像)。

$timeout以我的拙见,使用它是一个很弱的选择,在某些情况下可能会失败。

这是我要使用的完整代码:

.directive('directiveExample', function(){
   return {
       restrict: 'A',
       link: function($scope, $elem, attrs){

           $(window).load(function() {
               //...JS here...
           });
       }
   }
});

1
您能否详细说明为什么“在某些情况下可能会失败”?你是什​​么意思
rryter 2014年

6
您假设jQuery在这里可用。
乔纳森·克雷敏

3
@JonathanCremin根据OP,jQuery选择是当前的问题
Nick Devereaux 2014年

1
这很有用,但是,如果有一个使用伪指令构建新项目的帖子,则在初始加载后不会触发窗口加载,因此将无法正常运行。
Brian Scott

@BrianScott-我使用$(window).load的组合进行初始页面渲染(我的用例正在等待嵌入的字体文件),然后使用element.ready进行切换视图。
aaaaaa

8

有一个 ngcontentloaded事件,我想你可以使用它

.directive('directiveExample', function(){
   return {
       restrict: 'A',
       link: function(scope, elem, attrs){

                $$window = $ $window


                init = function(){
                    contentHeight = elem.outerHeight()
                    //do the things
                }

                $$window.on('ngcontentloaded',init)

       }
   }
});

21
您能解释一下$ $window做什么吗?
fish鱼

2
看起来像一些咖啡脚本,也许是为了将$($ window)和$ window注入指令中
mdob 2015年

5

如果由于外部资源而无法使用$ timeout,并且由于时间的特定问题而无法使用指令,请使用广播。

$scope.$broadcast("variable_name_here");所需的外部资源之后或长时间运行的控制器/指令已经完成。

在外部资源加载完成后,添加以下内容。

$scope.$on("variable_name_here", function(){ 
   // DOM manipulation here
   jQuery('selector').height(); 
}

例如,在承诺延迟的HTTP请求中。

MyHttpService.then(function(data){
   $scope.MyHttpReturnedImage = data.image;
   $scope.$broadcast("imageLoaded");
});

$scope.$on("imageLoaded", function(){ 
   jQuery('img').height(80).width(80); 
}

2
这并不能解决问题,因为加载的数据并不意味着它们已经在DOM中呈现,即使它们处于绑定到DOM元素的适当范围变量中也是如此。它们在示波器中加载的时间与dom中渲染的输出之间存在时间间隔。
勒内·斯塔尔德

1

我遇到了类似的问题,想在这里分享我的解决方案。

我有以下HTML:

<div data-my-directive>
  <div id='sub' ng-include='includedFile.htm'></div>
</div>

问题:在父div指令的链接功能中,我想对子div#sub进行jquery。但这给了我一个空的对象,因为当指令的链接功能运行时ng-include尚未完成。因此,首先我用$ timeout进行了一个肮脏的解决,它可以工作,但是延迟参数取决于客户端速度(没人喜欢)。

可行但很脏:

app.directive('myDirective', [function () {
    var directive = {};
    directive.link = function (scope, element, attrs) {
        $timeout(function() {
            //very dirty cause of client-depending varying delay time 
            $('#sub').css(/*whatever*/);
        }, 350);
    };
    return directive;
}]);

这是干净的解决方案:

app.directive('myDirective', [function () {
    var directive = {};
    directive.link = function (scope, element, attrs) {
        scope.$on('$includeContentLoaded', function() {
            //just happens in the moment when ng-included finished
            $('#sub').css(/*whatever*/);
        };
    };
    return directive;
}]);

也许对某人有帮助。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.