AngularJs:在视图完全渲染后运行指令

20
我是一个新手,对Angular世界一无所知,我正在面临一个管理指令的问题。
我正在处理一个使用选项卡的项目,并希望扩展其功能以处理当窗口大小比所有选项卡的宽度更窄时的溢出选项卡,因此需要计算一些元素的尺寸才能实现这一点。选项卡是从$scope中的对象构建的,问题在于计算维度的指令在视图完全编译之前运行。
Plnkr链接:http://plnkr.co/edit/LOT4sZsNxnfmQ8zHymvw?p=preview 我尝试过:
1.使用templateUrl在指令中加载模板 2.使用transclude 3.在ng-repeat中使用$last 4.尝试重新排序指令并在每个选项卡中创建一个虚拟指令来触发事件
我认为有一些AngularJS事件或属性可以处理这种情况。
请大家帮帮忙 :)

2
你使用的 $timeout 有什么问题吗?我一直在使用类似的指令,并且将延迟设置为 0,这样在指令中运行代码之前,浏览器有机会绘制 DOM。 - charlietfl
2
你说得没错,但这只是解决问题的权宜之计,当你需要构建复杂的指令时,这将导致多次使用超时,从而造成混乱! - Abu Qusai
是一个常见的用法...习惯它 - charlietfl
4
如果我们只是“习惯了”所有的计算,我们仍然需要打机器代码。 - Alexander Mistakidis
我也需要备份这个。我正在将指令插入视图中,它们会立即呈现,但动态类不会。更糟糕的是,在添加过程中会出现中间类,例如your-class-name-add。我已经尝试了$timeout,并且需要添加长达500毫秒的延迟来覆盖这些更改-这是不可靠的。Alexander提供的下面答案到目前为止代表了最优雅的方法,但涉及另一个监视...它变得难以管理。 - sidonaldson
自从 Angular 1.3 发布以来,在这种情况下 $timeout 不再起作用。 - JARRRRG
4个回答

8

我在你的 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


1
只要您控制好ng-repeat和组件,这是一个很好的解决方案。例如,在我的情况下,我想使用switchery,并且需要确切的视图加载点,以便我可以将其注入到所有呈现的控件中。 - Kat Lim Ruiz

6
您可以在实际的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);

6

嵌套的 $timeout 是我所需要的。 - fizch

3
根据您的评论,您已经有一个有效的解决方案... 目前我所知道的唯一解决方案是$timeout。
问题是,您想要实现什么?您希望在所有渲染完成后进行回调。好的,但是angular如何知道这一点呢?在使用angular时,您显然正在开发一个现代应用程序,重新渲染可能随时发生!可以发生js事件,从后端加载数据,甚至在初始化应用程序后立即发生,这导致重新渲染。用户的鼠标移动可能会触发元素的重新渲染,这些元素甚至超出了由于CSS规则而超出angular范围。任何时候都可能发生重新渲染。
因此,在考虑这些因素时,AngularJS能告诉您什么关于渲染何时完成?它只能告诉您当前的$apply和/或$digest链已处理,即浏览器事件队列现在为空。这正是AngularJS所知道的唯一信息。您可以使用延迟为0的$timeout来实现这一点。
是的,您是正确的,在更大的应用程序中,这可能会变得棘手到不再可靠的程度。但在这种情况下,您应该考虑以另一种方式解决此问题。例如,如果稍后发生重新渲染,则必须有原因,例如从后端加载的新数据。如果是这样,您就知道何时加载数据,因此重新渲染发生,因此您确切地知道何时必须更新宽度。

Angular负责DOM操作,因此,每当DOM操作结束时,可以预期会触发事件。这是在浏览器将更改呈现在屏幕上之前的一个时间点。我认为,使用这样的事件可能足以解决Abu(或我现在遇到的其他人)所遇到的问题。 - ggalmazor
就像我之前写的那样:你可以使用$timeout事件来实现。我不建议过度使用它,而是设计你的控制器代码,让你的应用程序自己知道何时更新某些元素。 - Marco Rinck

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接