Qual é a diferença entre compilar e ligar function em angularjs

Alguém pode explicar em termos simples?

Os documentos parecem um pouco obtusos. Eu não estou recebendo a essência e a grande figura de quando usar um sobre o outro. Um exemplo que contrasta os dois seria fantástico.

  • function de compilation – use para manipulação de DOM de modelo (ou seja, manipulação de elemento tElement = template), portanto, manipulações que se aplicam a todos os clones DOM do modelo associado à diretiva.

  • function de link – use para registrar ouvintes DOM (isto é, expressões $ watch no escopo de instância) bem como manipulação de DOM de instância (ou seja, manipulação de elemento de instância individual iElement).
    É executado depois que o modelo foi clonado. Por exemplo, dentro de uma

  • , a function de binding é executada após o modelo
  • (tElement) ter sido clonado (em um iElement) para esse elemento
  • específico.
    Um $ watch () permite que uma diretiva seja notificada de alterações de propriedade de escopo de instância (um escopo de instância é associado a cada instância), o que permite que a diretiva renderize um valor de instância atualizado para o DOM – copiando o conteúdo do escopo da instância o DOM.

Observe que as transformações DOM podem ser feitas na function de compilation e / ou na function de link.

A maioria das diretivas só precisa de uma function de link, já que a maioria das diretivas trata apenas de uma instância de elemento DOM específica (e seu escopo de instância).

Uma maneira de ajudar a determinar qual usar: considere que a function de compilation não recebe um argumento de scope . (Estou propositalmente ignorando o argumento de function de vinculação de transclude, que recebe um escopo transcluído – isso raramente é usado.) Portanto, a function de compilation não pode fazer nada que você queira fazer que exija um escopo (instância) – você pode Observe todas as propriedades de escopo de modelo / instância, você não pode manipular o DOM usando informações de escopo de instância, você não pode chamar funções definidas no escopo da instância, etc.

No entanto, a function de compilation (como a function de link) tem access aos atributos. Portanto, se as manipulações do DOM não exigirem o escopo da instância, você poderá usar uma function de compilation. Aqui está um exemplo de uma diretiva que usa apenas uma function de compilation, por esses motivos. Ele examina os atributos, mas não precisa de um escopo de instância para fazer seu trabalho.

Aqui está um exemplo de uma diretiva que também usa apenas uma function de compilation. A diretiva só precisa transformar o modelo DOM, portanto, uma function de compilation pode ser usada.

Outra maneira de ajudar a determinar qual usar: se você não usar o parâmetro “element” na function de link, provavelmente não precisará de uma function de link.

Como a maioria das diretivas tem uma function de link, não fornecerei exemplos – elas devem ser muito fáceis de encontrar.

Observe que, se você precisar de uma function de compilation e de link (ou funções de link de pré e pós), a function de compilation deve retornar as funções de link porque o atributo ‘link’ é ignorado se o atributo ‘compile’ estiver definido.

Veja também

  • Diferença entre as funções ‘controller’, ‘link’ e ‘compile’ ao definir uma diretiva
  • O excelente ng-conf 2104 de Dave Smith fala sobre diretivas (o link vai para a seção do vídeo que fala sobre compilation e link)

Eu bati minha cabeça contra a parede por alguns dias, e sinto que um pouco mais de explicação está em ordem.

Basicamente, os documentos mencionam que a separação é em grande parte um aprimoramento de desempenho. Gostaria de reiterar que a fase de compilation é usada principalmente quando você precisa modificar o DOM ANTES que os subelementos sejam compilados.

Para nossos propósitos, vou enfatizar a terminologia, que é confusa:

O compilador SERVICE ($ compile) é o mecanismo angular que processa o DOM e executa os vários bits de código nas diretivas.

A compilation FUNCTION é um bit de código dentro de uma diretiva, que é executada em um determinado momento pelo compilador SERVICE ($ compile).

