есть ли обратный вызов пост рендера для Angular JS?

Я только что получил мою директиву, чтобы получить шаблон для добавления к его элементу, например так:

# CoffeeScript
.directive 'dashboardTable', ->
  controller: lineItemIndexCtrl
  templateUrl: "<%= asset_path('angular/templates/line_items/dashboard_rows.html') %>"
  (scope, element, attrs) ->
    element.parent('table#line_items').dataTable()
    console.log 'Just to make sure this is run'

# HTML
<table id="line_items">
    <tbody dashboard-table>
    </tbody>
</table>

Я также использую плагин jQuery под названием DataTables. Его общее использование выглядит следующим образом: $ ('table # some_id'). DataTable (). Вы можете передать данные JSON в вызов dataTable () для предоставления данных таблицы ИЛИ у вас могут быть данные уже на странице, и они будут делать все остальное. Я делаю последнее, когда строки уже находятся на странице HTML. ,

Но проблема в том, что я должен вызвать dataTable () в таблице # line_items ПОСЛЕ ТОГО, КАК DOM готов. Моя директива выше вызывает метод dataTable () ДО того, как шаблон будет добавлен к элементу директивы. Есть ли способ, которым я могу вызывать функции ПОСЛЕ добавления?

Спасибо за помощь!

ОБНОВЛЕНИЕ 1 после ответа Энди:

Я хочу убедиться, что метод link вызывается только ПОСЛЕ того, как все на странице, поэтому я изменил директиву для небольшого теста:

# CoffeeScript
#angular.module(...)
.directive 'dashboardTable', ->
    {
      link: (scope,element,attrs) -> 
        console.log 'Just to make sure this gets run'
        element.find('#sayboo').html('boo')

      controller: lineItemIndexCtrl
      template: "<div id='sayboo'></div>"

    }

И я действительно вижу «бу» в div # sayboo.

Затем я пробую свой вызов jquery datatable

.directive 'dashboardTable',  ->
    {
      link: (scope,element,attrs) -> 
        console.log 'Just to make sure this gets run'
        element.parent('table').dataTable() # NEW LINE

      controller: lineItemIndexCtrl
      templateUrl: "<%= asset_path('angular/templates/line_items/dashboard_rows.html') %>"
    }

Не повезло там

Затем я пытаюсь добавить время ожидания:

.directive 'dashboardTable', ($timeout) ->
    {
      link: (scope,element,attrs) -> 
        console.log 'Just to make sure this gets run'
        $timeout -> # NEW LINE
          element.parent('table').dataTable()
        ,5000
      controller: lineItemIndexCtrl
      templateUrl: "<%= asset_path('angular/templates/line_items/dashboard_rows.html') %>"
    }

И это работает. Так что мне интересно, что не так в нережимной версии кода?

 Nik So08 авг. 2012 г., 05:35
@adardesign И да, я перепробовал все методы компиляции, метод компиляции, возвращающий объект, содержащий методы postLink / preLink, метод компиляции, возвращающий только функцию (а именно функцию связывания), метод связывания (без метода компиляции, потому что, насколько я могу судить, если у вас есть метод компиляции, который возвращает метод связывания, функция связывания игнорируется). Ничего не сработало, поэтому приходится полагаться на старый добрый $ timeout. Будет ли обновлять этот пост, если я найду что-то, что работает лучше или просто, когда я обнаружу, что обратный вызов действительно действует как обратный вызов
 Nik So07 авг. 2012 г., 20:02
@adardesign Нет, я никогда не делал, мне пришлось использовать таймер. По какой-то причине обратный вызов здесь не является обратным вызовом. У меня есть таблица с 11 столбцами и 100 строками, поэтому естественно, что угловые выглядят как хорошая ставка для привязки данных; но мне также нужно использовать плагин jquery Datatables, который так же прост, как $ ('table' '). datatable (). Используя директиву или просто имея тупой объект json со всеми строками и используя ng-repeat для итерации, я не могу получить свой $ (). Datatable () для запуска ПОСЛЕ отображения элемента html таблицы, поэтому в настоящее время я использую таймер чтобы проверить, является ли $ ('tr' '). length & gt; 3 (б / с верхнего / нижнего колонтитула)

