【问题标题】:AngularJs: Run Directives after View is fully renderedAngularJs:视图完全呈现后运行指令
【发布时间】:2013-02-18 07:17:32
【问题描述】:

我是所有 Angular World 的新手,我在管理指令时遇到了问题。

我正在开发一个使用选项卡的项目,我想扩展它的功能以在窗口大小比我所有选项卡的宽度更窄时处理溢出的选项卡,所以我需要计算元素的一些尺寸来实现这一点. 选项卡是从 $scope 中的对象构建的,问题是计算尺寸的指令在视图完全编译之前运行。

Plnkr 链接: http://plnkr.co/edit/LOT4sZsNxnfmQ8zHymvw?p=preview

我尝试过的:

  1. 使用 templateUrl 在指令中加载模板
  2. 使用 transclude
  3. 在 ng-repeat 中使用 $last
  4. 尝试重新排序指令并在每个选项卡中创建一个虚拟指令以触发事件

我认为有一些 AngularJs 事件或属性来处理这种情况。

请大家帮忙:)

【问题讨论】:

  • 您使用的$timeout 有什么问题?我一直在使用类似的指令,并使用0 进行延迟,似乎让浏览器有机会在代码在指令中运行之前绘制 DOM
  • 你是对的,但这是解决方法而不是解决问题的方法,当你有复杂的指令来构建它时,将通过多次使用超时而结束并导致一团糟!
  • 是常用的用法……习惯了
  • 如果我们只是“习惯”了我们所有的计算......我们仍然会输入机器代码。
  • 我也需要备份。我将指令插入到视图中,它们会立即呈现,但动态类不会。更糟糕的是,您在添加过程中会获得中间类,例如 your-class-name-add 。我已经尝试过 $timeout 并且我需要添加一个长达 500 毫秒的延迟来覆盖这些更改 - 这是不可靠的。亚历山大的以下回答代表了迄今为止最优雅的方法,但涉及另一只手表......它变得难以管理。

标签: angularjs angularjs-directive


【解决方案1】:

我在你的 plunker 上做了一个分支,我想这就是你要找的东西

我把你的指令改成这个

.directive('onFinishRenderFilters', function ($timeout) {
    return {
        restrict: 'A',
        link: function (scope, element, attr) {
            if (scope.$last === true) {
                $timeout(function () {
                    scope.$emit('ngRepeatFinished');
                });
            }
        }
    }
});

然后我在你的 HTML 中添加了指令

 <li ng-repeat="tab in tabs" on-finish-render-filters>

最后我把要运行的代码放在重复完成后

$scope.$on('ngRepeatFinished', function (ngRepeatFinished) {
        $scope.tabs = [{
            index: 1,
            title: "Tab 1",
            link: "/tab1/"
        },{
            index: 2,
            title: "Tab 2",
            link: "/tab2/"
        },{
            index: 3,
            title: "Tab 3",
            link: "/tab3/"
        },{
            index: 4,
            title: "Tab 4",
            link: "/tab4/"
        }];

  });

http://plnkr.co/edit/TGduPB8FV47QvjjLnFg2?p=preview

【讨论】:

  • 就您控制 ng-repeat 和组件而言,这是一个很好的解决方案。例如,在我的情况下,我想使用切换器,并且我需要加载视图的确切位置,以便我可以将它注入到所有呈现的控件中。
【解决方案2】:

您可以在实际的 DOM 元素上添加一个监视,以便了解您的 ng-repeat 何时加载到 DOM 中。

给你的 ul 一个 ID,比如#tabs

$scope.$watch(
    function () { return document.getElementById('tabs').innerHTML },  
    function(newval, oldval){
        //console.log(newval, oldval);
        //do what you like
    }, true);

【讨论】:

    【解决方案3】:

    对于 ng-repeat 中使用的指令,AngularJS 似乎没有可靠的渲染后回调。 [1]

    也许您可以在 CSS 级别通过为特定屏幕宽度添加“响应式”溢出控制元素来解决此问题。

    1. https://groups.google.com/forum/#!topic/angular/SCc45uVhTt8

    更新:现在有一种方法可以使用嵌套的 $timeouts。见:http://lorenzmerdian.blogspot.de/2013/03/how-to-handle-dom-updates-in-angularjs.html

    【讨论】:

    • 嵌套的 $timeout 是我所需要的。
    【解决方案4】:

    根据您的 cmets,您已经有了一个可行的解决方案……而且它目前是我所知道的唯一解决方案:$timeout。

    问题是,你想实现什么?当所有渲染完成时,您需要一个回调。好的,很好,但是 Angular 怎么知道呢?在您显然正在使用 Angular 开发的现代应用程序中,重新渲染可能一直发生!从后端加载数据的地方可能会发生 js 事件,即使在初始化应用程序之后也是如此,这会导致重新渲染。用户的鼠标移动可以触发元素的重新渲染,由于 CSS 规则,这些元素甚至超出了角度范围。任何时候都可能发生重新渲染。

    那么,考虑到这些因素,angularJS 在渲染完成时能告诉你什么?它只能告诉你,当当前的 $apply 和/或 $digest 链被处理时,浏览器事件队列现在是空的。这正是 angularJS 知道的唯一信息。为此,您使用延迟为 0 的 $timeout。

    是的,你是对的,在更大的应用程序中,这可能会变得非常棘手,以至于它不再可靠。但是在这种情况下,您应该考虑如何以另一种方式解决此问题。菲如果稍后发生重新渲染,则一定有原因,例如从后端加载的新数据。如果是这样,您知道何时加载数据并因此发生重新渲染,因此您确切知道何时必须更新宽度。

    【讨论】:

    • Angualr 负责 DOM 操作,因此,只要 DOM 操作结束,就会触发一个事件。那是浏览器在屏幕上呈现更改之前的时刻。我认为这样的事件可能足以解决阿布遇到的问题(或我现在也遇到的其他问题)。
    • 就像我已经写过的:你可以使用的事件是 $timeout。我不建议广泛使用它,而是设计您的控制器代码,以便您的应用知道何时更新某些元素。
    猜你喜欢
    • 2012-01-26
    • 1970-01-01
    • 1970-01-01
    • 2014-04-25
    • 2019-09-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-30
    相关资源
    最近更新 更多