Diretrizes angulares – quando e como usar a compilation, o controlador, o pré-link e o pós-link

Ao escrever uma diretiva Angular, pode-se usar qualquer uma das seguintes funções para manipular o comportamento DOM, conteúdo e aparência do elemento no qual a diretiva é declarada:

  • compilar
  • controlador
  • pré-link
  • post-link

Parece haver alguma confusão quanto a qual function deve ser usada. Esta questão cobre:

Princípios básicos da diretiva

  • Como declarar as várias funções?
  • Qual é a diferença entre um modelo de origem e um modelo de instância ?
  • Em que ordem as funções da diretiva são executadas?
  • O que mais acontece entre essas chamadas de function?

Função natureza, faça e não faça

  • Compilar
  • Controlador
  • Pré-link
  • Post-link

Perguntas relacionadas:

  • Diretiva: link vs compile vs controller .
  • Diferença entre as funções ‘controller’, ‘link’ e ‘compile’ ao definir uma diretiva angular.js .
  • Qual é a diferença entre compilar e ligar function em angularjs .
  • Diferença entre o elemento pré-compilar e pós-compilar nas diretivas AngularJS? .
  • Diretiva Angular JS – Modelo, compilation ou link? .
  • post link vs pre link nas diretivas js Angular .

Em que ordem as funções da diretiva são executadas?

Para uma diretiva única

Com base no seguinte plunk , considere a seguinte marcação HTML:

 

Com a seguinte declaração de diretiva:

 myApp.directive('log', function() { return { controller: function( $scope, $element, $attrs, $transclude ) { console.log( $attrs.log + ' (controller)' ); }, compile: function compile( tElement, tAttributes ) { console.log( tAttributes.log + ' (compile)' ); return { pre: function preLink( scope, element, attributes ) { console.log( attributes.log + ' (pre-link)' ); }, post: function postLink( scope, element, attributes ) { console.log( attributes.log + ' (post-link)' ); } }; } }; }); 

A saída do console será:

 some-div (compile) some-div (controller) some-div (pre-link) some-div (post-link) 

Podemos ver que a compile é executada primeiro, depois o controller , depois o pre-link e o último é o post-link .

Para diretivas aninhadas

Nota: O seguinte não se aplica a diretivas que renderizam seus filhos em sua function de link. Algumas diretivas angulares fazem isso (como ngIf, ngRepeat ou qualquer diretiva com transclude ). Essas diretivas terão nativamente sua function de link chamada antes que sua diretiva filha compile seja chamada.

A marcação HTML original é geralmente feita de elementos nesteds, cada um com sua própria diretiva. Como na marcação a seguir (consulte plunk ):

  

A saída do console ficará assim:

 // The compile phase parent (compile) ..first-child (compile) ..second-child (compile) // The link phase parent (controller) parent (pre-link) ..first-child (controller) ..first-child (pre-link) ..first-child (post-link) ..second-child (controller) ..second-child (pre-link) ..second-child (post-link) parent (post-link) 

Podemos distinguir duas fases aqui – a fase de compilation e a fase de link .

A fase de compilation

Quando o DOM é carregado, o Angular inicia a fase de compilation, onde ele percorre a marcação de cima para baixo e chama compile em todas as diretivas. Graficamente, poderíamos expressá-lo assim:

Uma imagem ilustrando o loop de compilação para crianças

Talvez seja importante mencionar que, nesse estágio, os modelos que a function de compilation obtém são os modelos de origem (não o modelo de instância).

A fase de binding

As instâncias DOM são geralmente o resultado de um modelo de origem sendo renderizado para o DOM, mas elas podem ser criadas por ng-repeat ou introduzidas na hora.

Sempre que uma nova instância de um elemento com uma diretiva é renderizada no DOM, a fase de link é iniciada.

Nessa fase, Angular chama o controller , pre-link , repete os filhos, e chama post-link em todas as diretivas, assim:

Uma ilustração demonstrando as etapas da fase de link

O que mais acontece entre essas chamadas de function?

As várias funções de diretivas são executadas a partir de duas outras funções angulares chamadas $compile (onde a compile da diretiva é executada) e uma function interna chamada nodeLinkFn (onde o controller da diretiva, preLink e postLink são executados). Várias coisas acontecem dentro da function angular antes e depois das funções da diretiva serem chamadas. Talvez mais notavelmente seja a recursion infantil. A ilustração simplificada a seguir mostra as principais etapas nas fases de compilation e link:

Uma ilustração mostrando as fases de compilação e vinculação angular