Algumas notas sobre a function compilar:

  1. Você não pode modificar o elemento ROOT (aquele que sua diretiva afeta), já que ele já está sendo compilado a partir do nível externo do DOM (a compilation SERVICE já varreu as diretivas daquele elemento).

  2. Se você quiser adicionar outras diretivas a elementos (nesteds), você:

    1. Tem que adicioná-los durante a fase de compilation.

    2. Tem que injetar o serviço de compilation na fase de vinculação e compilar os elementos manualmente. MAS, cuidado com a compilation de algo duas vezes!

Também é útil ver como o aninhamento e as chamadas explícitas para $ compile funcionam, então criei um playground para visualizá-lo em http://jsbin.com/imUPAMoV/1/edit . Basicamente, ele apenas registra as etapas no console.log.

Eu declararei os resultados do que você veria naquela checkbox aqui. Para um DOM de diretivas personalizadas tp e sp aninhadas da seguinte forma:

     

Compile Angular SERVICE chamará:

 tp compile sp compile tp pre-link sp pre-link sp post-link tp post-link 

O código jsbin também tem a function tp post-link explicitamente chamada de compile SERVICE em uma terceira diretiva (up), que faz todas as três etapas no final.

Agora, quero percorrer alguns cenários para mostrar como é possível usar a compilation e o link para fazer várias coisas:

CENÁRIO 1: Directiva como MACRO

Você deseja adicionar uma diretiva (digamos ng-show) dinamicamente a algo em seu modelo que possa derivar de um atributo.

Digamos que você tenha um templateUrl que aponte para:

 

e você quer uma diretiva personalizada:

  

que transforma o DOM nisso:

 

Basicamente, você quer reduzir o clichê com alguma estrutura de modelo consistente que sua diretiva possa interpretar. Em outras palavras: você quer uma macro.

Este é um ótimo uso para a fase de compilation, já que você pode basear todas as manipulações do DOM em coisas que você conhece apenas dos atributos. Basta usar o jQuery para adicionar os atributos:

 compile: function(tele, tattr) { var span = jQuery(tele).find('span').first(); span.attr('ng-show', tattr.model + ".visible." + tattr.name); ... return { pre: function() { }, post: function() {} }; } 

A seqüência de operações será (você pode ver isso através do jsbin mencionado anteriormente):

  1. O serviço de compilation encontra meu campo
  2. Ele chama o FUNCTION de compilation na diretiva, que atualiza o DOM.
  3. A compilation SERVICE entra no DOM resultante e COMPILES (recursivamente)
  4. O compile SERVICE chama então o pre-link top-down
  5. A compile SERVICE chama então o post-link BOTTOM UP, então a function de link do meu campo é chamada de AFTER: os nós interiores foram ligados.

No exemplo acima, nenhuma vinculação é necessária, uma vez que todo o trabalho da diretiva foi feito na function compilar.

A qualquer momento, o código em uma diretiva pode solicitar que o compilador SERVICE seja executado em elementos adicionais.

