繁体   English   中英

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

[英]Run jQuery code after AngularJS completes rendering HTML

在控制器中,我使用$ http或$ resource服务获取一些JSON数据。 然后我在$ scope中写这个数据,AngularJS更新页面的HTML结构。 我的问题是我需要知道填充Angular ng-repeat指令的列表的新大小(宽度和高度)是什么(我的意思是,HTML DOM元素)。 因此,我必须在Angular完成更新DOM结构后立即运行javascript代码。 这样做的正确方法是什么? 我在过去四个小时内搜索了互联网,但我找不到解决问题的方法。

这是我收到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

});

这就是init()函数中发生的事情:

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

我知道必须有一些容易解决这个问题的东西,但我现在不能找到它。 提前致谢。

实际上在这种情况下,角度方式不是简单的方法,但唯一正确的方式:)

你必须写一个指令并附加到你想知道高度的元素。 从控制器你广播一个事件,指令将捕获事件,你可以在那里进行DOM操作。 永远不要在控制器中。

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的答案很好,但有一个问题:如果您忘记播放该事件,您的javascript将无法运行,而您的数据可能已更改。 另一种解决方案是观察范围的变化,例如:

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>

这样, 每次数据更改时都会调用javascript函数,而不需要自定义事件。

使用JQuery的另一个建议。 不得不通过一个指令生成的网格来解决这个问题。 我想滚动到网格中的特定行。 使用$ emit从指令广播到父控制器:

在控制器中:

    ['$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);
        }

在指令中

.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');
            }]


        };
    }])

这是网格指令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>

列表和所选参数是从使用该指令的html注入的

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

我想补充一点,因为前面的答案认为在完成ngRepeat之后运行的代码是一个角度代码,在这种情况下,上面的所有答案都提供了一个非常简单的解决方案,一些比其他更通用,如果它是重要的摘要生命周期阶段你可以看看Ben Nadel的博客,除了使用$ parse而不是$ eval。

但根据我的经验,正如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);        
  };
});

对于谁想知道为什么不使用$ timeout,它会导致另一个完全不必要的摘要周期。

编辑:

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