oninit - html compile angularjs




Diretrizes angulares-quando e como usar compile, controller, pre-link e post-link (6)

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
  • pós-link

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

Noções básicas 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 função?

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

  • Compile
  • Controller
  • Pre-link
  • Post-link

Perguntas relacionadas:


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 objeto contendo as funções pre-link e post-link; Na linguagem angular, dizemos que a função de compilação retorna uma função de modelo .

Compilar, Controlador e Post-link

Se o pre-link não for necessário, a função de compilação pode simplesmente retornar a função post-link em vez de um objeto 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 , após o método de link (post) ter sido 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 função de compilação é necessária, pode-se pular sua declaração completamente e fornecer a função post-link sob a propriedade link do objeto 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 função do controller , se não for necessário. Então, por exemplo, se apenas post-link função 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                 
        },          
    };  
});

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:

<body>
    <div log='some-div'></div>
</body>

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 função de link. Algumas diretivas angulares fazem isso (como ngIf, ngRepeat ou qualquer diretiva com transclude ). Essas diretivas terão nativamente sua função de link chamada antes que sua diretiva filha compile seja chamada.

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

<body>
    <div log='parent'>
        <div log='..first-child'></div>
        <div log='..second-child'></div>
    </div>
</body>

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 compilação e a fase de link .

A fase de compilação

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

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

A fase de ligação

Geralmente, as instâncias DOM são simplesmente 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 , itera os filhos, e chama post-link em todas as diretivas, assim:


Função de pós-link

Quando a função 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, assim, instanciados).
  • Anexe manipuladores de eventos.
  • Inspecione elementos filho.
  • Configure observações sobre atributos.
  • Configure relógios no escopo.

Função de pré-link

A função de pre-link cada diretiva é chamada sempre que um novo elemento relacionado é instanciado.

Como visto anteriormente na seção de ordem de compilação, 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 função 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.).

O que mais acontece entre essas chamadas de função?

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

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

<div ng-repeat="i in [0,1,2]">
    <my-element>
        <div>Inner content</div>
    </my-element>
</div>

Com a seguinte diretiva:

myApp.directive( 'myElement', function() {
    return {
        restrict:   'EA',
        transclude: true,
        template:   '<div>{{label}}<div ng-transclude></div></div>'
    }
});

Compilar

A API de compile parece assim:

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

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

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

<my-element>
    <div>
        "{{label}}"
        <div ng-transclude></div>
    </div>
</my-element>

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 ) { ... }

Entrando na fase de link, a função de link retornada via $compile agora é fornecida com um escopo.

Primeiro, a função 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 é semelhante a:

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

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

Após a chamada .preLink , a função de link percorrerá cada elemento filho - chamando a função 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 função de pre-link :

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

Talvez valha a pena notar que, uma vez que a função .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:

  • ligação de dados
  • transclusão aplicada
  • escopo anexado

O modelo nesse estágio será assim:

<my-element>
    <div class="ng-binding">
        "{{label}}"
        <div ng-transclude>                
            <div class="ng-scope">Inner content</div>
        </div>
    </div>
</my-element>

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 entrada no processo de compilação às vezes difere da saída. Particularmente, algumas marcações de entrada 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:

<div ng-repeat="i in [0,1,2]">
    <my-directive>{{i}}</my-directive>
</div>

A fonte html define

    <my-directive>{{i}}</my-directive>

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.





angularjs-directive