O que estou tentando implementar é basicamente um manipulador "on ng repeat terminou rendering". Sou capaz de detectar quando isso é feito, mas não consigo descobrir como acionar uma função a partir dela.
Verifique o violino: 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>
Resposta : Violino de trabalho de finishmove: http://jsfiddle.net/paulocoelho/BsMqq/4/
element.ready()
snippet? Quero dizer .. é algum tipo de plugin jQuery que você possui ou deve ser acionado quando o elemento estiver pronto?Respostas:
Observe que eu não usei,
.ready()
mas o envolvi em um$timeout
.$timeout
garante que ele seja executado quando os elementos ng-repetidos REALMENTE terminarem a renderização (porque eles$timeout
serão executados no final do ciclo de compilação atual - e também serão chamados$apply
internamente, ao contráriosetTimeout
). Portanto, após ong-repeat
término, usamos$emit
para emitir um evento para escopos externos (escopos irmão e pai).E então no seu controlador, você pode capturá-lo com
$on
:Com html que se parece com isso:
fonte
$timeout
(que é basicamente osetTimeout
+ Angular$scope.$apply()
)setInterval
.$timeout
será executado apenas uma vez porif
condição, e isso será no início do próximo$digest
ciclo. Para mais informações sobre limites de tempo JavaScript, consulte: ejohn.org/blog/how-javascript-timers-worksetTimeout()
0 atraso é outra pergunta, embora eu deva dizer que nunca encontrei uma especificação real para a fila de eventos do navegador em qualquer lugar, apenas o que está implícito no encadeamento único e na existência desetTimeout()
.on-finish-render="ngRepeatFinished"
? Quando eu atribuoon-finish-render="helloworld"
, funciona da mesma maneira.Use $ evalAsync se desejar que seu retorno de chamada (ou seja, test ()) seja executado após a construção do DOM, mas antes da renderização do navegador. Isso evitará tremulações - ref .
Violino .
Se você realmente deseja chamar seu retorno de chamada após a renderização, use $ timeout:
Prefiro $ eval em vez de um evento. Com um evento, precisamos saber o nome do evento e adicionar código ao nosso controlador para esse evento. Com $ eval, há menos acoplamento entre o controlador e a diretiva.
fonte
$last
faz? Não é possível encontrá-lo nos documentos. Foi removido?$last
é definido no escopo se umng-repeat
estiver ativo no elemento.$timeout
.As respostas que foram dadas até agora só funcionam na primeira vez em que
ng-repeat
são renderizadas , mas se você tiver uma dinâmicang-repeat
, significa que você irá adicionar / excluir / filtrar itens e precisará ser notificado sempre que ong-repeat
for renderizada, essas soluções não funcionarão para você.Então, se você precisar ser notificado TODAS AS VEZES de que as imagens
ng-repeat
são renderizadas novamente e não apenas a primeira vez, eu encontrei uma maneira de fazer isso, é bastante 'hacky', mas funcionará bem se você souber o que é fazendo. Use isso$filter
no seung-repeat
antes de usar qualquer outro$filter
:Será
$emit
um evento chamadongRepeatFinished
sempre que ang-repeat
renderização for renderizada.Como usá-lo:
O
ngRepeatFinish
filtro precisa ser aplicado diretamente a umArray
ou a umObject
definido no seu$scope
, você pode aplicar outros filtros depois.Como NÃO usá-lo:
Não aplique outros filtros primeiro e depois aplique o
ngRepeatFinish
filtro.Quando devo usar isso?
Se você deseja aplicar certos estilos css no DOM após a conclusão da renderização da lista, é necessário ter em conta as novas dimensões dos elementos DOM que foram renderizados novamente pelo
ng-repeat
. (BTW: esse tipo de operação deve ser realizado dentro de uma diretiva)O que NÃO FAZER na função que lida com o
ngRepeatFinished
evento:Não execute a
$scope.$apply
nessa função ou você colocará o Angular em um loop infinito que o Angular não poderá detectar.Não use-o para fazer alterações nas
$scope
propriedades, porque essas alterações não serão refletidas na sua visualização até o próximo$digest
loop e, como você não pode executar uma,$scope.$apply
elas não serão de uso algum."Mas os filtros não devem ser usados assim !!"
Não, eles não são, isso é um hack, se você não gostar, não use. Se você souber uma maneira melhor de realizar a mesma coisa, informe-me.
Resumindo
fonte
$last
elemento também é alterado, umangRepeatFinish
diretiva simples , verificando$last
agora, funcionará. Assim que ngRepeatFinish é chamado, removo o item fictício. (Eu também escondê-lo com CSS por isso dooesn't aparecer brevemente)Mark Rajcok
funciona perfeitamente em todas as repetições do ng-repeat (por exemplo, devido à alteração do modelo). Portanto, essa solução hacky não é necessária.Se você precisar chamar funções diferentes para diferentes repetições ng no mesmo controlador, tente algo como isto:
A diretiva:
No seu controlador, capture eventos com $ on:
No seu modelo com várias repetições ng
fonte
As outras soluções funcionarão bem no carregamento inicial da página, mas chamar $ timeout do controlador é a única maneira de garantir que sua função seja chamada quando o modelo for alterado. Aqui está um violino que usa $ timeout. Para o seu exemplo, seria:
O ngRepeat avaliará apenas uma diretiva quando o conteúdo da linha for novo; portanto, se você remover itens da sua lista, o onFinishRender não será acionado. Por exemplo, tente inserir valores de filtro nesses toques emitidos .
fonte
ta
dentro$scope.$watch("ta",...
?Se você não é avesso a usar adereços de escopo com dois dólares e está escrevendo uma diretiva cujo único conteúdo é uma repetição, existe uma solução bastante simples (supondo que você se preocupe apenas com a renderização inicial). Na função de link:
Isso é útil nos casos em que você tem uma diretiva que precisa manipular o DOM com base nas larguras ou alturas dos membros de uma lista renderizada (que eu acho que é o motivo mais provável de alguém fazer essa pergunta), mas não é tão genérica como as outras soluções propostas.
fonte
Uma solução para esse problema com um ngRepeat filtrado poderia estar com o Mutation eventos de , mas eles foram preteridos (sem substituição imediata).
Então pensei em outro fácil:
Isso se conecta aos métodos Node.prototype do elemento pai com um timeout $ timeout para observar as modificações sucessivas.
Funciona principalmente correto, mas recebi alguns casos em que o altDone seria chamado duas vezes.
Novamente ... adicione esta diretiva ao pai do ngRepeat.
fonte
appendChild
também (desde que eu usei apenasinsertBefore
eremoveChild
) no código acima.Muito fácil, foi assim que eu fiz.
fonte
Por favor, dê uma olhada no violino, http://jsfiddle.net/yNXS2/ . Como a diretiva que você criou não criou um novo escopo, continuei no caminho.
$scope.test = function(){...
fez isso acontecer.fonte
Estou muito surpreso por não encontrar a solução mais simples entre as respostas a esta pergunta. O que você deseja fazer é adicionar uma
ngInit
diretiva ao seu elemento repetido (o elemento com angRepeat
diretiva) verificando$last
(uma variável especial definida no escopo pelangRepeat
qual indica que o elemento repetido é o último da lista). Se$last
for verdade, renderizamos o último elemento e podemos chamar a função que queremos.O código completo para sua marcação HTML seria:
Você não precisa de nenhum código JS extra em seu aplicativo além da função de escopo que deseja chamar (neste caso
test
) , poisngInit
é fornecida pelo Angular.js. Apenas certifique-se de ter suatest
função no escopo para que possa ser acessada a partir do modelo:fonte