Para demonstrar estes passos, vamos usar a seguinte marcação HTML:

 
Inner content

Com a seguinte diretiva:

 myApp.directive( 'myElement', function() { return { restrict: 'EA', transclude: true, template: '
{{label}}
' } });

Compilar

A API de compile parece assim:

 compile: function compile( tElement, tAttributes ) { ... } 

Geralmente, os parâmetros são prefixados com t para significar que os elementos e atributos fornecidos são aqueles do modelo de origem, e não os da instância.

Antes da chamada para compile conteúdo transcluído (se houver) é removida e o modelo é aplicado à marcação. Assim, o elemento fornecido para a function de compile será semelhante a:

  
"{{label}}"

Observe que o conteúdo transcluído não é reinserido neste momento.

Após a chamada para o .compile da .compile , o Angular irá percorrer todos os elementos filho, incluindo aqueles que podem ter sido introduzidos pela diretiva (os elementos de modelo, por exemplo).

Criação de Instância

Em nosso caso, três instâncias do modelo de origem acima serão criadas (por ng-repeat ). Assim, a seguinte sequência será executada três vezes, uma vez por instância.

Controlador

A API do controller envolve:

 controller: function( $scope, $element, $attrs, $transclude ) { ... } 

Ao entrar na fase de link, a function de link retornada via $compile agora é fornecida com um escopo.

Primeiro, a function de link cria um escopo filho ( scope: true ) ou um escopo isolado ( scope: {...} ) se solicitado.

O controlador é então executado, fornecido com o escopo do elemento da instância.

Pré-link

A API de pre-link é assim:

 function preLink( scope, element, attributes, controller ) { ... } 

Praticamente nada acontece entre a chamada ao .controller da diretiva e a function .preLink . Angular ainda fornece recomendações sobre como cada um deve ser usado.

Após a chamada .preLink , a function de link percorrerá cada elemento filho – chamando a function de link correta e anexando a ele o escopo atual (que serve como escopo pai para elementos filho).

Post-link

A API de post-link é semelhante à da function de pre-link :

 function postLink( scope, element, attributes, controller ) { ... } 

Talvez valha a pena notar que uma vez que a function .postLink uma diretiva é chamada, o processo de link de todos os seus elementos filhos foi concluído, incluindo todas as funções .postLink das crianças.

Isso significa que quando o .postLink é chamado, as crianças estão “ao vivo” estão prontas. Isso inclui:

  • binding de dados
  • transclusão aplicada
  • escopo anexado

O modelo nesta fase será assim:

  
"{{label}}"
Inner content

Como declarar as várias funções?

Compile, Controller, Pré-link e Post-link

Se é para usar todas as quatro funções, a diretiva seguirá este formulário:

 myApp.directive( 'myDirective', function () { return { restrict: 'EA', controller: function( $scope, $element, $attrs, $transclude ) { // Controller code goes here. }, compile: function compile( tElement, tAttributes, transcludeFn ) { // Compile code goes here. return { pre: function preLink( scope, element, attributes, controller, transcludeFn ) { // Pre-link code goes here }, post: function postLink( scope, element, attributes, controller, transcludeFn ) { // Post-link code goes here } }; } }; }); 

Observe que compile retorna um object contendo as funções pre-link e post-link; Na linguagem angular, dizemos que a function de compilation retorna uma function de modelo .

Compilar, Controlador e Post-Link

Se o pre-link não for necessário, a function de compilation pode simplesmente retornar a function post-link em vez de um object de definição, da seguinte forma:

 myApp.directive( 'myDirective', function () { return { restrict: 'EA', controller: function( $scope, $element, $attrs, $transclude ) { // Controller code goes here. }, compile: function compile( tElement, tAttributes, transcludeFn ) { // Compile code goes here. return function postLink( scope, element, attributes, controller, transcludeFn ) { // Post-link code goes here }; } }; }); 

Às vezes, alguém deseja adicionar um método de compile , depois que o método de link (post) foi definido. Para isso, pode-se usar:

 myApp.directive( 'myDirective', function () { return { restrict: 'EA', controller: function( $scope, $element, $attrs, $transclude ) { // Controller code goes here. }, compile: function compile( tElement, tAttributes, transcludeFn ) { // Compile code goes here. return this.link; }, link: function( scope, element, attributes, controller, transcludeFn ) { // Post-link code goes here } }; }); 

Controller & Post-link

Se nenhuma function de compilation é necessária, pode-se pular sua declaração completamente e fornecer a function post-link sob a propriedade link do object de configuração da diretiva:

 myApp.directive( 'myDirective', function () { return { restrict: 'EA', controller: function( $scope, $element, $attrs, $transclude ) { // Controller code goes here. }, link: function postLink( scope, element, attributes, controller, transcludeFn ) { // Post-link code goes here }, }; }); 

Nenhum controlador

Em qualquer um dos exemplos acima, pode-se simplesmente remover a function do controller , se não for necessário. Então, por exemplo, se apenas post-link function post-link for necessária, pode-se usar:

 myApp.directive( 'myDirective', function () { return { restrict: 'EA', link: function postLink( scope, element, attributes, controller, transcludeFn ) { // Post-link code goes here }, }; }); 

Qual é a diferença entre um modelo de origem e um modelo de instância ?

O fato de Angular permitir a manipulação DOM significa que a marcação de input no processo de compilation às vezes difere da saída. Particularmente, algumas marcações de input podem ser clonadas algumas vezes (como com ng-repeat ) antes de serem renderizadas para o DOM.

A terminologia angular é um pouco inconsistente, mas ainda distingue entre dois tipos de marcação:

  • Modelo de origem – a marcação a ser clonada, se necessário. Se clonado, essa marcação não será renderizada no DOM.
  • Modelo de instância – a marcação real a ser renderizada no DOM. Se a clonagem estiver envolvida, cada instância será um clone.

A marcação a seguir demonstra isso:

 
{{i}}

A fonte html define

  {{i}} 

que serve como modelo de origem.

Mas como ele é colocado dentro de uma diretiva ng-repeat , esse modelo de origem será clonado (3 vezes no nosso caso). Esses clones são modelo de instância, cada um aparecerá no DOM e será vinculado ao escopo relevante.

Função de compilation

A function de compile cada diretiva é chamada apenas uma vez, quando Bootstraps angulares.

Oficialmente, este é o local para executar manipulações de modelo (fonte) que não envolvem escopo ou vinculação de dados.

Primeiramente, isso é feito para fins de otimização; considere a seguinte marcação:

    

A diretiva processará um conjunto específico de marcação DOM. Então nós podemos:

  • Permitir que ng-repeat duplique o modelo de origem ( ) e, em seguida, modifique a marcação de cada modelo de instância (fora da function de compile ).
  • Modifique o modelo de origem para envolver a marcação desejada (na function de compile ) e, em seguida, permita que ng-repeat a duplique.

Se houver 1000 itens na coleção raws , a última opção poderá ser mais rápida que a anterior.

Faz:

  • Manipula a marcação para servir como um modelo para instâncias (clones).

Não

  • Anexe manipuladores de events.
  • Inspecione elementos filho.
  • Configure observações sobre atributos.
  • Configurar relógios no escopo.

Função de pós-link

Quando a function de post-link é chamada, todas as etapas anteriores ocorreram – encadernação, transclusão etc.

Normalmente, esse é um local para manipular ainda mais o DOM renderizado.

Faz:

  • Manipular elementos DOM (renderizados e, portanto, instanciados).
  • Anexe manipuladores de events.
  • Inspecione elementos filho.
  • Configure observações sobre atributos.
  • Configurar relógios no escopo.

Função do controlador

A function de controller cada diretiva é chamada sempre que um novo elemento relacionado é instanciado.

Oficialmente, a function do controller é onde um:

  • Define a lógica do controlador (methods) que podem ser compartilhados entre os controladores.
  • Inicia variables ​​de escopo.

Novamente, é importante lembrar que, se a diretiva envolver um escopo isolado, todas as propriedades dentro dele que herdarem do escopo pai ainda não estarão disponíveis.

Faz:

  • Definir lógica do controlador
  • Iniciar variables ​​de escopo

Não:

  • Inspecionar elementos filho (eles podem não ser renderizados ainda, vinculados ao escopo etc.).

Função de pré-link

A function de pre-link cada diretiva é chamada sempre que um novo elemento relacionado é instanciado.

Como visto anteriormente na seção de ordem de compilation, pre-link funções de pre-link são chamadas de parent-then-child, enquanto post-link funções de post-link são chamadas de child-then-parent .

A function de pre-link é raramente usada, mas pode ser útil em cenários especiais; por exemplo, quando um controlador filho se registra com o controlador pai, mas o registro deve estar em uma forma parent-then-child ( ngModelController faz as coisas dessa maneira).

Não:

  • Inspecionar elementos filho (eles podem não ser renderizados ainda, vinculados ao escopo etc.).