Ответы на вопрос(10)

После того, как порядок загрузки не может быть предвиден, можно использовать простое решение.

Давайте посмотрим на директиву - пользователя директивы. отношения. Обычно пользователь директивы передает некоторые данные директиве или использует некоторые функциональные возможности (функции), предоставляемые директивой. Директива, с другой стороны, ожидает, что некоторые переменные будут определены в ее области видимости.

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

А теперь директива:

app.directive('aDirective', function () {
    return {
        scope: {
            input: '=',
            control: '='
        },
        link: function (scope, element) {
            function functionThatNeedsInput(){
                //use scope.input here
            }
            if ( scope.input){ //We already have input 
                functionThatNeedsInput();
            } else {
                scope.control.init = functionThatNeedsInput;
            }
          }

        };
})

а теперь пользователь директивы html

<a-directive control="control" input="input"></a-directive>

и где-то в контроллере компонента, который использует директиву:

$scope.control = {};
...
$scope.input = 'some data could be async';
if ( $scope.control.functionThatNeedsInput){
    $scope.control.functionThatNeedsInput();
}

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

Ни одно из решений не помогло мне принять тайм-аут. Это потому, что я использовал шаблон, который динамически создавался во время postLink.

Отметьте, однако, что может быть тайм-аут «0». по мере того, как тайм-аут добавляет функцию, вызываемую в очередь браузера, что будет происходить после механизма угловой визуализации, поскольку он уже находится в очереди.

Обратитесь к этому:http://blog.brunoscopelliti.com/run-a-directive-after-the-dom-has-finished-rendering

Вы можете использовать «ссылку»; функция, также известная как postLink, которая запускается после вставки шаблона.

app.directive('myDirective', function() {
  return {
    link: function(scope, elm, attrs) { /*I run after template is put in */ },
    template: '<b>Hello</b>'
  }
});

Прочитайте это, если вы планируете делать директивы, это большая помощь:http://docs.angularjs.org/guide/directive

 Nik So20 июн. 2012 г., 21:37
Привет Энди, вернул мои результаты; Я почти потерял рассудок, потому что я действительно сделал то, что вы ответили здесь. Пожалуйста, смотрите мое обновление
 Nik So20 июн. 2012 г., 21:02
Привет Энди, большое спасибо за ответ; Я попробовал функцию связи, но я не возражал бы повторить попытку в точности так, как вы ее кодировали; Последние 1,5 дня я читал на этой странице директивы; и, глядя на примеры на сайте angular. Попробую ваш код сейчас.
 20 июн. 2012 г., 21:09
Ах, теперь я вижу, что вы пытались сделать ссылку, но вы делали это неправильно. Если вы просто возвращаете функцию, она считается ссылкой. Если вы возвращаете объект, вы должны вернуть его с ключом в виде «ссылки». Вы также можете вернуть функцию связывания из функции компиляции.
 03 янв. 2017 г., 11:05
Этот работал для меня, я хотел переместить элементы в dom post рендеринга шаблона для моего требования, сделал это в функции ссылки. Спасибо
 20 июн. 2012 г., 22:28
Хм, попробуйте что-нибудь вроде: & lt; table id = "bob" & lt; tbody dashboard-table = "# bob" & gt; & lt; / tbody & gt; / table & gt; Затем в своей ссылке выполните $ (attrs.dashboardTable) .dataTable (), чтобы убедиться, что он выбран правильно. Или, я думаю, вы уже пробовали это ... Я действительно не уверен, что ссылка не работает.
Решение Вопроса

Если второй параметр, «delay» не предусмотрено, поведение по умолчанию - выполнение функции после того, как DOM завершил рендеринг. Поэтому вместо setTimeout используйте $ timeout:

$timeout(function () {
    //DOM has finished rendering
});
 23 дек. 2014 г., 12:25
Почему это не объясняется вdocs ?
 25 дек. 2014 г., 18:50
