Запустить код jQuery после того, как AngularJS завершает рендеринг HTML
В контроллере я получаю некоторые данные JSON, используя $http или $resource services. Затем я пишу эти данные в $scope и AngularJS обновляет структуру HTML страницы. Моя проблема в том, что мне нужно знать, что такое новый размер (ширина и высота) списка (я имею в виду элемент HTML DOM), который заполняется директивой Angular ng-repeat
. Следовательно, я должен запустить javascript-код сразу после завершения Angular, чтобы обновить структуру DOM. Каков правильный способ сделать это? Я искал интернет за последние четыре часа, но я не мог найти решение моей проблемы.
Вот как я получаю данные 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());
}
Я знаю, что для решения этой проблемы должно быть что-то легкое, но я не могу просто найти ее сейчас.
Спасибо заранее.
Ответы
Ответ 1
На самом деле в этом случае способ angular - это не простой способ, но единственный правильный путь:)
Вам нужно написать директиву и прикрепить элемент, который вы хотите узнать о высоте. А из контроллера вы передаете событие, директива поймает событие и там вы сможете выполнять манипуляции с 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);
})
}
};
}]);
Ответ 2
Ответ 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 вызывается каждый раз, когда данные изменяются без каких-либо особых событий.
Ответ 3
Другое предложение работать с 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 директивы grid:
<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>
Ответ 4
Я хотел бы добавить еще один ответ, так как в предыдущих ответах он понимает, что код, необходимый для запуска после выполнения ngRepeat, представляет собой код angular, который в этом случае все ответы выше дают большое и простое решение, некоторые более общий, чем другие, и в случае его важного этапа жизненного цикла дайджеста вы можете взглянуть на блог Ben Nadel об этом, за исключением использования $parse вместо $eval.
Но по моему опыту, как утверждает OP, в нем обычно запускаются некоторые плагины или методы jQuery на компилируемом DOM, который в этом случае я обнаружил, что самым простым решением является создание директивы с setTimeout
, так как функция setTimeout
попадает в конец очереди браузера, ее всегда справа, когда все делается в angular, обычно ng-repeat
, которая продолжается после того, как родительская функция postLinking
angular.module('myApp', [])
.directive('pluginNameOrWhatever', function() {
return function(scope, element, attrs) {
setTimeout(function doWork(){
//jquery code and plugins
}, 0);
};
});
Для тех, кто задается вопросом, почему не использовать $timeout, его то, что он вызывает другой цикл дайджеста, совершенно не нужно.
EDIT:
Thanx to drzaus для ссылки на способ использования $timeout без вызывания дайджест http://www.codelord.net/2015/10/14/angular-nitpicking-differences-between-timeout-and-settimeout/