Isso significa que podemos fazer exatamente a mesma coisa em uma function de link se você injetar o serviço de compilation:

 directive('d', function($compile) { return { // REMEMBER, link is called AFTER nested elements have been compiled and linked! link: function(scope, iele, iattr) { var span = jQuery(iele).find('span').first(); span.attr('ng-show', iattr.model + ".visible." + iattr.name); // CAREFUL! If span had directives on it before // you will cause them to be processed again: $compile(span)(scope); } }); 

Se você tem certeza de que os elementos que você está passando para $ compile SERVICE originalmente eram livres de diretivas (por exemplo, eles vieram de um template que você definiu ou você os criou com angular.element ()), então o resultado final é praticamente o mesmo que antes (embora você possa estar repetindo algum trabalho). No entanto, se o elemento tiver outras diretivas, você fará com que elas sejam processadas novamente, o que pode causar todos os tipos de comportamento errático (por exemplo, registro duplo de events e relógios).

Assim, a fase de compilation é uma escolha muito melhor para o trabalho de estilo macro.

CENÁRIO 2: Configuração do DOM por meio de dados de escopo

Este segue do exemplo acima. Suponha que você precise de access ao escopo durante a manipulação do DOM. Bem, nesse caso, a seção de compilation é inútil para você, já que isso acontece antes que um escopo esteja disponível.

Portanto, digamos que você queira extrair uma input com validações, mas deseja exportar suas validações de uma class ORM do lado do servidor (DRY) e fazer com que elas se apliquem automaticamente e gerem a UI do lado do cliente adequada para essas validações.

Seu modelo pode empurrar:

 scope.metadata = { validations: { address: [ { pattern: '^[0-9]', message: "Address must begin with a number" }, { maxlength: 100, message: "Address too long" } ] } }; scope.state = { address: '123 Fern Dr' }; 

e você pode querer uma diretiva:

 

para include automaticamente as diretivas e divs corretas para mostrar os vários erros de validação:

 
Address must begin with a number ...

Nesse caso, você definitivamente precisa acessar o escopo (já que é onde suas validações são armazenadas), e terá que compilar as adições manualmente, novamente tomando cuidado para não duplicar as coisas. (como uma nota lateral, você precisaria definir um nome na tag de formulário contendo (estou assumindo oForm aqui), e poderia acessá-lo em um link com iElement.parent (). controller (‘form’). $ name) .

Neste caso, não há sentido em escrever uma function de compilation. Link é realmente o que você quer. Os passos seriam:

  1. Defina um modelo que seja completamente desprovido de diretivas angulares.
  2. Definir uma function de link que adiciona os vários atributos
  3. REMOVA quaisquer diretivas angulares que você possa permitir em seu elemento de nível superior (a diretiva my-field). Eles já foram processados ​​e essa é uma maneira de impedi-los de serem processados ​​duas vezes.
  4. Conclua chamando o serviço de compilation no seu elemento de nível superior

Igual a:

 angular.module('app', []). directive('my-field', function($compile) { return { link: function(scope, iele, iattr) { // jquery additions via attr() // remove ng attr from top-level iele (to avoid duplicate processing) $compile(iele)(scope); // will pick up additions } }; }); 

Você poderia, é claro, compilar os elementos nesteds um a um para evitar ter que se preocupar com o processamento duplicado de diretivas ng ao compilar o elemento de nível superior novamente.

Uma nota final sobre este cenário: Eu sugeri que você estaria empurrando a definição das validações de um servidor e, no meu exemplo, mostrei-as como dados já no escopo. Deixo como um exercício para o leitor descobrir como alguém pode lidar com a necessidade de extrair esses dados de uma API REST (dica: compilation adiada).

CENÁRIO 3: vinculação de dados bidirecional via link

É claro que o uso mais comum do link é simplesmente conectar a binding de dados bidirecional via watch / apply. A maioria das diretivas se enquadra nessa categoria, portanto, é adequadamente abordada em outros lugares.

Dos docs:

Compilador

Compiler é um serviço angular que percorre o DOM procurando por atributos. O processo de compilation acontece em duas fases.

  1. Compile: percorra o DOM e colete todas as diretivas. O resultado é uma function de vinculação.

  2. Link: combine as diretivas com um escopo e produza uma exibição ao vivo. Quaisquer alterações no modelo de escopo são refletidas na exibição e qualquer interação do usuário com a exibição é refletida no modelo de escopo. Tornar o modelo de escopo uma única fonte de verdade.

Algumas diretivas como ng-repeat clonam elementos DOM uma vez para cada item na coleção. Ter uma fase de compilation e link melhora o desempenho, pois o modelo clonado só precisa ser compilado uma vez e, em seguida, vinculado uma vez para cada instância de clone.

Então, pelo menos em alguns casos, as duas fases existem separadamente como uma otimização.


De @ UmurKontacı :

Se você vai fazer transformações DOM, deve ser compile . Se você quiser adicionar alguns resources que são mudanças de comportamento, ele deve estar no link .

Isto é da palestra de Misko sobre diretivas. http://youtu.be/WqmeI5fZcho?t=16m23s

Pense na function de compilador como a coisa que funciona em um modelo e a coisa que tem permissão para alterar o próprio modelo, por exemplo, adicionando uma class a ele ou algo do tipo. Mas é a function de vinculação que realmente faz o trabalho de vincular os dois juntos porque a function de vinculação tem access ao escopo e é a function de vinculação que é executada uma vez para cada instanciação do modelo específico. Portanto, o único tipo de coisas que você pode colocar dentro das funções de compilation são coisas comuns em todas as instâncias.

Pouco atrasado para o fio. Mas, para o benefício dos futuros leitores:

Eu me deparei com o seguinte vídeo que explica Compile e Link em Angular JS de uma forma muito boa:

https://www.youtube.com/watch?v=bjFqSyddCeA

Não seria agradável copiar / digitar todo o conteúdo aqui. Eu tirei algumas capturas de canvas do vídeo, o que explica cada estágio das fases Compile e Link:

Compilar e vincular em JS Angular

Compilar e vincular em diretivas angular JS aninhadas

A segunda canvas é um pouco confusa. Mas, se seguirmos a numeração dos passos, é bastante simples.

Primeiro ciclo: “Compile” é executado em todas as diretivas primeiro.
Segundo ciclo: “Controlador” e “Pre-Link” são executados (apenas um após o outro) Terceiro ciclo: “Post-Link” é executado na ordem inversa (começando do mais interno)

A seguir está o código, que demonstra o acima:

 var app = angular.module ('app', []);

 app.controller ('msg', ['$ scope', function ($ scope) {

 }]);

 app.directive ('message', function ($ interpolate) {
     Retorna{

         compile: function (tElement, tAttributes) { 
             console.log (tAttributes.text + "- em compilation ..");
             Retorna {

                 pre: function (scope, iElement, iAttributes, controller) {
                     console.log (iAttributes.text + "-In pre ..");
                 }

                 post: function (scope, iElement, iAttributes, controller) {
                     console.log (iAttributes.text + "-In Post ..");
                 }

             }
         }

         controller: function ($ scope, $ elemento, $ attrs) {
             console.log ($ attrs.text + "-In controller ..");
         }

     }
 });
  

ATUALIZAR:

A parte 2 do mesmo vídeo está disponível aqui: https://www.youtube.com/watch?v=1M3LZ1cu7rw O vídeo explica mais sobre como modificar o DOM e lidar com events durante o processo Compile e Link do Angular JS, em um exemplo simples .

Duas fases: compilar e vincular

Compilar:

Atravessar a tree DOM procurando por diretivas (elementos / atributos / classs / comentários). Cada compilation de uma diretiva pode modificar seu modelo ou modificar seu conteúdo que ainda não foi compilado. Quando uma diretiva é correspondida, ela retorna uma function de vinculação, que é usada em uma fase posterior para vincular os elementos. No final da fase de compilation, temos uma lista de diretivas compiladas e suas correspondentes funções de vinculação.

Ligação:

Quando um elemento é vinculado, a tree DOM é quebrada em seu ponto de ramificação na tree DOM e o conteúdo é substituído pela instância compilada (e vinculada) do modelo. O conteúdo original deslocado é descartado ou, no caso de transclusão, é novamente ligado ao modelo. Com a transclusão, as duas partes são ligadas juntas (como uma corrente, com a peça do modelo no meio). Quando a function de binding é chamada, o modelo já foi vinculado a um escopo e adicionado como filho do elemento. A function de link é a sua oportunidade de manipular o DOM e configurar ouvintes de mudança.

Esta questão é antiga, gostaria de fazer um pequeno resumo que pode ajudar:

  • Compile chamada de instância de diretiva de uma vez por todas
  • A principal finalidade da compilation é retornar / criar o link (e possivelmente pré / pós) function / object. Você também pode iniciar coisas que são compartilhadas entre instâncias da diretiva.
  • Na minha opinião, “link” é um nome confuso para esse recurso. Eu preferiria “pré-renderizar”.
  • link é chamado para cada instância da diretiva e sua finalidade é preparar a renderização da diretiva no DOM.