推迟大小计算直到ui渲染

时间:2014-08-07 18:10:26

标签: javascript css html5 angularjs angularjs-directive

这是我的标记:

<div resizer>
    <div class="a"></div>
    <div class="b">
        This content generated dynamically using ng-include and other directives inside
    </div>
</div>

第二个div(.b)改变它的高度(动态添加在其中的元素)。我写了一个指令,应该根据可用的高度更新第一个div(.a):

'use strict';
myModule.directive('resizer', ['$timeout', function ($timeout) {
    return {
        restrict: 'A',
        link: function (scope, element, attrs) {

            function updateSizes(event) {
                var a = element.find('.a');
                var b = element.find('.b');

                var bHeight = b.outerHeight();
                var totalHeight = element.height();
                a.height(totalHeight - bHeight);
            }

            element.on('some-event', updateSizes);
            $timeout(updateSizes);
        }
    };
}]);

当我启动我的应用程序时,有时候,我的div的高度等于零(高度:0),因为b.outerHeight();和element.height();返回零 这是为什么?我使用了$ timeout,因此浏览器有时间呈现视图 有什么想法吗?

5 个答案:

答案 0 :(得分:2)

您有两种可能的选择(请参阅ngInclude docs here):

  1. 使用ngInclude onload事件
  2. 在加载内容时听取ngInclude发出的$ includeContentLoaded事件
  3. 我建议使用第二个选项,因为每次要使用指令时都会保存以指定onload事件。
    您可以在链接函数中指定侦听器,如下所示:

    function link(scope, element, attrs) {
        scope.$on('$includeContentLoaded', function(){
            console.log('Template included');
            // you can add all your height calculation and DOM manipulation here
        });
    }
    

    看一下this demo Plunker(我希望内容完全不言自明) 您的代码应该看起来像这样:

    'use strict';
    myModule.directive('resizer', ['$timeout', function ($timeout) {
        return {
            restrict: 'A',
            link: function (scope, element, attrs) {
    
                function updateSizes(event) {
                    var a = element.find('.a');
                    var b = element.find('.b');
    
                    var bHeight = b.outerHeight();
                    var totalHeight = element.height();
                    a.height(totalHeight - bHeight);
                }
    
                scope.$on('$includeContentLoaded', updateSizes);
    
            }
        };
    }]);
    

答案 1 :(得分:1)

您的问题似乎是在您的指令模板中有ng-include

ng-include本身就是一个指令,它创造了一个新的范围。然而,只要呈现HTML,就会执行resizer指令link函数,因此ng-include - d模板已经存在,但其内容尚未加载(甚至是单独的如果之前未加载模板,则 HTTP / GET 请求。

我确信还有更多修复方法,但可以使用onload标记的ng-include参数:

// ... your directive ...
link: function (scope, element, attrs) {
  function updateSizes(event) {
    console.log (element.find('.b').outerHeight());
  }

  scope.ngOnload = updateSizes;
}

现在进入你的html模板:

<div resizer>
  <div class="a"></div>
  <div class="b">
    <div ng-include="'sometemplate.html'" onload="ngOnload()"></div>
  </div>
</div>

这对我起了作用,并且div.b元素outerHeight在控制台日志中不再为0。


我知道,您已经使用$timeout来等待浏览器了。如果我的解决方案不适合您,那些div中必须加载一些奇怪的东西,可能需要解决方案所需的更多细节。但是debugger函数中带有chrome调试器的updateSizes语句可以提供很多帮助。

答案 2 :(得分:1)

方法

使用angular的常见模式是使嵌套指令 require 一个父指令并将其自身注册到父指令。您可以使用此模式来解决您的问题。我已将嵌套指令 resizer-source resizer-dest 添加到您的标记中:

<div resizer>
    <div class="a" resizer-dest></div>
    <div class="b" resizer-source>
        This content generated dynamically using ng-include and other directives inside
    </div>
</div>

大小调整源

myModule.directive('resizerSource', function () {
    return {
        require: '^resizer',
        link: function (scope, element, attrs, ctrl) {
          ctrl.registerSource(element);
        }
    };
});

大小调整-DEST

myModule.directive('resizerDest', function () {
    return {
        require: '^resizer',
        link: function (scope, element, attrs, ctrl) {
          ctrl.registerDest(element);
        }
    };
});

大小调整

myModule.directive('resizer', ['$timeout', function ($timeout) {
    return {
        controller: function($scope) {
            var a,b;
            function update() {
              if (a && b) {
                var bHeight = b.outerHeight();
                var totalHeight = element.height();
                a.height(totalHeight - bHeight);
              }
            }
            this.registerSource = function(elm) {
                b = elm;
                update();
            }
            this.registerDest = function(elm) {
                a = elm;
                update();
            }
        }
    };
}]);

更具灵活性

如果出于某种原因, b 的高度即使在 b 的链接功能运行时也不可用,您可以通过设置 b 的高度为$watch,仅当手表评估为正数时才会注册 b 。之后,取消观察者,除非您期望元素的高度继续变化。

其他信息

  

如果我要观察b的大小,我将不得不引起一个摘要周期(通过调用$ apply()或$ digest)。我通知调整器使用dom事件进行更新。你觉得怎么样?

(1)$watch尺寸是一项昂贵的操作,因为每次检查高度时它都会强制重绕。所以尽快取消$watch。 (2)无法保证角度会在正确的时间触发消化周期,因此可能需要使用您提到的dom事件。

出于这些原因,使用$watch可能不是最好的方法,但这实际上取决于您的具体情况。

  

在链接函数中使用元素不是一个好习惯吗?

使用控制器而不是链接功能没有任何意义。他们有不同的调用顺序,但这不会在这里发挥作用。

  

您无法访问控制器中的“元素”..是否将链接函数中的缩放器代码安全?我的意思是,我必须将registerSource和registerDest放在控制器上

如果需要,您可以将$element注入控制器。或者,您可以将指令自己的控制器添加到require属性,以便从link函数访问它。您是否选择将代码放入控制器或link函数通常会归结为组织,因此如果它简化了代码,请不要害怕使用控制器而不是link函数。

答案 3 :(得分:1)

我怀疑这个问题可能是因加载ng-include资源的延迟造成的。您可以预加载缓存:

$templateCache.put('template.html', '<b>Template</b>');

或者如果内容来自$ http电话:

$http.get('template.html', {cache:$templateCache});

答案 4 :(得分:1)

我知道这不是您正在寻找的答案,但为什么您要走这么简单的事情呢?

The best programming is, no programming

我建议您只为此目的使用css

<style>
.tbl {display:table}
.tbl-row {display:table-row}
.tbl-cell {display:table-cell}
</style>

<div class="tbl">
  <div class="tbl-row">
    <div class="tbl-cell">Foo</div>
    <div class="tbl-cell">
        This content generated dynamically using ng-include and other directives inside
    </div>
  </div>
</div>

示例:http://plnkr.co/edit/H81p2yu0jG8OuB8paFpM?p=preview