【发布时间】:2013-11-14 12:16:51
【问题描述】:
我正在使用 ng-if 来显示和隐藏一个元素。当元素出现时,我想调用一个服务,它在新元素中滚动到某个子元素(按 Id)。问题是,如果我尝试在将元素设置为可见后立即调用我的服务函数,则 DOM 似乎还没有准备好。
var myApp = angular.module('myApp',[]);
myApp.factory("ScrollService", function () {
return {
scroll: function (id) {
console.log(document.getElementById(id));
}
};
});
function MyCtrl($scope, ScrollService) {
$scope.visible = false;
$scope.toggleVisibility = function () {
$scope.visible = !$scope.visible;
if ($scope.visible) {
ScrollService.scroll("myId"); //output: null
}
};
}
document.getElementById() 将始终导致 null。
这也是一个小提琴,它演示了问题:http://jsfiddle.net/Dpuq2/
那么有什么方法可以在被 ng-if 操作后,在 DOM 准备好后立即触发函数?
编辑
使用 MinkoGechev 的小提琴,我能够在更真实的环境中重现我的错误,并使用指令而不是服务:FIDDLE
问题似乎出现了,因为我在ng-if-container 内部使用了ng-repeat:
<div ng-controller="MyCtrl">
<div ng-if="visible">
<div id="myId" data-scroll="itemId">
<div id="xy"></div>
<div ng-repeat="item in items" id="{{ item.number }}">{{ item.number }}</div>
</div>
</div>
<button ng-click="toggleVisibility()">toggle</button>
</div>
这里是相应的指令加上控制器:
var myApp = angular.module('myApp',[]);
myApp.directive("scroll", function () {
return {
scope: {
scroll: '='
},
link: function (scope) {
scope.$watch('scroll', function (v) {
console.log(v, document.getElementById(scope.scroll));
});
},
transclude: true,
template: "<div ng-transclude></div>"
};
});
function MyCtrl($scope) {
$scope.visible = false;
$scope.itemId = "";
$scope.items = [];
for (var i = 1; i < 10; i++) {
$scope.items.push({
number: i,
text: "content " + i
});
}
$scope.toggleVisibility = function () {
$scope.visible = !$scope.visible;
if ($scope.visible) {
$scope.itemId = "3";
}
};
}
因此,一旦我切换容器的可见性,我就会设置要滚动到的元素的 Id:
$scope.itemId = "3"
如果我使用从 1 到 10 的数字之一(由 ng-repeat 创建的元素的 ID),它将失败。如果我使用“xy”(与 ng-repeat 元素相邻的一个元素的 ID),它会成功。
【问题讨论】:
-
请不要在控制器中实现 DOM,而是使用指令
-
@MaximShoustin 我没有将它实现到控制器中,而是实现到服务中。我也尝试使用指令,结果相同。
-
@basilikum 在指令的
link()函数中,您确定链接到该指令的元素的DOM 已准备就绪。在这里,您必须将元素滚动到他的孩子。 -
@Blackhole 也是一个不错的建议,但它似乎还没有完全准备好,因为 ng-repeat 中的元素尚不存在(请参阅更新的问题)。
标签: javascript angularjs dom