Javascript 在AngularJS完成呈现HTML后运行jQuery代码
在controller中,我使用$http或$resource服务获取一些JSON数据。然后我在$scope中写入这些数据,AngularJS更新页面的HTML结构。我的问题是,我需要知道列表(我的意思是HTML DOM元素)的新大小(宽度和高度)是多少,它填充了AngularJavascript 在AngularJS完成呈现HTML后运行jQuery代码,javascript,jquery,angularjs,Javascript,Jquery,Angularjs,在controller中,我使用$http或$resource服务获取一些JSON数据。然后我在$scope中写入这些数据,AngularJS更新页面的HTML结构。我的问题是,我需要知道列表(我的意思是HTML DOM元素)的新大小(宽度和高度)是多少,它填充了Angularng repeat指令。因此,我必须在Angular完成DOM结构更新后立即运行javascript代码。正确的方法是什么?在过去的四个小时里,我一直在网上搜索,但我找不到任何解决问题的方法 这就是我接收JSON数据的方式
ng repeat
指令。因此,我必须在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);
})
}
};
}]);
另一个使用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);
}
在控制器中:
['$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');
}]
};
}])
<run-grid list="list" selected="selectedActionID"></run-grid>
这是grid指令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中注入的
.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');
}]
};
}])
<run-grid list="list" selected="selectedActionID"></run-grid>
Olivér的回答很好,但有一个问题:如果忘记广播事件,javascript将无法运行,而数据可能已经更改。
另一个解决方案是观察范围的变化,例如:
var tradesInfo=tradesInfo.get({},函数(数据){
控制台日志(数据);
$scope.profile=data.profile;
// ...
});
指令('heightStuff',['$timeout',
函数($timeout){
返回{
范围:{
myData:“=”
},
链接:函数($scope,element,attrs){
$scope.$watch('myData',function()){
$timeout(function(){//您可能需要此超时以确保其在DOM呈现后运行。
元素宽度()
元素。高度()
},0,假);
})
}
};
}
]);
这样,每次数据更改时都会调用javascript函数,而不需要任何自定义事件。我想添加另一个答案,因为前面的答案认为在完成ngRepeat后需要运行的代码是一个角度代码,在这种情况下,上面的所有答案都给出了一个非常简单的解决方案,有些比其他更通用,如果在摘要生命周期阶段很重要,你可以看看Ben Nadel的博客,除了使用$parse而不是$eval 但根据我的经验,正如OP所说,它通常在最终编译的DOM上运行一些jQuery插件或方法,在这种情况下,我发现最简单的解决方案是创建一个带有
setTimeout
的指令,因为setTimeout
函数被推到浏览器队列的末尾,它总是在所有事情都以角度完成之后,通常是ng repeat
,在它的父级后链接函数之后继续
angular.module('myApp', [])
.directive('pluginNameOrWhatever', function() {
return function(scope, element, attrs) {
setTimeout(function doWork(){
//jquery code and plugins
}, 0);
};
});
对于那些想知道为什么不使用$timeout的人来说,这是因为它会导致另一个完全不必要的摘要循环
编辑:
Thanx到drzaus,获取如何使用$timeout而不引起摘要的链接您不能。同样简单。可能会有一个意外的$compile调用,任意数量的指令,它们自己操作DOM,并在$timeout之后设置要执行的操作。。。AngularJS系统的架构非常漂亮,因此各个部分的行为都是独立的,这是一个很小的代价。找另一条路吧。我想无论如何都有可能做到。我的意思是,dom及其指令可以调用编译函数或执行其他不可预测的操作这一事实并不能阻止它,imho。很抱歉让这个老问题变成现实,但你能分享#wrapper的内容吗?你可能只是循环交易?非常感谢。它起作用了。你能解释一下为什么需要$0ms超时吗?我问这个问题是因为我试图忽略它,在这个例子中,宽度和高度是不正确的。带超时功能的Javascript行为是什么?它是否在另一个线程中运行代码?不,实际上没有其他线程,这就是为什么浏览器会这样做。它们仅在闭包的和处运行dom回流,因此,如果需要元素的维度,则无法获得它们,因为它们尚未渲染。您必须延迟测量例程的执行,这就是setTimeout与0的关系。它会告诉浏览器:在执行当前关闭并已注册超时后运行我。非常基本的例子:这是不正确的。当您尝试访问相关的样式属性时,浏览器将立即重新渲染块-因此在一般情况下,在这种情况下您不需要调用setTimeout-请参阅演示。但是AngularJS的工作方式是另一种——当您更改模型时,AngularJS不会立即更新DOM,而不是browserI。我认为您需要调用
$scope.$on(…)
,而不是$scope.on(…)
(缺少一个美元符号)。确保广播事件的大小写正确匹配('dataLoaded'!='dataLoaded'))应该使用angular的,我已经在最后一行写了为什么我不想使用$timeout,请再读一遍。奇怪,不知道为什么我没有看到。但是,如果您不想使用摘要循环,那么只需使用invokeApply=false
调用即可。否则,就需要使用Angular开发者编写的内容来替换setTimeout
。