Qual é a diferença entre a função de compilação e link em angularjs

208

Alguém pode explicar em termos simples?

Os documentos parecem um pouco obtusos. Não estou obtendo a essência e o panorama geral de quando usar um sobre o outro. Um exemplo contrastando os dois seria incrível.

numan salati
fonte
2
Talvez uma visão mais abrangente das funções de diretiva: Diretivas angulares - quando usar compilação, controlador, pré-link e pós-link .
Izhaki 07/07
jvandemo.com/…
EMuentes 4/04

Respostas:

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

  • função de link - use para registrar ouvintes DOM (por exemplo, expressões $ watch no escopo da instância), bem como manipulação DOM da instância ( por exemplo , manipulação de iElement = elemento de instância individual).
    É executado após a clonagem do modelo. Por exemplo, dentro de um <li ng-repeat ...>, a função de link é executada depois que o modelo <li> (tElement) foi clonado (em um iElement) para esse elemento <li> específico.
    Um $ watch () permite que uma diretiva seja notificada de alterações na propriedade do escopo da instância (um escopo da instância está associado a cada instância), o que permite à diretiva renderizar um valor atualizado da instância para o DOM - copiando o conteúdo do escopo da instância para o DOM.

Observe que as transformações do DOM podem ser feitas na função de compilação e / ou na função de link.

A maioria das diretivas precisa apenas de uma função de link, uma vez que a maioria das diretivas lida apenas com uma instância específica do elemento DOM (e seu escopo de instância).

Uma maneira de ajudar a determinar qual usar: considere que a função de compilação não recebe um scopeargumento. (Estou intencionalmente ignorando o argumento da função de vinculação de transclude, que recebe um escopo transcluído - isso raramente é usado.) Portanto, a função de compilação não pode fazer o que você deseja que exija um escopo (instância) - você pode Não observe nenhuma propriedade de escopo de modelo / instância, você não pode manipular o DOM usando informações de escopo de instância, não pode chamar funções definidas no escopo de instância, etc.

No entanto, a função de compilação (como a função de link) tem acesso aos atributos. Portanto, se suas manipulações DOM não exigirem o escopo da instância, você poderá usar uma função de compilação. Aqui está um exemplo de diretiva que usa apenas uma função de compilação, 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 diretiva que também usa apenas uma função de compilação. A diretiva precisa apenas transformar o DOM do modelo, para que uma função de compilação possa ser usada.

Outra maneira de ajudar a determinar qual usar: se você não usar o parâmetro "elemento" na função de link, provavelmente não precisará de uma função de link.

Como a maioria das diretivas possui uma função de link, não fornecerei exemplos - elas devem ser muito fáceis de encontrar.

Observe que se você precisar de uma função de compilação e de uma função de link (ou funções de pré e pós-link), a função de compilação deverá retornar as funções de link porque o atributo 'link' será ignorado se o atributo 'compile' estiver definido.

Veja também

Mark Rajcok
fonte
5
Melhor explicação sobre compilação vs link.
Nexus23
1
Quando você diz if you don't use the "element" parameter in the link function, then you probably don't need a link function.que quer dizer "escopo" em vez de "elemento"?
Jason Larke
69

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

Basicamente, os documentos mencionam que a separação é amplamente um aprimoramento de desempenho. Eu reiteraria que a fase de compilação é usada principalmente quando você precisa modificar o DOM ANTES de os subelementos serem 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 função de compilação é um bit de código dentro de uma diretiva, que é executada em um determinado momento pelo compilador SERVICE ($ compile).

Algumas notas sobre a função de compilação:

  1. Você não pode modificar o elemento ROOT (aquele afetado pela sua diretiva), pois ele já está sendo compilado a partir do nível externo do DOM (o SERVICE de compilação já examinou as diretivas nesse elemento).

  2. Se você deseja adicionar outras diretivas aos elementos (aninhados), você pode:

    1. Tem que adicioná-los durante a fase de compilação.

    2. É necessário injetar o serviço de compilação na fase de vinculação e compilar os elementos manualmente. MAS, cuidado com a compilação de algo duas vezes!

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