Под браузером следует понимать определенные задачи, такие как «выполнение JavaScript». и «рендеринг DOM»; отдельно, и что setTimeout (fn, 0) выдвигает в настоящий момент запущенное «выполнение JavaScript» в конец очереди, после рендеринга.
 25 дек. 2014 г., 18:48
Вы правы, мой ответ немного вводит в заблуждение, потому что я пытался упростить его. Полный ответ заключается в том, что этот эффект является не результатом Angular, а скорее браузером.$timeout(fn) в конечном итоге звонкиsetTimeout(fn, 0) что приводит к прерыванию выполнения Javascript и позволяет браузеру сначала визуализировать содержимое, прежде чем продолжить выполнение этого Javascript.
 10 авг. 2015 г., 06:07
@GabLeRoux yup, это будет иметь тот же эффект, за исключением того, что $ timeout имеет дополнительное преимущество вызова $ scope. $ Apply () после его запуска. С _.defer () вам нужно будет вызывать его вручную, если myFunction изменяет переменные в области.
 19 окт. 2015 г., 00:08
У меня есть сценарий, в котором это не помогает, когда на странице 1 ng-repeat отображает кучу элементов, затем я перехожу на страницу 2, а затем я возвращаюсь на страницу 1 и пытаюсь получить максимум родительского элемента ng-repeat ... он возвращает неправильную высоту. Если я делаю тайм-аут как 1000 мс, то это работает.

У меня была та же проблема, но с помощью Angular + DataTable сfnDrawCallback + группировка строк + $ скомпилированные вложенные директивы. Я поместил тайм-аут в мойfnDrawCallback функция исправления рендеринга пагинации.

Перед примером, основанным на источнике row_grouping:

var myDrawCallback = function myDrawCallbackFn(oSettings){
  var nTrs = $('table#result>tbody>tr');
  for(var i=0; i<nTrs.length; i++){
     //1. group rows per row_grouping example
     //2. $compile html templates to hook datatable into Angular lifecycle
  }
}

После примера:

var myDrawCallback = function myDrawCallbackFn(oSettings){
  var nTrs = $('table#result>tbody>tr');
  $timeout(function requiredRenderTimeoutDelay(){
    for(var i=0; i<nTrs.length; i++){
       //1. group rows per row_grouping example
       //2. $compile html templates to hook datatable into Angular lifecycle
    }
  ,50); //end $timeout
}

Даже небольшая задержка была достаточной, чтобы Angular мог отрисовать мои скомпилированные директивы Angular.

 Nik So05 февр. 2013 г., 11:10
Просто любопытно, у вас есть довольно большая таблица с множеством столбцов? потому что я обнаружил, что мне нужно раздражать много миллисекунд (& gt; 100), чтобы не позволить dataTable () вызвать удушье
 06 февр. 2013 г., 00:36
Я обнаружил, что проблема произошла наDataTable навигация по страницам для результирующих наборов от 2 до более 150 строк. Так что нет - я не думаю, что проблема была в размере таблицы, но, возможно, в DataTable было добавлено достаточно ресурсов для рендеринга, чтобы отжать некоторые из этих миллисекунд. Я сосредоточился на том, чтобы заставить работать группу строк в DataTable с минимальной интеграцией с AngularJS.

Хотя мой ответ не связан с таблицами данных, он затрагивает проблему манипулирования DOM и, например, Инициализация плагина jQuery для директив, используемых для элементов, содержимое которых обновляется асинхронно.

Вместо реализации тайм-аута можно просто добавить часы, которые будут прослушивать изменения содержимого (или даже дополнительные внешние триггеры).

В моем случае я использовал этот обходной путь для инициализации плагина jQuery, как только было выполнено ng-repeat, которое создало мой внутренний DOM - в другом случае я использовал его просто для манипулирования DOM после изменения свойства scope на контроллере. Вот как я это сделал ...

HTML:

<div my-directive my-directive-watch="!!myContent">{{myContent}}</div>

JS:

app.directive('myDirective', [ function(){
    return {
        restrict : 'A',
        scope : {
            myDirectiveWatch : '='
        },
        compile : function(){
            return {
                post : function(scope, element, attributes){

                    scope.$watch('myDirectiveWatch', function(newVal, oldVal){
                        if (newVal !== oldVal) {
                            // Do stuff ...
                        }
                    });

                }
            }
        }
    }
}]);

Note: Вместо того, чтобы просто приводить переменную myContent в bool к атрибуту my-directive-watch, можно представить любое произвольное выражение.

Note: Выделение области, как в приведенном выше примере, может быть выполнено только один раз для каждого элемента - попытка сделать это с несколькими директивами на одном элементе приведет к ошибке $ compile: multidir - см .:https://docs.angularjs.org/error/$compile/multidir

Я получил это работая со следующей директивой:

app.directive('datatableSetup', function () {
    return { link: function (scope, elm, attrs) { elm.dataTable(); } }
});

И в HTML:

<table class="table table-hover dataTable dataTable-columnfilter " datatable-setup="">

trouble shooting if the above doesnt work for you.

1) обратите внимание, что «datatableSetup»; является эквивалентом «datatable-setup». Angular меняет формат на верблюжий корпус.

