简体   繁体   English

在AngularJS完成呈现HTML之后运行jQuery代码

[英]Run jQuery code after AngularJS completes rendering HTML

In controller I get some JSON data using $http or $resource services. 在控制器中,我使用$ http或$ resource服务获取一些JSON数据。 Then I write this data in $scope and AngularJS updates HTML structure of the page. 然后我在$ scope中写这个数据,AngularJS更新页面的HTML结构。 My problem is that I need to know what is the new size (width and height) of the list (I mean, HTML DOM element) that is filled with Angular ng-repeat directive. 我的问题是我需要知道填充Angular ng-repeat指令的列表的新大小(宽度和高度)是什么(我的意思是,HTML DOM元素)。 Consequently, I have to run javascript code right after Angular finishes updating DOM structure. 因此,我必须在Angular完成更新DOM结构后立即运行javascript代码。 What is the proper way to do it? 这样做的正确方法是什么? I have searched internet over the last four hours but I couldn't find any solution to my problem. 我在过去四个小时内搜索了互联网,但我找不到解决问题的方法。

This is how I receive JSON data: 这是我收到JSON数据的方式:

var tradesInfo = TradesInfo.get({}, function(data){
    console.log(data);
    $scope.source.profile = data.profile;
            $scope.trades = $scope.source.profile.trades;
        $scope.activetrade = $scope.trades[0];
        $scope.ready = true;


    init();  //I need to call this function after update is complete

});

And this is what happens in init() function: 这就是init()函数中发生的事情:

function init(){
    alert($('#wrapper').width());
    alert($('#wrapper').height());
}

I know that there must be something easy to solve this problem but I can't just find it now. 我知道必须有一些容易解决这个问题的东西,但我现在不能找到它。 Thanks in advance. 提前致谢。

Actually in this case the angular way is not the easy way but the only right way :) 实际上在这种情况下,角度方式不是简单的方法,但唯一正确的方式:)

You have to write a directive and attach to the element you want to know the height of. 你必须写一个指令并附加到你想知道高度的元素。 And from the controller you $broadcast an event, the directive'll catch the event and there you can do the DOM manipulation. 从控制器你广播一个事件,指令将捕获事件,你可以在那里进行DOM操作。 NEVER in the controller. 永远不要在控制器中。

var tradesInfo = TradesInfo.get({}, function(data){
    console.log(data);
    $scope.source.profile = data.profile;
    ...

    $scope.$broadcast('dataloaded');
});


directive('heightStuff', ['$timeout', function ($timeout) {
    return {
        link: function ($scope, element, attrs) {
            $scope.$on('dataloaded', function () {
                $timeout(function () { // You might need this timeout to be sure its run after DOM render.
                    element.width()
                    element.height()
                }, 0, false);
            })
        }
    };
}]);

Olivér's answer is good, but has an issue: if you forget to broadcast the event, your javascript will not run whereas your data might have changed. Olivér的答案很好,但有一个问题:如果您忘记播放该事件,您的javascript将无法运行,而您的数据可能已更改。 Another solution would be to watch for changes on the scope, for instance: 另一种解决方案是观察范围的变化,例如:

var tradesInfo = TradesInfo.get({}, function(data) {
  console.log(data);
  $scope.profile = data.profile;
  // ...
});


directive('heightStuff', ['$timeout',
  function($timeout) {
    return {
      scope: {
        myData: '='
      },
      link: function($scope, element, attrs) {
        $scope.$watch('myData', function() {
          $timeout(function() { // You might need this timeout to be sure its run after DOM render.
            element.width()
            element.height()
          }, 0, false);
        })
      }
    };
  }
]);
<div height-stuff my-data="profile"></div>

This way the javascript functions are called every time the data changes without any need for a custom event. 这样, 每次数据更改时都会调用javascript函数,而不需要自定义事件。

Another suggestion to work with JQuery. 使用JQuery的另一个建议。 Had to work this through for a grid that was generated in a directive. 不得不通过一个指令生成的网格来解决这个问题。 I wanted to scroll to a specific row in the grid. 我想滚动到网格中的特定行。 Use $emit to broadcast from directive to parent controller: 使用$ emit从指令广播到父控制器:

