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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-27 21:08:04  来源:igfitidea点击:

AngularJS ngIf prevents finding element inside directive

javascriptangularjs

提问by Jacopo

I have an AngularJS directive that includes an ngIfand I would like to modify some of the DOM inside the ngIfin the directive link function. Unfortunately it seems that ngIfprevents 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.findcall in the link function returns an empty array but as soon as I remove the ngIffrom 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 columndirective 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 columndirective scope and has always access to the selectelement because it's inside its DOM tree. I pay the cost of an extra directive but I don't have to use the $timeouthack. I created a new jsFiddle to illustrate the solution http://jsfiddle.net/dedalusj/nx3vX/1/

form 指令没有它自己的作用域,所以它有效地在column指令作用域之外运行,并且总是可以访问select元素,因为它在它的 DOM 树内。我支付额外指令的费用,但我不必使用$timeouthack。我创建了一个新的 jsFiddle 来说明解决方案http://jsfiddle.net/dedalusj/nx3vX/1/

Thanks @Michael but I can't simply use the ng-optionbecause the typesarray 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 ngIfdirective works by using Angular's transclusion feature. What happens during the compile/link cycle is:

ngIf指令通过使用 Angular 的嵌入功能来工作。在编译/链接周期中发生的事情是:

  1. The content inside the ngIfis removed from the DOM when it is compiled
  2. Angular runs the link functions. The ngIf's link function is run before the link function of the directive using it. When ngIf's link function runs, it uses $scope.$watch()to watch the value of the ng-ifattribute.
  3. Your directive's link function runs, at this point the content of the ngIfis not part of the DOM
  4. The watch set up in step (2) is called, and ngIfwill then call the $transcludefunction to insert the contents of the ngIfinto the DOM if the ng-ifattribute value is truthy.
  5. Any watch functions, $timeoutcalls or use of $scope.$evalAsyncthat you registered in your directive's link function will run.
  1. 里面的内容在ngIf编译时从DOM中移除
  2. Angular 运行链接功能。本ngIf的链接功能正在使用它的指令链接功能之前运行。当ngIf的链接函数运行时,它用于$scope.$watch()监视ng-if属性的值。
  3. 您的指令的链接功能运行,此时 的内容ngIf不是 DOM 的一部分
  4. 调用步骤(2)中设置的watch,如果属性值为true ,ngIf则会调用$transclude函数将 的内容插入ngIf到DOM中ng-if
  5. 您在指令的链接函数中注册的任何监视函数、$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, $timeoutor $scope.$evalAsyncin 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, ngIfremoves 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 $timeoutwith a 0 interval in order to wait for Angular to update the DOM.

这里的技巧是find()通过使用$timeout0 间隔来延迟调用,以等待 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>

jsFiddle

js小提琴

Now you don't need to worry about finding the selectelement 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.

干杯。