问题 如何在处理子节点后调用Angular指令中的函数?


我正在尝试使用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添加一个边距,因此内容居中。

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

我尝试过使用post链接功能,如图所示 这个问题。预链接和后链接函数按预期顺序调用,但不会在具有s-center-content指令的elemenet的子节点被渲染之后调用。由于没有找到孩子,因此刷新调用失败。

如何拨打电话以确保孩子们已经处理完毕?


4656
2018-04-10 17:43


起源



答案:


我认为使用角度 $腕表 是一个更好,更优雅的解决方案:

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:清除$ watch


7
2017-07-21 20:55



这非常聪明 - 也许对答案的更多描述会使其更加明显。 - Roy Truelove
这很酷。可能只想看$(选择器).length虽然不完全确定它是否总是有效。 - David


答案:


我认为使用角度 $腕表 是一个更好,更优雅的解决方案:

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:清除$ watch


7
2017-07-21 20:55



这非常聪明 - 也许对答案的更多描述会使其更加明显。 - Roy Truelove
这很酷。可能只想看$(选择器).length虽然不完全确定它是否总是有效。 - David


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

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

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


5
2018-04-10 19:20



看看父元素怎么样?我会收到儿童补充通知吗?如果是这样,那可能就是这样。我会检查! - Daniel Cerecedo


终于成功了!

我使用了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);                   
                });
            });
        }
    };
});

1
2018-04-11 10:54



干得好,这非常方便!我将尝试找到谷歌小组的主题和角度问题,讨论这个问题,我会参考你的答案。 - Roy Truelove
谢谢Roy的努力。 - Daniel Cerecedo
如果你看一下插件 码,你会注意到它也使用过 setTimeout(fn, 20) 匹配新元素。所以它是一个友好的视图'包'的脏解决方案。 - FelikZ
作为替代,Angular的 $evalAsync 不需要第三方插件 stackoverflow.com/a/24228604/1079110。 - danijar


我相信这可以用css完成。无论这样的数字,目标都是中心元素吗?

[]  []  []  []  []

    []  []  []

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

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


0
2018-01-29 18:09