In Controller: 在控制器中:

    ['$timeout',function($timeout){
...
 $scope.$on('dataloaded', function () {
            $timeout(function () { // You might need this timeout to be sure its run after DOM render.
                $scope.scrollToPosition();
            }, 0, false);
        });
        $scope.scrollToPosition = function () {
            var rowpos = $('#row_' + $scope.selectedActionID, "#runGrid").position();
            var tablepost = $('table', "#runGrid").position();
            $('#runGrid').scrollTop(rowpos.top - tablepost.top);
        }

In directive 在指令中

.directive('runGrid',['$timeout', function ($timeout) {
        // This directive generates the grip of data
        return {
            restrict: 'E',  //DOM Element
            scope: {    //define isolated scope
                list: '=',   //use the parent object
                selected: "="
            },

            templateUrl: '/CampaignFlow/StaticContent/Runs/run.grid.0.0.0.0.htm',  //HTML template URL

            controller: ['$scope', function ($scope) {  //the directive private controller, whith its private scope
                //$scope.statusList = [{ data_1: 11, data_2: 12 }, { data_1: 21, data_2: 22 }, { data_1: 31, data_2: 32 }];
                //Controller contains sort functionallity

                $scope.sort = { column: null, direction: 1 }
                $scope.column = null;
                $scope.direction = "asc";
                $scope.sortColumn = function (id) {
                    if(id!=$scope.column) {
                        $scope.column = id;
                        $scope.direction = "asc";
                    } else {
                        $scope.column = null;
                    }
                }
                $scope.toggleDir = function () {
                    $scope.direction = ($scope.direction == "asc") ? "desc" : "asc";
                }
               $scope.$emit('dataloaded');
            }]


        };
    }])

And this is a snippet of the grid directive html template: 这是网格指令html模板的片段:

 <div style="overflow-y:auto;height: 200px;" id="runGrid">
            <table class="table table-striped" style="table-layout:fixed">
           <tbody>
                <tr  ng-repeat="status in list" id="row_{{status.action_id}}" ng-class="(status.action_id==selected)?'selected':''">
                    <td>

the list and selected parameters are injected from the html that is using the directive 列表和所选参数是从使用该指令的html注入的

<run-grid list="list" selected="selectedActionID"></run-grid>

I would like to add another answer, since the preceding answers takes it that the code needed to run after the ngRepeat is done is an angular code, which in that case all answers above give a great and simple solution, some more generic than others, and in case its important the digest life cycle stage you can take a look at Ben Nadel's blog about it, with the exception of using $parse instead of $eval. 我想补充一点,因为前面的答案认为在完成ngRepeat之后运行的代码是一个角度代码,在这种情况下,上面的所有答案都提供了一个非常简单的解决方案,一些比其他更通用,如果它是重要的摘要生命周期阶段你可以看看Ben Nadel的博客,除了使用$ parse而不是$ eval。

But in my experience, as the OP states, it is usually running some jQuery plugins or methods on the finally compiled DOM, which in that case I found that the most simple solution is to create a directive with a setTimeout , since the setTimeout function gets pushed to the end of the queue of the browser, its always right after everything is done in angular, usually ng-repeat which continues after it's parents postLinking function 但根据我的经验,正如OP所说,它通常在最终编译的DOM上运行一些jQuery插件或方法,在这种情况下,我发现最简单的解决方案是使用setTimeout创建一个指令,因为setTimeout函数得到推到浏览器队列的末尾,它总是在一切都以角度完成之后,通常是ng-repeat ,它继续在父亲postLinking函数之后

angular.module('myApp', [])
.directive('pluginNameOrWhatever', function() {
  return function(scope, element, attrs) {        
    setTimeout(function doWork(){
      //jquery code and plugins
    }, 0);        
  };
});

For whoever wondering why not to use $timeout, its that it causes another digest cycle that is completely unnecessary. 对于谁想知道为什么不使用$ timeout,它会导致另一个完全不必要的摘要周期。

EDIT: 编辑:

Thanx to drzaus for the link to how to use $timeout without causing digest http://www.codelord.net/2015/10/14/angular-nitpicking-differences-between-timeout-and-settimeout/ Thanx到drzaus链接到如何使用$ timeout而不引起摘要http://www.codelord.net/2015/10/14/angular-nitpicking-differences-between-timeout-and-settimeout/

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM