javascript angularJS:在指令加载之前等待模板被评估
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/19839260/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
angularJS: wait for template to be evaluated before directive loads
提问by basilikum
The Situation
情况
Lets say I have a directive, that has to access certain elements via ID, inside the element on which the directive is defined. The problem, that can occur, is that by the time the directive is evaluated, the child-elements are not yet. The result is, that I'm not able to access those elements by their ID.
假设我有一个指令,它必须通过 ID 访问定义指令的元素内的某些元素。可能发生的问题是,在评估指令时,子元素还没有。结果是,我无法通过它们的 ID 访问这些元素。
Example
例子
<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>
So my directive should just paint the background-color of the element with the id in $scope.elementId
. (Btw. I know I can handle this simple example much easier, it should just illustrate the general issue).
The problem is, that the ids of the elements inside ng-repeat are not there yet. As pointed out in the comment in the code, the id is still "{{ item.id }}". So angular didn't evaluate this part yet.
所以我的指令应该只用 id 绘制元素的背景颜色$scope.elementId
。(顺便说一句。我知道我可以更轻松地处理这个简单的例子,它应该只是说明一般问题)。问题是,ng-repeat 中元素的 id 还不存在。正如代码中的注释所指出的,id 仍然是“{{ item.id }}”。所以angular还没有评估这部分。
Question
问题
My obvious question is now: how can I make my directive to wait for descendent elements to be completely evaluated?
我现在明显的问题是:我怎样才能让我的指令等待后代元素被完全评估?
Further Explaination
进一步说明
In my real application I want to have a directive, that enables me to scroll to a certain elements on the page. I also use a pagination directive to split up the elements I want to show. Because of the pagination, only the elements that are really visible, are in the DOM, so the invisible elements are already filtered out in my controller.
在我的实际应用程序中,我想要一个指令,使我能够滚动到页面上的某些元素。我还使用分页指令来拆分我想要显示的元素。因为分页,DOM中只有真正可见的元素,所以不可见的元素已经在我的控制器中过滤掉了。
I also have a sidebar, where are small links to ALL the elements (not only the visible ones). When someone clicks on an element in the sidebar, two events should occur:
我还有一个侧边栏,其中有指向所有元素(不仅是可见元素)的小链接。当有人点击侧边栏中的元素时,应该发生两个事件:
- jump to the correct page
- scroll to the corrent element
- 跳转到正确的页面
- 滚动到相应的元素
When I jump to the page, I basically have the situation, I described above. I have a complete new list of elements, that have to be processed by ng-repeat. But directly after that, I try to tell my scroll-directive, that it should scroll the element with the ID "xy", but this ID is not assigned yet.
当我跳转到页面时,我基本上有我上面描述的情况。我有一个完整的新元素列表,必须由 ng-repeat 处理。但紧接着,我尝试告诉我的滚动指令,它应该滚动 ID 为“xy”的元素,但尚未分配此 ID。
回答by Prasad
Wrap your $scope.elementId = "Id1" with $timeout to notify angular to call listeners. (this can alternatively be done with $scope.$apply(), but it's causing another issue here)
用 $timeout 包裹你的 $scope.elementId = "Id1" 以通知 angular 调用侦听器。(这也可以用 $scope.$apply() 来完成,但这会导致另一个问题)
here is the jsfiddle link
Code is -
代码是——
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";
});
});
回答by basilikum
If finally ended up writing a getElementById
helper function, that returns a promise and has an internal interval, that check every 100ms if the element is present or not:
如果最终编写了一个getElementById
辅助函数,它返回一个 promise 并有一个内部间隔,每 100 毫秒检查一次元素是否存在:
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;
}
In my given example, I would use it like this:
在我给定的示例中,我会像这样使用它:
getElementById(id).then(function (element) {
$(element).css("background-color","red");
}, function (message) {
console.log(message);
});
It's still not my preferred solution, but it works and solves my problem for now. But I'm still curious, if there is any better approach to this.
它仍然不是我的首选解决方案,但它现在有效并解决了我的问题。但我仍然很好奇,是否有更好的方法来解决这个问题。
回答by manohar
As per Jim Hoskins article, the following snippet should help you.
根据 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))
});
Posting this answer to help people save some time(of course it's helpful to read the complete article)
发布此答案以帮助人们节省一些时间(当然阅读完整文章会有所帮助)
回答by Fals
You should complete the directive including a controller
option.
您应该完成包含controller
选项的指令。
controller: function ($scope){
$scope.items = [
{ id:"id1", name:"item1" },
{ id:"id2", name:"item2" }
];
}
This will create everything in the controller scope, and then you can access it from the controller of the view that uses this directive.
这将在控制器范围内创建所有内容,然后您可以从使用此指令的视图的控制器访问它。