То, что я пытаюсь реализовать, это в основном обработчик «on ng repeat Законченный рендеринг». Я могу определить, когда это сделано, но я не могу понять, как вызвать функцию из этого.
Проверьте скрипку: http://jsfiddle.net/paulocoelho/BsMqq/3/
JS
var module = angular.module('testApp', [])
.directive('onFinishRender', function () {
return {
restrict: 'A',
link: function (scope, element, attr) {
if (scope.$last === true) {
element.ready(function () {
console.log("calling:"+attr.onFinishRender);
// CALL TEST HERE!
});
}
}
}
});
function myC($scope) {
$scope.ta = [1, 2, 3, 4, 5, 6];
function test() {
console.log("test executed");
}
}
HTML
<div ng-app="testApp" ng-controller="myC">
<p ng-repeat="t in ta" on-finish-render="test()">{{t}}</p>
</div>
Ответ : Рабочая скрипка с финишной черты : http://jsfiddle.net/paulocoelho/BsMqq/4/
element.ready()
фрагмента? Я имею в виду ... это какой-то плагин jQuery, который у вас есть, или он должен быть запущен, когда элемент готов?Ответы:
Обратите внимание, что я не использовал,
.ready()
а завернул его в$timeout
.$timeout
проверяет, выполняется ли он, когда элементы с повторением ng ДЕЙСТВИТЕЛЬНО завершили рендеринг (потому что он$timeout
будет выполнен в конце текущего цикла дайджеста - и он также будет вызываться$apply
внутри, в отличие от этогоsetTimeout
) Поэтому послеng-repeat
завершения мы используем$emit
для отправки события на внешние области (родственные и родительские области).И тогда в вашем контроллере вы можете поймать его с помощью
$on
:С HTML, который выглядит примерно так:
источник
$timeout
(что в основномsetTimeout
+ с Angular$scope.$apply()
)setInterval
.$timeout
будет выполняться только один раз в соответствии сif
условием, и это будет в начале следующего$digest
цикла. Дополнительную информацию о тайм- аутахsetTimeout()
задержки 0, это другой вопрос, хотя я должен сказать, что я никогда не сталкивался с реальной спецификацией для очереди событий браузера где-либо, только то, что подразумевает однопоточность и существованиеsetTimeout()
.on-finish-render="ngRepeatFinished"
? Когда я назначаюon-finish-render="helloworld"
, он работает так же.Используйте $ evalAsync, если вы хотите, чтобы ваш обратный вызов (т. Е. Test ()) выполнялся после создания DOM, но до того, как браузер отобразит. Это предотвратит мерцание - ref .
скрипка .
Если вы действительно хотите вызвать обратный вызов после рендеринга, используйте $ timeout:
Я предпочитаю $ eval вместо события. Для события нам нужно знать имя события и добавить код в наш контроллер для этого события. С $ eval связь между контроллером и директивой уменьшается.
источник
$last
? Не могу найти это в документах. Это было удалено?$last
определяется в области действия, еслиng-repeat
элемент активен.$timeout
.Ответы, которые были даны до сих пор, будут работать только при первом
ng-repeat
отображении , но если у вас есть динамикаng-repeat
, это означает, что вы собираетесь добавлять / удалять / фильтровать элементы, и вы должны получать уведомления каждый раз, когдаng-repeat
эти решения не будут работать для вас.Итак, если вам нужно КАЖДЫЙ ВРЕМЯ быть уведомленным о том, что
ng-repeat
рендеринг повторяется, а не только в первый раз, я нашел способ сделать это, он довольно «хакерский», но он будет работать нормально, если вы знаете, что вы делаете. делает. Используйте это$filter
в вашем,ng-repeat
прежде чем использовать любой другой$filter
:Это будет
$emit
событие, вызываемоеngRepeatFinished
каждый раз, когдаng-repeat
рендерится.Как это использовать:
В
ngRepeatFinish
потребности фильтра должны применяться непосредственно кArray
илиObject
определено в вашем$scope
, вы можете применить другие фильтры после.Как НЕ использовать это:
Не применяйте сначала другие фильтры, а затем применяйте
ngRepeatFinish
фильтр.Когда я должен использовать это?
Если вы хотите применить определенные стили CSS в DOM после завершения рендеринга списка, потому что вам нужно учитывать новые измерения элементов DOM, которые были перерисованы
ng-repeat
. (Кстати: такого рода операции должны выполняться внутри директивы)Что НЕ ДЕЛАТЬ в функции, которая обрабатывает
ngRepeatFinished
событие:Не выполняйте
$scope.$apply
в этой функции, иначе вы поместите Angular в бесконечный цикл, который Angular не сможет обнаружить.Не используйте его для внесения изменений в
$scope
свойства, потому что эти изменения не будут отражены в вашем представлении до следующего$digest
цикла, и поскольку вы не можете выполнить$scope.$apply
их, они не будут иметь никакого смысла.«Но фильтры не предназначены для такого использования !!»
Нет, это не хак, если вам не нравится, не используйте его. Если вы знаете лучший способ сделать то же самое, пожалуйста, дайте мне знать.
Подведение итогов
источник
$last
элемент также изменяется, будет работать простаяngRepeatFinish
директива, проверяющая$last
теперь. Как только вызывается ngRepeatFinish, я удаляю фиктивный элемент. (Я также скрываю это с помощью CSS, чтобы он не появлялся кратко)Mark Rajcok
работает отлично при каждом повторном рендеринге ng-repeat (например, из-за изменения модели). Так что это хакерское решение не требуется.Если вам нужно вызывать разные функции для разных ng-повторов на одном контроллере, вы можете попробовать что-то вроде этого:
Директива:
В вашем контроллере ловите события с $ on:
В вашем шаблоне с несколькими ng-repeat
источник
Другие решения будут нормально работать при начальной загрузке страницы, но вызов $ timeout из контроллера - единственный способ убедиться, что ваша функция вызывается при изменении модели. Вот рабочая скрипка, которая использует $ timeout. Для вашего примера это будет:
ngRepeat будет оценивать директиву только тогда, когда содержимое строки новое, поэтому, если вы удалите элементы из своего списка, onFinishRender не сработает. Например, попробуйте ввести значения фильтра в эти эмиссии скриптов .
источник
ta
внутри$scope.$watch("ta",...
?Если вы не против использования приманок с двойным долларовым охватом и пишете директиву, единственным содержанием которой является повторение, есть довольно простое решение (при условии, что вы заботитесь только о начальном рендере). В функции ссылки:
Это полезно в тех случаях, когда у вас есть директива, которая должна выполнять DOM-манипуляции на основе ширины или высоты членов отображаемого списка (что, я думаю, является наиболее вероятной причиной, по которой можно задать этот вопрос), но она не такая общая как и другие решения, которые были предложены.
источник
Решение этой задачи с фильтрованной ngRepeat мог быть с мутацией событиями , но они устарели (без немедленной замены).
Тогда я подумал о другом простом:
Это подключается к методам Node.prototype родительского элемента с однократным $ timeout для отслеживания последовательных изменений.
Это работает в основном правильно, но я получил несколько случаев, когда altDone вызывался бы дважды.
Снова ... добавьте эту директиву к родителю ngRepeat.
источник
appendChild
а также (так как я использовал толькоinsertBefore
иremoveChild
) в приведенном выше коде.Очень просто, вот как я это сделал.
источник
Пожалуйста, посмотрите на скрипку, http://jsfiddle.net/yNXS2/ . Так как созданная вами директива не создала новую область, я продолжил свой путь.
$scope.test = function(){...
сделал это случиться.источник
Я очень удивлен, что не вижу самого простого решения среди ответов на этот вопрос. То, что вы хотите сделать, это добавить
ngInit
директиву в ваш повторяющийся элемент (элемент сngRepeat
директивой), проверяя наличие$last
(специальная переменная, установленная в области видимости,ngRepeat
которая указывает, что повторяющийся элемент является последним в списке). Если$last
это правда, мы рендерим последний элемент, и мы можем вызвать функцию, которую мы хотим.Полный код для вашей разметки HTML будет:
Вам не нужен дополнительный JS-код в вашем приложении, кроме функции объема, которую вы хотите вызвать (в данном случае
test
), так какngInit
она предоставляется Angular.js. Просто убедитесь, что вашаtest
функция находится в области видимости, чтобы к ней можно было получить доступ из шаблона:источник