2) убедитесь, что приложение определено до директивы. например простое определение приложения и директива.

var app = angular.module('app', []);
app.directive('datatableSetup', function () {
    return { link: function (scope, elm, attrs) { elm.dataTable(); } }
});

Может быть, поздно, чтобы ответить на этот вопрос. Но все же кто-то может получить пользу от моего ответа.

У меня была похожая проблема, и в моем случае я не могу изменить директиву, так как это библиотека, и изменение кода библиотеки не является хорошей практикой. Поэтому я использовал переменную для ожидания загрузки страницы и использовал ng-if внутри моего html для ожидания рендеринга конкретного элемента.

В моем контроллере:

$scope.render=false;

//this will fire after load the the page

angular.element(document).ready(function() {
    $scope.render=true;
});

В моем HTML (в моем случае компонент HTML является холст)

<canvas ng-if="render"> </canvas>

У меня была та же проблема, и я считаю, что ответ на самом деле нет. УвидетьКомментарий МиКо и немногообсуждение в группе.

Angular может отследить, что все вызовы функций, которые он делает для манипулирования DOM, завершены, но, поскольку эти функции могут запускать асинхронную логику, которая все еще обновляет DOM после того, как они вернутся, Angular не мог бы знать об этом. Любой обратный звонок Angular даетmight работать иногда, но на него нельзя было бы полагаться.

Мы решили это эвристически с помощью setTimeout, как вы сделали.

(Please keep in mind that not everyone agrees with me - you should read the comments on the links above and see what you think.)

Вот директива для программирования действий после поверхностного рендеринга. Под мелким я подразумеваю, что это оценит после того самого элемента, и это не будет связано сwhen его содержимое визуализируется. Поэтому, если вам нужен какой-то подэлемент, выполняющий действие пост-рендеринга, вы должны рассмотреть его использование там:

define(['angular'], function (angular) {
  'use strict';
  return angular.module('app.common.after-render', [])
    .directive('afterRender', [ '$timeout', function($timeout) {
    var def = {
        restrict : 'A', 
        terminal : true,
        transclude : false,
        link : function(scope, element, attrs) {
            if (attrs) { scope.$eval(attrs.afterRender) }
            scope.$emit('onAfterRender')
        }
    };
    return def;
    }]);
});

тогда вы можете сделать:

<div after-render></div>

или с любым полезным выражением, например:

<div after-render="$emit='onAfterThisConcreteThingRendered'"></div>

 25 мар. 2015 г., 17:39
Это не совсем после того, как контент рендерится. Если бы у меня было выражение внутри элемента & lt; div after-render & gt; {{бла}} & lt; / div & gt; на данный момент выражение еще не оценено. Содержимое div по-прежнему {{blah}} внутри функции ссылки. Таким образом, технически вы запускаете событие до того, как контент будет обработан.
 25 мар. 2015 г., 20:15
Это мелкое действие после рендеринга, я никогда не заявлял, что оно глубокое

Ваш ответ на вопрос