Acabei de receber minha diretiva para puxar um modelo para acrescentar ao seu elemento assim:
# 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>
Também estou usando um plugin jQuery chamado DataTables. O uso geral disso é assim: $ ('table # some_id'). DataTable (). Você pode passar os dados JSON para a chamada dataTable () para fornecer os dados da tabela OU pode ter os dados já na página e eles farão o resto. Estou fazendo o último, com as linhas já na página HTML .
Mas o problema é que preciso chamar o dataTable () na tabela # line_items APÓS o DOM estar pronto. Minha diretiva acima chama o método dataTable () ANTES que o modelo seja anexado ao elemento da diretiva. Existe uma maneira de chamar funções após o acréscimo?
Obrigado pela ajuda!
ATUALIZAÇÃO 1 após a resposta de Andy:
Quero ter certeza de que o método link só é chamado DEPOIS de que tudo esteja na página, então alterei a diretiva para um pequeno teste:
# 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>"
}
E eu realmente vejo "boo" no div # sayboo.
Então eu tento minha chamada de dados jquery
.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') %>"
}
Sem sorte ai
Então tento adicionar um tempo limite:
.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') %>"
}
E isso funciona. Então, eu me pergunto o que há de errado na versão não temporizada do código?
Respostas:
Se o segundo parâmetro, "atraso" não for fornecido, o comportamento padrão é executar a função após o DOM concluir a renderização. Então, em vez de setTimeout, use $ timeout:
fonte
$timeout(fn)
em última análise, chama osetTimeout(fn, 0)
que interrompe a execução do Javascript e permite que o navegador renderize o conteúdo primeiro, antes de continuar a execução desse Javascript.Eu tive o mesmo problema e acredito que a resposta realmente é não. Veja o comentário de Miško e algumas discussões no grupo .
O Angular pode rastrear que todas as chamadas de função que ele faz para manipular o DOM estão concluídas, mas como essas funções podem acionar uma lógica assíncrona que ainda atualiza o DOM após o retorno, não se espera que o Angular saiba sobre isso. Qualquer retorno de chamada que a Angular fornece pode funcionar algumas vezes, mas não seria seguro confiar nela.
Resolvemos isso heuristicamente com um setTimeout, como você fez.
(Lembre-se de que nem todos concordam comigo - você deve ler os comentários nos links acima e ver o que pensa.)
fonte
Você pode usar a função 'link', também conhecida como postLink, que é executada após a inserção do modelo.
Leia isto se você planeja fazer diretrizes, é uma grande ajuda: http://docs.angularjs.org/guide/directive
fonte
Embora minha resposta não esteja relacionada às tabelas de dados, ela aborda a questão da manipulação do DOM e, por exemplo, a inicialização do plugin jQuery para diretivas usadas em elementos com conteúdo atualizado de maneira assíncrona.
Em vez de implementar um tempo limite, pode-se adicionar um relógio que ouvirá alterações no conteúdo (ou até gatilhos externos adicionais).
No meu caso, usei essa solução alternativa para inicializar um plug-in jQuery depois que o ng-repeat foi feito, o que criou meu DOM interno - em outro caso, usei-o apenas para manipular o DOM depois que a propriedade do escopo foi alterada no controlador. Aqui está como eu fiz ...
HTML:
JS:
Nota: Em vez de apenas converter a variável myContent para boolear no atributo my-Directive-watch, pode-se imaginar qualquer expressão arbitrária lá.
Nota: O isolamento do escopo, como no exemplo acima, pode ser feito apenas uma vez por elemento - tentar fazer isso com várias diretivas no mesmo elemento resultará em um erro $ compile: multidir - consulte: https://docs.angularjs.org / error / $ compile / multidir
fonte
Pode chegar tarde para responder a esta pergunta. Mas ainda assim alguém pode se beneficiar da minha resposta.
Eu tive um problema semelhante e, no meu caso, não posso alterar a diretiva, pois é uma biblioteca e alterar um código da biblioteca não é uma boa prática. Então, o que eu fiz foi usar uma variável para aguardar o carregamento da página e usar ng-if dentro do meu html para esperar renderizar o elemento específico.
No meu controlador:
No meu html (no meu caso, o componente html é uma tela)
fonte
Eu tive o mesmo problema, mas usando Angular + DataTable com um
fnDrawCallback
+ agrupamento de linhas + $ diretivas aninhadas compiladas. Coloquei o $ timeout na minhafnDrawCallback
função para corrigir a renderização da paginação.Antes do exemplo, com base na fonte row_grouping:
Após o exemplo:
Mesmo um pequeno atraso de tempo limite foi suficiente para permitir que o Angular processasse minhas diretivas compiladas do Angular.
fonte
Nenhuma das soluções trabalhadas para mim aceita o uso de um tempo limite. Isso ocorre porque eu estava usando um modelo que estava sendo criado dinamicamente durante o postLink.
Observe, no entanto, pode haver um tempo limite de '0', pois o tempo limite adiciona a função que está sendo chamada à fila do navegador, o que ocorrerá após o mecanismo de renderização angular, pois ele já está na fila.
Consulte: http://blog.brunoscopelliti.com/run-a-directive-after-the-dom-has-finished-rendering
fonte
Aqui está uma diretiva para que as ações sejam programadas após uma renderização superficial. Por superficial, quero dizer que ele será avaliado depois desse elemento renderizado e que não terá relação com o momento em que seu conteúdo for renderizado. Portanto, se você precisar de algum subelemento executando uma ação pós-renderização, considere usá-la lá:
então você pode fazer:
<div after-render></div>
ou com qualquer expressão útil como:
<div after-render="$emit='onAfterThisConcreteThingRendered'"></div>
fonte
Eu consegui isso trabalhando com a seguinte diretiva:
E no HTML:
resolução de problemas se o acima não funcionar para você.
1) observe que 'datatableSetup' é o equivalente a 'datatable-setup'. Angular altera o formato para estojo de camelo.
2) verifique se o aplicativo está definido antes da diretiva. por exemplo, definição e diretiva simples de aplicativo.
fonte
Após o fato de que a ordem de carregamento não pode ser antecipada, uma solução simples pode ser usada.
Vejamos o relacionamento diretivo-usuário da diretiva. Normalmente, o usuário da diretiva fornecerá alguns dados à diretiva ou usará algumas funcionalidades (funções) que a diretiva fornece. A diretiva, por outro lado, espera que algumas variáveis sejam definidas em seu escopo.
Se pudermos garantir que todos os jogadores cumpram todos os requisitos de ação antes de tentar executá-las - tudo deve estar bem.
E agora a diretiva:
e agora o usuário da diretiva html
e em algum lugar no controlador do componente que usa a diretiva:
É sobre isso. Há muita sobrecarga, mas você pode perder o tempo limite de $. Também assumimos que o componente que usa a diretiva é instanciado antes da diretiva, porque dependemos da variável de controle que existe quando a diretiva é instanciada.
fonte