【问题标题】:How to invoke a function in an Angular directive after the child nodes have been processed?处理完子节点后,如何在 Angular 指令中调用函数?
【发布时间】:2015-08-14 03:10:44
【问题描述】:

我正在尝试使用 Angular JS 指令将 div 的内容居中。

这是模板的简化版:

<div id="mosaic" class="span12 clearfix" s-center-content>
    <div ng-repeat="item in hits" id="{{item._id}}" >
    </div>
</div>

这是指令:

 module.directive('sCenterContent', function() {
    var refresh = function(element){
        var aWidth= element.innerWidth();
        var cWidth= element.children()[0].offsetWidth;
        var cHeight= element.children()[0].offsetHeight;
        var perRow= Math.floor(aWidth/cWidth);
        var margin=(aWidth-perRow*cWidth)/2;
        if(perRow==0){
            perRow=1;
        }
        var top=0;
        element.children().each(function(index, child){
            $(child).css('margin-left','0px');
            if((index % perRow)==0){
                $(child).css('margin-left',margin+'px');
            }
        });
    };
    return {
        link : function(scope, element, attrs) {
            $(window).resize(function(event){
                refresh(element);
            });
        }
    };
});

它基本上是浮动一些内部div,并为每行的第一个div添加一个边距,因此内容居中。

调整浏览器大小时,这可以正常工作。当我在初始化后尝试进行刷新时,问题就出现了。

我已尝试使用this question 中的帖子链接功能。 pre link 和 post link 函数按预期顺序调用,但不是在具有 s-center-content 指令的 elemenet 的子级被渲染之后。由于找不到子节点,刷新调用失败。

如何调用 refresh 以确保子项已得到处理?

【问题讨论】:

    标签: angularjs angularjs-directive


    【解决方案1】:

    我认为使用 angular 的 $watch 是一个更好、更优雅的解决方案:

    link : function(scope, element, attrs) {
                $(window).resize(function(event){
                    refresh(element);
                });
    
                var watchCount = 0,
                unWatcher = $watch(
                    function() {
                       return $('*[s-center-content] img').length !== 0;
                    },
                    function() {
                       // ensure refresh isn't called the first time (before the count of elements is > 0)
                       if (++watchCount === 1) {
                           return;
                       }
    
                       refresh(element);                   
    
                       // stop watching the element now that refresh has been called
                       unWatcher();
                    }, true);
            }
    

    关于停止观看:AngularJS : Clear $watch

    【讨论】:

    • 这很聪明——也许对答案的描述多一点会让它更明显。
    • 这很酷。可能只想看 $(selector).length 虽然不完全确定这是否总是有效。
    【解决方案2】:

    这是一个反复出现的问题(在某个地方存在与此相关的问题,以及谷歌论坛中的一个主题..)

    Angular 无法知道 dom 何时完成,它只能知道它调用的函数何时返回(以及任何 Promise 都已解决)。如果这些函数是异步的并且没有回调或承诺,则无法获得通知。

    我看到它完成的方式(以及我完成它的方式)是使用 setInterval 定期检查 DOM 以查看它是否处于完成状态。我讨厌这个解决方案,并且会很感激有一个替代方案,但是,它确实完成了工作。

    【讨论】:

    • 看父元素呢?我会收到添加儿童的通知吗?如果是这样,那可能就是这样。我会检查的!
    【解决方案3】:

    终于成功了!

    我使用了 livequery 插件,并在链接方法的末尾添加了以下行:

    $('*[s-center-content] > *').livequery( function(event){
        refresh(element);
    });
    

    我使用 s-center-content 指令选择元素的任何第一级子元素(当前和未来),并附加一个处理程序,当元素添加到 DOM 时调用该处理程序。

    编辑

    最后一点。我的内容的布局主要取决于具有动态评估src 属性的内部图像。所以为了让它工作,我不得不把代码改成这样:

    $('*[s-center-content] img').livequery( function(event){
        $(this).load(function(){
           refresh(element);                    
        });
    });
    

    将左浮动 div 居中放置的指令的完整代码如下。请注意,它依赖于将计算出的边距添加到行中的第一个 div。

    module.directive('sCenterContent', function() {
        var refresh = function(element){
            var aWidth= element.innerWidth();
            var cWidth= element.children()[0].offsetWidth;
            var cHeight= element.children()[0].offsetHeight;
            var perRow= Math.floor(aWidth/cWidth);
            var margin=(aWidth-perRow*cWidth)/2;
            if(perRow==0){
                perRow=1;
            }
            var top=0;
            element.children().each(function(index, child){
                $(child).css('margin-left','0px');
                if((index % perRow)==0){
                    $(child).css('margin-left',margin+'px');
                }
            });
        };
        return {
            link : function(scope, element, attrs) {
                $(window).resize(function(event){
                    refresh(element);
                });
                $('*[s-center-content] img').livequery( function(event){
                    $(this).load(function(){
                        refresh(element);                   
                    });
                });
            }
        };
    });
    

    【讨论】:

    • 干得好,这很方便!我将尝试查找 google 组线程和有关此问题的 Angular 问题,我将参考您的答案。
    • 感谢罗伊的努力。
    • 如果您查看插件code,您会注意到它还使用setTimeout(fn, 20) 来匹配新元素。所以它在友好的视图'pack'中是一个肮脏的解决方案。
    • 作为替代方案,Angular 的$evalAsync 不需要第三方插件stackoverflow.com/a/24228604/1079110
    【解决方案4】:

    我相信这可以通过 css 来实现。是不是像这样不分数量的以目标为中心的元素?

    []  []  []  []  []
    
        []  []  []
    

    如果是这样,这绝对可以用 css 实现,并且可以用更少的复杂性来实现。 这是一个例子:

    http://codepen.io/thetallweeks/pen/kvAJb

    【讨论】:

      猜你喜欢
      • 2015-11-22
      • 2019-11-25
      • 2014-12-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-01-15
      • 2023-04-08
      • 1970-01-01
      相关资源
      最近更新 更多