AngularJS:如何在 AngularJS 呈现模板后运行附加代码?

IT技术 javascript jquery dom angularjs
2021-02-12 11:41:23

我在 DOM 中有一个 Angular 模板。当我的控制器从服务中获取新数据时,它会更新 $scope 中的模型,并重新呈现模板。到目前为止一切都很好。

问题是,在重新渲染模板并在 DOM 中(在本例中为 jQuery 插件)后,我还需要做一些额外的工作。

好像应该有一个事件要听,比如AfterRender,但是我找不到任何这样的东西。也许指令会是一种方式,但它似乎也为时过早。

这是一个 jsFiddle 概述了我的问题:Fiddle-AngularIssue

== 更新 ==

根据有用的评论,我相应地切换到一个指令来处理 DOM 操作,并在该指令中实现了一个模型 $watch。但是,我仍然遇到相同的基本问题;$watch 事件中的代码在模板被编译并插入到 DOM 之前触发,因此,jquery 插件总是评估一个空表。

有趣的是,如果我删除 async 调用,整个事情都可以正常工作,所以这是朝着正确方向迈出的一步。

这是我更新的 Fiddle 以反映这些变化:http : //jsfiddle.net/uNREn/12/

6个回答

首先,处理渲染的正确位置是指令。我的建议是通过像这样的指令来包装 DOM 操作 jQuery 插件。

我遇到了同样的问题并想出了这个片段。它使用$watch$evalAsync确保您的代码在诸如ng-repeat已解析的指令和诸如{{ value }}呈现的模板之类的指令之后运行

app.directive('name', function() {
    return {
        link: function($scope, element, attrs) {
            // Trigger when number of children changes,
            // including by directives like ng-repeat
            var watch = $scope.$watch(function() {
                return element.children().length;
            }, function() {
                // Wait for templates to render
                $scope.$evalAsync(function() {
                    // Finally, directives are evaluated
                    // and templates are renderer here
                    var children = element.children();
                    console.log(children);
                });
            });
        },
    };
});

希望这可以帮助您避免一些斗争。

值得注意的是,您还可以将link函数与templateUrl.
2021-03-22 11:41:23
这可能是显而易见的,但如果这对您不起作用,请检查链接函数/控制器中的异步操作。我不认为 $evalAsync 可以考虑这些。
2021-03-26 11:41:23

这篇文章很旧,但我将您的代码更改为:

scope.$watch("assignments", function (value) {//I change here
  var val = value || null;            
  if (val)
    element.dataTable({"bDestroy": true});
  });
}

jsfiddle

我希望它能帮助你

我对 Angular 比较陌生,我看不出如何在我的具体情况下实现这个答案。我一直在查看各种答案并进行猜测,但这不起作用。我不确定“手表”功能应该看什么。我们的 angular 应用程序有各种不同的 eng- 指令,它们会因页面而异,所以我怎么知道要“看”什么?当页面完成渲染时,肯定有一种简单的方法来触发一些代码吗?
2021-03-22 11:41:23
这是否已被弃用?当我尝试这个时,它实际上渲染 DOM之前调用它依赖于影子dom还是什么?
2021-04-02 11:41:23
谢谢,这很有帮助。这对我来说是最好的解决方案,因为它不需要控制器的 dom 操作,并且当数据从真正的异步服务响应更新时仍然可以工作(我不必在我的控制器中使用 $evalAsync)。
2021-04-12 11:41:23

按照 Misko 的建议,如果你想要异步操作,那么代替 $timeout() (这不起作用)

$timeout(function () { $scope.assignmentsLoaded(data); }, 1000);

使用 $evalAsync() (确实有效)

$scope.$evalAsync(function() { $scope.assignmentsLoaded(data); } );

小提琴我还添加了一个“删除数据行”链接,该链接将修改 $scope.assignments,模拟对数据/模型的更改——以表明更改数据有效。

概念概述页面运行时部分解释了当您需要在当前堆栈帧之外但在浏览器呈现之前发生某些事情时,应使用 evalAsync。(在这里猜测……“当前堆栈帧”可能包括 Angular DOM 更新。)如果您需要在浏览器呈现后发生某些事情,请使用 $timeout。

但是,正如您已经发现的那样,我认为这里不需要异步操作。

$scope.$evalAsync($scope.assignmentsLoaded(data));IMO 没有任何意义。for 的参数$evalAsync()应该是一个函数。在您的示例中,您在应该注册函数以供以后执行时调用该函数。
2021-04-03 11:41:23
@hgoebl,$evalAsync的参数可以是函数或表达式。在这种情况下,我使用了一个表达式。但你是对的,表达式会立即执行。我修改了答案以将其包装在一个函数中以供以后执行。谢谢你抓住那个。
2021-04-08 11:41:23

我发现最简单(便宜又好用)的解决方案就是在渲染的最后一个元素的末尾添加一个带有 ng-show = "someFunctionThatAlwaysReturnsZeroOrNothing()" 的空跨度。此函数将在检查是否应显示 span 元素时运行。执行此函数中的任何其他代码。

我意识到这不是最优雅的做事方式,但是,它对我有用......

我有一个类似的情况,虽然稍微相反,我需要在动画开始时删除加载指示器,在移动设备上 angular 的初始化速度比要显示的动画快得多,并且使用 ng-cloak 是不够的,因为加载指示器是在显示任何真实数据之前就删除了。在这种情况下,我只是将 return 0 函数添加到第一个呈现的元素,并在该函数中翻转了隐藏加载指示器的 var。(当然,我在这个函数触发的加载指示器中添加了一个 ng-hide。

这肯定是一个黑客,但这正是我所需要的。强制我的代码完全在渲染之后运行(或非常接近)。在理想的世界中,我不会这样做,但我们在这里......
2021-03-16 11:41:23
我需要在$anchorScrollDOM 呈现后调用内置服务,但除此之外似乎没有其他方法可以解决问题。黑客但有效。
2021-03-18 11:41:23
ng-init 可能并不总是有效。例如,我有一个 ng-repeat,它向 dom 添加了许多图像。如果我想在每个图像添加到 ng-repeat 之后调用一个函数,我必须使用 ng-show。
2021-03-19 11:41:23
ng-init 是更好的选择
2021-03-22 11:41:23
哇!好点子。对自身使用 Angular。这是唯一有效的方法,所有涉及异步操作的解决方案都不起作用,因为视图在更改之前是可见的,这会导致闪烁。Angular 非常棒,但是这些琐碎的事情(例如,简单地直接聚焦一个元素)很难在其中实现并且需要 hack
2021-03-22 11:41:23
感谢您的评论。也许我对 Angular 太陌生,但我没有看到如何将它应用到我的代码中。有什么例子可以指给我看吗?
2021-04-02 11:41:23