есть ли обратная связь для рендеринга для Angular JS?
Я только что получил мою директиву, чтобы вытащить шаблон для добавления к его элементу следующим образом:
# CoffeeScript .directive 'dashboardTable', -> controller: lineItemIndexCtrl templateUrl: "" (scope, element, attrs) -> element.parent('table#line_items').dataTable() console.log 'Just to make sure this is run' # HTML
Я также использую плагин jQuery под названием DataTables. Общее его использование выглядит так: $ (‘table # some_id’). DataTable (). Вы можете передать данные JSON в вызов dataTable (), чтобы предоставить данные таблицы, или вы можете иметь данные уже на странице, и это сделает все остальное. Я делаю последнее, имея строки уже на HTML-странице ,
Но проблема в том, что я должен вызвать dataTable () в таблице # line_items ПОСЛЕ DOM готов. Моя директива выше вызывает метод dataTable (), прежде чем шаблон добавляется к элементу директивы. Есть ли способ, которым я могу вызывать функции ПОСЛЕ добавления?
- Перенаправление на определенный маршрут на основе условия
- Объединение данных между путями на основе идентификатора с помощью AngularFire
- Борьба с controllerом AngularJS дважды
- Как получить данные POST с urlencoded с помощью $ http без jQuery?
- Как использовать «ng-repeat» в шаблоне директивы в Angular JS?
Спасибо за помощь!
ОБНОВЛЕНИЕ 1 после ответа Энди:
Я хочу удостовериться, что метод ссылки вызван только после того, как все на странице, поэтому я изменил директиву для небольшого теста:
# 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: "" }
И я действительно вижу «boo» в 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: "" }
Не повезло
Затем я пытаюсь добавить тайм-аут:
.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: "" }
И это работает. Поэтому я задаюсь вопросом, что пошло не так в версии кода без таймера?
- как использовать параметр ng для установки значения по умолчанию для элемента select
- jQuery ui datepicker с Angularjs
- ng-model для `` (с директивой DEMO)
- Каков наилучший способ условного применения атрибутов в AngularJS?
- Как отправить POST в angularjs с несколькими параметрами?
- Смутно о Сервисе и Фабрике
- AngularJS - $ anchorScroll smooth / duration
- Установите фокус на первый недопустимый ввод в форме AngularJs
Если второй параметр «задержка» не указан, по умолчанию выполняется выполнение функции после завершения рендеринга DOM. Поэтому вместо setTimeout используйте $ timeout:
$timeout(function () { //DOM has finished rendering });
У меня была та же проблема, и я считаю, что ответ действительно нет. См . Комментарий Мишко и некоторые обсуждения в группе .
Угловой может отслеживать, что все вызовы функций, которые он делает для управления DOM, завершены, но поскольку эти функции могут вызывать асинхронную логику, которая все еще обновляет DOM после их возвращения, нельзя было ожидать, что Angular узнает об этом. Любой обратный вызов Угловые дамы могут работать иногда, но небезопасно полагаться.
Мы решили эту эвристику с помощью setTimeout, как и вы.
(Имейте в виду, что не все согласны со мной – вы должны прочитать комментарии по ссылкам выше и посмотреть, что вы думаете.)
Хотя мой ответ не связан с datatables, он рассматривает проблему манипуляции DOM и, например, инициализацию плагина jQuery для директив, используемых для элементов, которые обновляют их содержимое асинхронно.
Вместо реализации тайм-аута можно было просто добавить часы, которые будут прослушивать изменения контента (или даже дополнительные внешние триггеры).
В моем случае я использовал этот обходной путь для инициализации плагина jQuery после выполнения ng-repeat, который создал мою внутреннюю DOM – в другом случае я использовал его для просто манипулирования DOM после того, как свойство scope было изменено на controllerе. Вот как я это сделал …
HTML:
{{myContent}}
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 ... } }); } } } } }]);
Примечание. Вместо того, чтобы просто перебрасывать переменную myContent в bool в атрибуте my-directive-watch, можно было бы представить любое произвольное выражение.
Примечание. Изоляция области, как в приведенном выше примере, может быть выполнена только один раз для каждого элемента – попытка сделать это с помощью нескольких директив на одном и том же элементе приведет к компиляции $: multidir Error – см .: https://docs.angularjs.org / ошибка / $ компилировать / multidir
Вы можете использовать функцию «link», также известную как postLink, которая запускается после ввода шаблона.
app.directive('myDirective', function() { return { link: function(scope, elm, attrs) { /*I run after template is put in */ }, template: 'Hello' } });
Дайте этому прочитать, если вы планируете создавать директивы, это большая помощь: http://docs.angularjs.org/guide/directive
Может быть, опаздываю, чтобы ответить на этот вопрос. Но кто-то может получить выгоду из моего ответа.
У меня была аналогичная проблема, и в моем случае я не могу изменить директиву, так как это библиотека и изменение кода библиотеки не является хорошей практикой. Так что я использовал переменную, чтобы ждать загрузки страницы и использовать ng-if внутри моего html, чтобы ждать рендеринга конкретного элемента.
В моем controllerе:
$scope.render=false; //this will fire after load the the page angular.element(document).ready(function() { $scope.render=true; });
В моем html (в моем случае компонент html – это canvas)
У меня была такая же проблема, но с использованием Angular + DataTable с компиляцией вложенных директив fnDrawCallback
+ row fnDrawCallback
+ $. Я поместил $ timeout в свою функцию fnDrawCallback
для исправления рендеринга разбиения на страницы.
До примера, на основе источника row_grouping:
var myDrawCallback = function myDrawCallbackFn(oSettings){ var nTrs = $('table#result>tbody>tr'); for(var i=0; i
Пример:
var myDrawCallback = function myDrawCallbackFn(oSettings){ var nTrs = $('table#result>tbody>tr'); $timeout(function requiredRenderTimeoutDelay(){ for(var i=0; i
Даже короткой задержки таймаута было достаточно, чтобы позволить Angular отображать мои скомпилированные угловые директивы.
Ни одно из решений не помогло мне принять использование таймаута. Это связано с тем, что я использовал шаблон, который динамически создавался во время postLink.
Однако обратите внимание, что тайм-аут может быть «0», поскольку тайм-аут добавляет функцию, вызываемую в очередь браузера, которая будет возникать после движка углового рендеринга, поскольку это уже находится в очереди.
Обратитесь к этому: http://blog.brunoscopelliti.com/run-a-directive-after-the-dom-has-finished-rendering
Ниже приведена директива о выполнении действий, запрограммированных после мелкой визуализации. По мелким я имею в виду, что он будет оценивать после того, как этот элемент будет вынесен, и который не будет иметь отношения к тому, когда будет отображаться его содержимое. Поэтому, если вам нужен какой-то дополнительный элемент, выполняющий действие рендеринга, вы должны его использовать:
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; }]); });
то вы можете сделать:
или с любым полезным выражением типа:
Я получил эту работу со следующей директивой:
app.directive('datatableSetup', function () { return { link: function (scope, elm, attrs) { elm.dataTable(); } } });
И в HTML:
если проблема выше, не работает для вас.
1) обратите внимание, что «datatableSetup» эквивалентен «datatable-setup». Угловое изменение формата в случае верблюда.
2) убедитесь, что приложение определено перед директивой. например, простое определение приложения и директива.
var app = angular.module('app', []); app.directive('datatableSetup', function () { return { link: function (scope, elm, attrs) { elm.dataTable(); } } });
Следуя тому факту, что порядок загрузки не может быть ожидаемым, можно использовать простое решение.
Давайте посмотрим на директиву – отношение «директива». Обычно пользователь директивы будет предоставлять некоторые данные в директиву или использовать некоторые функции (функции) для поставок директив. С другой стороны, директива ожидает, что некоторые переменные будут определены в ее объеме.
Если мы сможем убедиться, что все игроки выполнили все свои требования к действию, прежде чем они попытаются выполнить эти действия – все должно быть хорошо.
И теперь директива:
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
и где-то в controllerе компонента, который использует директиву:
$scope.control = {}; ... $scope.input = 'some data could be async'; if ( $scope.control.functionThatNeedsInput){ $scope.control.functionThatNeedsInput(); }
Вот и все. Существует много накладных расходов, но вы можете потерять $ timeout. Мы также предполагаем, что компонент, который использует директиву, создается перед директивой, потому что мы зависим от управляющей переменной, которая существует при создании экземпляра.