javascript AngularJS ngIf 阻止在指令中查找元素
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/21541276/
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 ngIf prevents finding element inside directive
提问by Jacopo
I have an AngularJS directive that includes an ngIf
and I would like to modify some of the DOM inside the ngIf
in the directive link function. Unfortunately it seems that ngIf
prevents me from finding DOM elements within it in the link function.
我有一个包含 的 AngularJS 指令,ngIf
我想修改ngIf
指令链接函数中的一些 DOM 。不幸的是,这似乎ngIf
阻止了我在链接函数中找到其中的 DOM 元素。
Here is the code for the directive:
这是指令的代码:
directive('column', function () {
return {
templateUrl: 'views/column.html',
restrict: 'E',
scope: {
column: '='
},
controller: ['$scope', function ($scope) {
$scope.editing = true;
$scope.toggleEditing = function () {
$scope.editing = !$scope.editing;
};
}],
link: function postLink(scope, element) {
var select = element.find('select');
console.log(select); // See if it can find the select element
// var types = scope.column.types();
// add types as options to the select element
}
};
});
And here is the simplified html of the directive:
这是指令的简化 html:
<div class="column">
<div>{{ column.title }}</div>
<form name="columnForm" role="form" ng-if="editing">
<select></select>
</form>
</div>
Here is the link to the jsFiddle example http://jsfiddle.net/dedalusj/Y49Xx/1/
这是 jsFiddle 示例的链接http://jsfiddle.net/dedalusj/Y49Xx/1/
The element.find
call in the link function returns an empty array but as soon as I remove the ngIf
from the form it returns the proper select DOM element. I have the feeling that I'm doing this the wrong way.
element.find
链接函数中的调用返回一个空数组,但是一旦我ngIf
从表单中删除它,它就会返回正确的 select DOM 元素。我有一种感觉,我这样做是错误的。
UPDATE
更新
Thanks for the answers but I found another solution. I simply created another directive that encapsulate the form, added it to the column
directive template with ng-if="editing"
.
感谢您的回答,但我找到了另一个解决方案。我只是创建了另一个封装表单的column
指令,将它添加到带有ng-if="editing"
.
The form directive doesn't have it's own scope so it effectively operates out of the column
directive scope and has always access to the select
element because it's inside its DOM tree. I pay the cost of an extra directive but I don't have to use the $timeout
hack. I created a new jsFiddle to illustrate the solution http://jsfiddle.net/dedalusj/nx3vX/1/
form 指令没有它自己的作用域,所以它有效地在column
指令作用域之外运行,并且总是可以访问select
元素,因为它在它的 DOM 树内。我支付额外指令的费用,但我不必使用$timeout
hack。我创建了一个新的 jsFiddle 来说明解决方案http://jsfiddle.net/dedalusj/nx3vX/1/
Thanks @Michael but I can't simply use the ng-option
because the types
array comes from an XML file and its elements are other angular.element objects which cannot be inserted easily with ng-option
.
谢谢@Michael,但我不能简单地使用 ,ng-option
因为该types
数组来自一个 XML 文件,并且它的元素是其他 angular.element 对象,无法轻松插入ng-option
.
回答by Robert Knight
The ngIf
directive works by using Angular's transclusion feature. What happens during the compile/link cycle is:
该ngIf
指令通过使用 Angular 的嵌入功能来工作。在编译/链接周期中发生的事情是:
- The content inside the
ngIf
is removed from the DOM when it is compiled - Angular runs the link functions. The
ngIf
's link function is run before the link function of the directive using it. WhenngIf
's link function runs, it uses$scope.$watch()
to watch the value of theng-if
attribute. - Your directive's link function runs, at this point the content of the
ngIf
is not part of the DOM - The watch set up in step (2) is called, and
ngIf
will then call the$transclude
function to insert the contents of thengIf
into the DOM if theng-if
attribute value is truthy. - Any watch functions,
$timeout
calls or use of$scope.$evalAsync
that you registered in your directive's link function will run.
- 里面的内容在
ngIf
编译时从DOM中移除 - Angular 运行链接功能。本
ngIf
的链接功能正在使用它的指令链接功能之前运行。当ngIf
的链接函数运行时,它用于$scope.$watch()
监视ng-if
属性的值。 - 您的指令的链接功能运行,此时 的内容
ngIf
不是 DOM 的一部分 - 调用步骤(2)中设置的watch,如果属性值为true ,
ngIf
则会调用$transclude
函数将 的内容插入ngIf
到DOM中ng-if
。 - 您在指令的链接函数中注册的任何监视函数、
$timeout
调用或使用都$scope.$evalAsync
将运行。
So if you want to access elements inside the ngIf
's content, the code needs to run afterstep 4 above. This means that any functions registered with $scope.$watch
, $timeout
or $scope.$evalAsync
in your directive's link function will work. For a one-time piece of setup code, I would probably opt for $scope.$evalAsync
:
因此,如果要访问 的内容中的元素ngIf
,则代码需要在上述第 4 步之后运行。这意味着使用$scope.$watch
,$timeout
或$scope.$evalAsync
在您的指令的链接函数中注册的任何函数都将起作用。对于一次性设置代码,我可能会选择$scope.$evalAsync
:
angular.directive('yourDirective', function () {
return {
...
link: function(scope, elem) {
scope.$evalAsync(function () {
// code that runs after conditional content
// with ng-if has been added to DOM, if the ng-if
// is enabled
});
}
};
});
回答by Michael Benford
As @moderndegree has said, ngIf
removes the element it's applied to from the DOM, so you won't be able to find it when it's not there. But, you could write your directive in a way to workaround that:
正如@moderndegree 所说,ngIf
从 DOM 中删除它所应用的元素,因此当它不存在时您将无法找到它。但是,您可以编写指令来解决该问题:
controller: function ($scope, $element, $timeout) {
$scope.toggleEditing = function () {
$scope.editing = !$scope.editing;
$timeout(function() {
var select = $element.find('select');
select.append('<option>Value 1</option>')
.append('<option>Value 2</option>')
.append('<option>Value 3</option>');
});
};
}
Updated jsFiddle here.
在这里更新了 jsFiddle 。
The trick here is to delay the find()
call by using $timeout
with a 0 interval in order to wait for Angular to update the DOM.
这里的技巧是find()
通过使用$timeout
0 间隔来延迟调用,以等待 Angular 更新 DOM。
UPDATE
更新
After giving some more thought to your code, I realize that perhaps you can let Angular do the hard work for you:
在对您的代码进行更多思考之后,我意识到也许您可以让 Angular 为您完成繁重的工作:
Javascript
Javascript
directive('column', function () {
return {
templateUrl: 'views/column.html',
restrict: 'E',
scope: {
column: '='
},
controller: ['$scope', function ($scope) {
$scope.editing = true;
$scope.toggleEditing = function () {
$scope.editing = !$scope.editing;
};
}],
};
});
HTML
HTML
<div class="column">
<div>{{ column.title }}</div>
<form name="columnForm" role="form" ng-if="editing">
<select ng-model="type" ng-options="type for type in column.types"></select>
</form>
</div>
Now you don't need to worry about finding the select
element at the right time and populating it. Angular does all of that for you. :)
现在您无需担心select
在正确的时间找到元素并填充它。Angular 会为您完成所有这些。:)
回答by Arthur Shpakov
You can put your code from the link function inside $timeout.
您可以将链接函数中的代码放在 $timeout 中。
$timeout(function(){
var select = element.find('select');
console.log(select);
});
Don't forget to inject $timeout in your directive
不要忘记在您的指令中注入 $timeout
directive('column', function ($timeout) {
回答by Marcosocon
I was facing this same issue and i was able to resolve it using ng-show, this prevents this issue because ngIf removes the element it's applied to the DOM, so you won't be able to find it when it's not there.
我遇到了同样的问题,我能够使用 ng-show 解决它,这可以防止这个问题,因为 ngIf 删除了它应用于 DOM 的元素,所以当它不存在时你将无法找到它。
so in your case:
所以在你的情况下:
<div class="column">
<div>{{ column.title }}</div>
<form name="columnForm" role="form" ng-show="editing">
<select></select>
</form>
will work OK.
会正常工作。
Cheers.
干杯。