当Angular完成向DOM添加范围更新时如何触发方法?

aco*_*ado 36 angularjs angularjs-scope

在我将更改添加到$ scope变量之后,我正在寻找一种执行代码的方法,在本例中为$ scope.results.我需要这样做,以便在它可以执行之前调用一些遗留代码,这些遗留代码要求项目在DOM中.

我的真实代码是触发AJAX调用,并更新范围变量以更新ui.所以我目前我的代码在推送到示波器后立即执行,但遗留代码失败,因为dom元素尚未可用.

我可以使用setTimeout()添加一个丑陋的延迟,但这并不能保证DOM真正准备就绪.

我的问题是,有什么方法可以绑定到"渲染"类似的事件?

var myApp = angular.module('myApp', []);

myApp.controller("myController", ['$scope', function($scope){
    var resultsToLoad = [{id: 1, name: "one"},{id: 2, name: "two"},{id: 3, name: "three"}];
    $scope.results = [];

    $scope.loadResults = function(){
        for(var i=0; i < resultsToLoad.length; i++){
            $scope.results.push(resultsToLoad[i]);
        }
    }

    function doneAddingToDom(){
        // do something awesome like trigger a service call to log
    }
}]);
angular.bootstrap(document, ['myApp']);
Run Code Online (Sandbox Code Playgroud)

链接到模拟代码:http://jsfiddle.net/acolchado/BhApF/5/

提前致谢!

Mar*_*cok 61

$ evalAsync队列用于调度哪些需要当前堆栈帧之外发生的工作,但浏览器的看法之前渲染.- http://docs.angularjs.org/guide/concepts#runtime

好的,那么什么是"堆栈框架"?Github评论显示更多:

如果你从控制器入队,那么它将在之前,但如果你从指令入队,那么它将在之后.- https://github.com/angular/angular.js/issues/734#issuecomment-3675158

上面,Misko正在讨论何时运行由$ evalAsync排队执行的代码,与Angular更新DOM的时间有关.我建议之前阅读两个Github评论,以获得完整的上下文.

因此,如果代码使用指令中的$ evalAsync进行排队,则它应该在Angular操纵DOM之后但在浏览器呈现之前运行.如果您需要在浏览器渲染后运行某些内容,或者在控制器更新模型后运行,请使用$timeout(..., 0);

另请参阅/sf/answers/953352711/,其中还有一个使用$ evalAsync()的示例小提琴.

  • @CMCDragonkai,因为Javascript是单线程的,当Angular运行时,浏览器不会更新您看到的内容(即,它不会呈现).请参见[概念页面](http://docs.angularjs.org/guide/concepts)上的第二张图片."DOM渲染"发生在Angular摘要循环之后.我总是首先尝试使用$ evalAsync,如果这不起作用,我会尝试$ timeout. (3认同)

Ben*_*son 5

我分叉你的小提琴. http://jsfiddle.net/xGCmp/7/

我添加了一个名为emit-when的指令.它需要两个参数.要发出的事件以及要发出的事件必须满足的条件.这是有效的,因为当在指令中执行链接函数时,我们知道该元素已在DOM中呈现.我的解决方案是在渲染ng-repeat中的最后一项时发出一个事件.

如果我们有一个全Angular解决方案,我不建议这样做.这有点hacky.但是,它可能是处理您提到的遗留代码类型的一种解决方案.

var myApp = angular.module('myApp', []);

myApp.controller("myController", ['$scope', function($scope){
    var resultsToLoad = [
        {id: 1, name: "one"},
        {id: 2, name: "two"},
        {id: 3, name: "three"}
    ];

    function doneAddingToDom() {
        console.log(document.getElementById('renderedList').children.length);
    }

    $scope.results = [];

    $scope.loadResults = function(){
        $scope.results = resultsToLoad;
        // If run doneAddingToDom here, we will find 0 list elements in the DOM. Check console.
        doneAddingToDom();
    }

    // If we run on doneAddingToDom here, we will find 3 list elements in the DOM.
    $scope.$on('allRendered', doneAddingToDom);
}]);

myApp.directive("emitWhen", function(){
    return {
        restrict: 'A',
        link: function(scope, element, attrs) {
            var params = scope.$eval(attrs.emitWhen),
                event = params.event,
                condition = params.condition;
            if(condition){
                scope.$emit(event);
            }
        }
    }
});

angular.bootstrap(document, ['myApp']);
Run Code Online (Sandbox Code Playgroud)