Директива AngularJS работает до того, как элемент полностью загружен

У меня есть директива, прикрепленная к динамически сгенерированному элементу <table> внутри шаблона. Директива управляет DOM этой таблицы внутри функции link. Проблема в том, что директива запускается до отображения таблицы (путем оценки директив ng-repeat) - тогда таблица пуста.

Вопрос

Как я могу убедиться, что директива запускается после полной обработки таблицы?

<table directive-name>
    <tr ng-repeat="...">
        <td ng-repeat="..."></td>
    </tr>
</table>


module.directive("directiveName", function() {
    return {
        scope: "A",
        link: function(scope, element, attributes) {
            /* I need to be sure that the table is already fully
               rendered when this code runs */
        }
    };
});

Ответ 1

Вы не можете в общем смысле быть "полностью уверенным", просто указав элемент <table>.

Но вы можете быть уверены в некоторых случаях. В вашем случае, если внутренний контент ng-repeat -ed, то если массив элементов, над которыми работает ngRepeat, готов, то фактические элементы DOM будут готовы в конце цикла дайджеста. Вы можете записать его после $timeout с задержкой 0:

link: function(scope, element){
  $timeout(function(){
    console.log(element.find("tr").length); // will be > 0
  })
}

Но, в общем, вы не можете быть уверены в том, чтобы захватить содержимое. Что делать, если массив ngRepeat ed еще не существует? Или что, если вместо этого есть ng-include?

<table directive-name ng-include="'templates/tr.html'">
</table>

Или, что, если есть пользовательская директива, которая работает иначе, чем ngRepeat?

Но если у вас есть полный контроль над содержимым, один из возможных способов знать - включить какую-либо вспомогательную директиву в качестве самого внутреннего/последнего элемента и связать ее со своим родителем directiveName, когда он связан:

<table directive-name>
    <tr ng-repeat="...">
        <td ng-repeat="...">
          <directive-name-helper ng-if="$last">
        </td>
    </tr>
</table>
.directive("directiveNameHelper", function(){
  return {
    require: "?^directiveName",
    link: function(scope, element, attrs, ctrl){
      if (!ctrl) return;

      ctrl.notifyDone();
    }
  }
})

Ответ 2

Попробуйте обернуть в $timeout код из вашей функции ссылок, который будет выполняться после рендеринга DOM.

$timeout(function () {
    //do your stuff here as the DOM has finished rendering already
});

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

.directive("directiveName", function($timeout) {

Есть много альтернатив, но я думаю, что это чище, поскольку $timeout выполняется после того, как движок рендеринга завершил свою работу.

Ответ 3

Чистым способом было бы использовать что-то вроде метод lodash _.defer.

Вы можете вызвать его с помощью _.defer(your_func, your_func_arg1, your_func_arg2, ...) внутри вашей ссылки, чтобы выполнить метод, когда текущий стек вызовов очищен и все готово.

Таким образом, вам не нужно самостоятельно оценивать $timeout.