Подтвердить что ты не робот

AngularJS ngIf предотвращает поиск элемента внутри директивы

У меня есть директива AngularJS, которая включает ngIf, и я хотел бы изменить часть DOM внутри ngIf в функции ссылки. К сожалению, кажется, что 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 в функции link возвращает пустой массив, но как только я удаляю ngIf из формы, он возвращает правильный элемент DOM. У меня такое чувство, что я делаю это неправильно.

UPDATE

Спасибо за ответы, но я нашел другое решение. Я просто создал другую директиву, которая инкапсулирует форму, добавила ее в шаблон шаблона column с помощью ng-if="editing".

Директива формы не имеет собственной области видимости, поэтому она эффективно работает из области действия column и всегда имеет доступ к элементу select, поскольку она находится внутри своего дерева DOM. Я оплачиваю дополнительную директиву, но мне не нужно использовать взломать $timeout. Я создал новый jsFiddle, чтобы проиллюстрировать решение http://jsfiddle.net/dedalusj/nx3vX/1/

Спасибо @Michael, но я не могу просто использовать ng-option, потому что массив types поступает из XML файла, а его элементы - другие angular.элементные объекты, которые нельзя легко вставить с помощью ng-option.

4b9b3361

Ответ 1

Директива ngIf работает с использованием функции пересылки Angular. Что происходит во время цикла компиляции/ссылки:

  • Содержимое внутри ngIf удаляется из DOM при компиляции
  • Angular выполняет функции связи. Функция ссылки ngIf запускается до функция связи используемой директивы. Когда функция ngIf link работает, он использует $scope.$watch() для просмотра значения ng-if атрибут.
  • Выполняется функция вашей директивной ссылки, в этот момент содержимое ngIf не является частью DOM
  • Вызывается часы, созданные на шаге (2), и ngIf затем вызывает функцию $transclude, чтобы вставить содержимое ngIf в DOM, если значение атрибута ng-if правдиво.
  • Будут выполняться любые функции часов, $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
      });
    }
  };
});

Ответ 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 здесь.

Трюк здесь заключается в задержке вызова find() с помощью $timeout с интервалом 0, чтобы ждать 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 делает все это для вас.:)

Ответ 3

Вы можете поместить свой код из функции ссылок внутри $timeout.

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

Не забудьте ввести $timeout в свою директиву

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

Ответ 4

Я столкнулся с этой же проблемой, и я смог ее решить с помощью ng-show, это предотвратит эту проблему, потому что ngIf удаляет элемент, который он применил к DOM, поэтому вы не сможете найти его, когда он не будет.

поэтому в вашем случае:

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

будет работать нормально.

Приветствия.