Vou indicar os resultados do que você veria nessa lixeira aqui. Para um DOM de diretivas personalizadas tp e sp aninhadas da seguinte maneira:

<tp>
   <sp>
   </sp>
</tp>

O serviço de compilação angular chamará:

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

O código jsbin também possui a função de pós-link tp chamada explicitamente o SERVICE de compilação em uma terceira diretiva (up), que executa todas as três etapas no final.

Agora, quero percorrer alguns cenários para mostrar como alguém pode usar o link de compilação e fazer várias coisas:

CENÁRIO 1: Diretiva 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:

<div><span><input type="text"></span><div>

e você deseja uma diretiva personalizada:

<my-field model="state" name="address"></my-field>

que transforma o DOM nisso:

<div><span ng-show="state.visible.address"><input ng-model="state.fields.address" ...>

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

Esse é um ótimo uso para a fase de compilação, pois você pode basear todas as manipulações do DOM em coisas que você conhece apenas pelos 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 sequência de operações será (você pode ver isso através do jsbin mencionado anteriormente):

  1. O serviço de compilação encontra meu campo
  2. Ele chama a função de compilação na diretiva, que atualiza o DOM.
  3. O serviço de compilação entra no DOM resultante e COMPILES (recursivamente)
  4. O SERVIÇO de compilação chama o pré-link de cima para baixo
  5. O SERVIÇO de compilação chama o BOTTOM UP do pós-link, para que a função de link do meu campo seja chamada APÓS os nós interiores terem sido vinculados.

No exemplo acima, nenhuma ligação é necessária, pois todo o trabalho da diretiva foi realizado na compilação FUNCTION.

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 função de link se você injetar o serviço de compilação:

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 o $ compile SERVICE originalmente eram livres de diretiva (por exemplo, eles vieram de um modelo que você definiu ou apenas os criaram com angular.element ()), o resultado final é praticamente o mesmo de antes (embora você possa estar repetindo algum trabalho). No entanto, se o elemento continha outras diretrizes, você apenas as processou novamente, o que pode causar todo tipo de comportamento irregular (por exemplo, registro duplo de eventos e relógios).

Assim, a fase de compilação é uma opção muito melhor para o trabalho no estilo macro.

CENÁRIO 2: Configuração do DOM via dados do escopo

Este segue o exemplo acima. Suponha que você precise acessar o escopo enquanto manipula o DOM. Bem, nesse caso, a seção de compilação é inútil para você, pois ocorre antes que um escopo esteja disponível.

Então, digamos que você queira exibir uma entrada com validações, mas deseja exportar suas validações de uma classe ORM do lado do servidor (DRY) e aplicá-las automaticamente e gerar a interface do usuário do lado do cliente adequada para essas validações.

Seu modelo pode pressionar:

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:

<form name="theForm">
  <my-field model="state" metadata="metadata" name="address">
</form>

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

<form name="theForm">
  <div>
    <input ng-model="state.address" type="text">
    <div ng-show="theForm.address.$error.pattern">Address must begin with a number</input>
...

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 compilar as coisas duas vezes. (como uma observação lateral, você precisaria definir um nome na tag de formulário que contém (estou assumindo o formulário aqui) e poderia acessá-lo em link com iElement.parent (). controller ('form'). $ name) .

Nesse caso, não faz sentido escrever uma função de compilação. Link é realmente o que você deseja. Os passos seriam:

  1. Defina um modelo completamente desprovido de diretivas angulares.
  2. Definir uma função de link que adicione os vários atributos
  3. REMOVA quaisquer diretivas angulares que você possa permitir no seu elemento de nível superior (a diretiva my-field). Eles já foram processados ​​e essa é uma maneira de impedir que eles sejam processados ​​duas vezes.
  4. Conclua chamando o serviço de compilação 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
    }
  };
});

