AngularJS NGIF阻止在指令内找到元素



我有一个包括ngIf的AngularJS指令,我想在指令链接函数中修改ngIf内部的某些DOM。不幸的是,ngIf似乎阻止了我在链接函数中找到其中的DOM元素。

这是指令的代码:

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
      }
    };
  });

这是指令的简化HTML:

<div class="column">
    <div>{{ column.title }}</div>
    <form name="columnForm" role="form" ng-if="editing">
        <select></select>
    </form>
</div>

这是JSFIDDLE示例的链接http://jsfiddle.net/dedalusj/y49xx/1/

链接功能中的element.find调用将返回一个空数组,但是一旦我从返回正确的选择DOM元素的表单中删除ngIf。我有一个错误的方式。

update

感谢您的答案,但我找到了另一个解决方案。我只是创建了另一个封装表单的指令,将其添加到使用ng-if="editing"column指令模板中。

形式指令没有自己的范围,因此它有效地从column指令范围内运行,并且始终访问select元素,因为它在DOM树内。我支付了额外指令的费用,但我不必使用$timeout黑客。我创建了一个新的JSFiddle来说明解决方案http://jsfiddle.net/dedalusj/nx3vx/1/

谢谢@michael,但我不能简单地使用ng-option,因为types数组来自XML文件,其元素是其他Angular.Element对象。元素对象无法轻松使用ng-option插入。

使用Angular的trandclusion功能,ngIf指令作品。编译/链接周期中发生的情况是:

  1. ngIf的内容被编译后从DOM中删除
  2. Angular运行链接函数。ngIf的链接功能是在运行之前的使用该指令的链接功能。当ngIf的链接功能运行,它使用$scope.$watch()观察ng-if的值属性。
  3. 您的指令的链接函数运行,此时ngIf的内容不属于DOM
  4. 调用步骤(2)中设置的手表,然后ngIf将调用$transclude函数,以将ngIf的内容插入DOM中,如果ng-if属性值是诚实的。
  5. 您在指令的链接功能中注册的任何手表功能,$timeout调用或使用$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
      });
    }
  };
});

正如@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>');
    });            
  };
}

在此处更新了JSFIDDLE。

这里的诀窍是通过使用0间隔使用$timeout延迟find()调用,以便等待Angular更新DOM。

update

在对您的代码进行了更多思考之后,我意识到也许您可以让Angular为您做艰苦的工作:

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

<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

现在,您不必担心在正确的时间找到select元素并填充它。Angular为您做所有这些。:)

您可以从$ timeout中的链接函数中放置代码。

$timeout(function(){
     var select = element.find('select');
     console.log(select);
});

不要忘记将$超时注入您的指令

directive('column', function ($timeout) {

我面临着同样的问题,我能够使用ng-show解决它,这可以防止此问题,因为ngif删除了它应用于DOM的元素,因此您不会当它不存在时能够找到它。

因此,在您的情况下:

<div class="column">
<div>{{ column.title }}</div>
<form name="columnForm" role="form" ng-show="editing">
    <select></select>
</form>

会正常工作。

欢呼。

最新更新