AngularJS ngIf阻止查找元素内部指令

时间:2014-02-04 01:23:21

标签: javascript angularjs

我有一个包含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/

的链接

link函数中的element.find调用返回一个空数组,但只要从表单中删除ngIf,它就会返回正确的select DOM元素。我觉得我这样做的方式不对。

更新

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

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

感谢@Michael,但我不能简单地使用ng-option,因为types数组来自XML文件,其元素是其他angular.element对象,无法使用{{1 }}。

4 个答案:

答案 0 :(得分:10)

ngIf指令使用Angular的转义功能。在编译/链接循环期间发生的是:

  1. ngIf内的内容在编译时从DOM中删除
  2. Angular运行链接功能。之前运行ngIf的链接功能 使用它的指令的链接功能。当ngIf的链接功能时 运行时,它使用$scope.$watch()来观看ng-if的值 属性。
  3. 您的指令的链接功能运行,此时ngIf的内容不是DOM的一部分
  4. 调用步骤(2)中设置的监视,然后ngIf将调用$transclude函数,以便ngIfng-if的内容插入到DOM中{{1}属性值是真实的。
  5. 您在指令的链接功能中注册的任何监视功能$timeout调用或使用$scope.$evalAsync都将运行。
  6. 因此,如果您想访问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
          });
        }
      };
    });
    

答案 1 :(得分:2)

正如@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 here

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

<强>更新

在对代码进行更多考虑之后,我意识到也许你可以让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为您做了所有这些。 :)

答案 2 :(得分:2)

您可以将链接功能中的代码放在$ timeout内。

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

不要忘记在你的指令中注入$ timeout

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

答案 3 :(得分:0)

我遇到了同样的问题而且我能够使用ng-show解决它,这可以防止这个问题,因为ngIf删除了它应用于DOM的元素,所以当它不存在时你将无法找到它

所以在你的情况下:

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

可以正常工作。

干杯。