[英]angularJS: wait for template to be evaluated before directive loads
情况
假设我有一个指令,它必须通过ID在定义指令的元素内访问某些元素。 可能出现的问题是,在评估指令时,子元素还没有。 结果是,我无法通过其ID访问这些元素。
例
<div ng-controller="MyCtrl">
<div color="elementId">
<div ng-repeat="item in items" id="{{ item.id }}">
{{ item.name }}
</div>
</div>
</div>
<script>
var myApp = angular.module('myApp',[]);
myApp.directive("color", function () {
return {
restrict: "A",
link: function (scope, element, attributes) {
var name = attributes.color,
el = element[0];
scope.$watch(name, function () {
var id = scope[name];
console.log(id); //id1
console.log(element.children().eq(0).attr("id")); //{{ item.id }}
element.find("#"+id).css("background-color","red");
});
}
};
});
function MyCtrl($scope) {
$scope.items = [
{ id:"id1", name:"item1" },
{ id:"id2", name:"item2" }
];
$scope.elementId="id1";
}
</script>
因此,我的指令应该只使用$scope.elementId
的id $scope.elementId
元素的背景颜色。 (顺便说一句。我知道我可以更轻松地处理这个简单的例子,它应该只是说明一般问题)。 问题是,ng-repeat中的元素的id还没有。 正如代码中的注释所指出的那样,id仍然是“{{item.id}}”。 因此,角度尚未评估此部分。
题
我现在明显的问题是:如何让我的指令等待后代元素被完全评估?
进一步解释
在我的实际应用程序中,我希望有一个指令,使我能够滚动到页面上的某些元素。 我还使用分页指令来分割我想要显示的元素。 由于分页,只有真正可见的元素在DOM中,因此隐形元素已经在我的控制器中被过滤掉了。
我还有一个侧边栏,其中有所有元素的小链接(不仅是可见元素)。 当有人点击侧边栏中的元素时,应该发生两个事件:
当我跳到页面时,我基本上都有这种情况,我在上面描述过。 我有一个完整的新元素列表,必须由ng-repeat处理。 但是在那之后,我尝试告诉我的scroll-directive,它应该使用ID“xy”滚动元素,但是这个ID还没有分配。
用$ timeout包装$ scope.elementId =“Id1”以通知angular调用侦听器。 (也可以使用$ scope。$ apply()来完成,但这会导致另一个问题)
代码是 -
var myApp = angular.module('myApp',[]);
myApp.directive("color", ['$timeout', function ($timeout) {
return {
restrict: "A",
link: function (scope, element, attributes) {
console.log(element)
var name = attributes.color,
el = element[0];
scope.$watch(name, function () {
var id = scope[name];
console.log(id); //id1
console.log(element.find("#"+id)); //{{ item.id }}
element.find("#"+id).css("background-color","red");
});
}
};
}]);
myApp.controller("MyCtrl", function($scope, $timeout) {
$scope.items = [
{ id:"id1", name:"item1" },
{ id:"id2", name:"item2" }
];
$timeout(function() {
$scope.elementId="id1";
});
});
如果最终编写了一个getElementById
辅助函数,它返回一个promise并有一个内部间隔,如果该元素存在与否则每100ms检查一次:
function getElementById(elementId) {
var deferred = $q.defer(),
intervalKey,
counter = 0,
maxIterations = 50;
intervalKey = setInterval(function () {
var element = document.getElementById(elementId);
if (element) {
deferred.resolve(element);
clearInterval(intervalKey);
} else if (counter >= maxIterations) {
deferred.reject("no element found");
clearInterval(intervalKey);
}
counter++;
}, 100);
return deferred.promise;
}
在我给出的例子中,我会像这样使用它:
getElementById(id).then(function (element) {
$(element).css("background-color","red");
}, function (message) {
console.log(message);
});
它仍然不是我的首选解决方案,但它现在可以解决我的问题。 但我仍然很好奇,如果有更好的方法。
根据Jim Hoskins的文章,以下代码段可以帮助您。
scope.$watch(name, function () {
setTimeout(function () {
scope.$apply(function () {
var id = scope[name];
console.log(id); //id1
console.log(element.find("#"+id)); //{{ item.id }}
element.find("#"+id).css("background-color","red");
}
}, 200))
});
发布此答案可帮助人们节省一些时间(当然,阅读完整文章会很有帮助)
您应该完成指令,包括controller
选项。
controller: function ($scope){
$scope.items = [
{ id:"id1", name:"item1" },
{ id:"id2", name:"item2" }
];
}
这将在控制器范围内创建所有内容,然后您可以从使用此指令的视图的控制器访问它。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.