Obviamente, você pode compilar os elementos aninhados um a um para evitar a preocupação com o processamento duplicado das diretivas ng ao compilar o elemento de nível superior novamente.

Uma observação final sobre esse cenário: sugeri que você estaria pressionando 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 é possível lidar com a necessidade de extrair esses dados de uma API REST (dica: compilação adiada).

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

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

Tony K.
fonte
2
Resposta impressionante e legal!
Nexus23
Como adicionar elementos aninhados sem compilá-los duas vezes?
Art713
50

Dos documentos:

Compilador

Compilador é um serviço angular que atravessa o DOM procurando atributos. O processo de compilação ocorre em duas fases.

  1. Compilar: percorra o DOM e colete todas as diretivas. O resultado é uma função 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 visualização e quaisquer interações do usuário com a visualização são refletidas no modelo de escopo. Transformar o modelo de escopo em uma única fonte de verdade.

Algumas diretivas ng-repeatclonam elementos DOM uma vez para cada item da coleção. Ter uma fase de compilação e link melhora o desempenho, pois o modelo clonado precisa ser compilado apenas uma vez e depois vinculado uma vez para cada instância do clone.

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


From @ UmurKontacı :

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

Matt Ball
fonte
46
Se você estiver indo para fazer DOMa transformação, deve ser compilese você quiser adicionar algumas características são as mudanças de comportamento, ele deve estar em link.
Umur Kontacı
4
+1 ao comentário acima; esta é a descrição mais concisa que encontrei até agora. Combina com o tutorial que encontrei aqui .
Benny Bottema
18

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

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

SunnyShah
fonte
10

Um pouco tarde para a discussão. Mas, para o benefício de futuros leitores:

Me deparei com o vídeo a seguir, que explica Compile e Link no Angular JS de uma maneira muito boa:

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

Não seria agradável copiar / digitar todo o conteúdo aqui. Tirei algumas capturas de tela do vídeo, que explicam todas as etapas das fases de Compilação e Link:

Compilar e vincular em JS angular

Compilar e vincular em JS angular - diretivas aninhadas

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

Primeiro ciclo: "Compilar" é executado em todas as diretivas primeiro.
Segundo ciclo: "Controlador" e "Pré-Link" são executados (apenas um após o outro) Terceiro ciclo: "Pós-Link" é executado em ordem inversa (a partir do interior)

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

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

app.controller ('msg', ['$ scope', função ($ scope) {

}]);

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

        compile: function (tElement, tAttributes) { 
            console.log (tAttributes.text + "- Na compilação ..");
            Retorna {

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

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

            }
        }

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

    }
});
<body ng-app="app">
<div ng-controller="msg">
    <div message text="first">
        <div message text="..second">
            <div message text="....third">

            </div>              
        </div>  
    </div>
</div>

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 manipular eventos durante o processo de Compilação e Link do Angular JS, em um exemplo simples .

user203687
fonte
Usado compilee postpara modificar um DOM antes que ele seja modificado em templateparte a partir de uma diretiva de fornecedor.
jedi
6

Duas fases: compilar e vincular

Compilar:

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

Ligação:

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

pixelbits
fonte
3

Esta questão é antiga por gostaria de fazer um breve resumo que pode ajudar:

  • Compilar chamado uma vez para todas as instâncias de diretiva
  • O principal objetivo da compilação é retornar / criar a função / objeto do link (e possivelmente pré / pós). Você também pode iniciar itens compartilhados entre instâncias da diretiva.
  • Na minha opinião, "link" é um nome confuso para esse recurso. Eu preferiria "pré-renderizar".
  • O link é chamado para cada instância da diretiva e seu objetivo é preparar a renderização da diretiva no DOM.
Kfir Erez
fonte
1
uma mais para o nome da sugestão: "pré-renderização"
Hailong Cao