Usually when you use instructions in ng, the link function that most uses is the link attribute. The following article will tell you the usage and differences between compli, pre-link, and post-link.
The instructions in angularjs are very magical, allowing you to create very semantic and highly reusable components, which can be understood as the pioneer of web components.
There are already many articles and related books on the Internet that introduce how to use instructions. If you compare them, you rarely introduce the difference between compile and link, let alone pre-link and post-link.
Most tutorials simply say that compile will be used inside ng, and it is recommended that you only use the link attribute, which is the case in most instructions.
This is very unfortunate, because correctly understanding the difference between these functions will improve your understanding of the internal running mechanism of ng and help you develop better custom instructions.
So follow me to see the following content step by step to understand what these functions are and when they should be used
This article assumes that you have a certain understanding of the instructions. If not, it is strongly recommended that you read this article AngularJS developer guide section on directives
How to process instructions in NG
Before starting the analysis, let’s see how ng-Chinese handles instructions.
When the browser renders a page, it essentially reads the html identity, then creates a dom node, and broadcasts an event to us after the dom tree is created.
When you use script tags to load ng application code in the page, ng listens to the above dom completion event and finds elements with ng-app attributes.
After finding such an element, ng starts processing dom with the starting point of this element, so if ng-app is added to the html element, ng will start processing dom with html element.
Starting from this starting point, ng starts to recursively search all child elements, which complies with the instruction rules defined in the application.
How to deal with ng's instructions actually depends on the object properties it defines. You can define a compile or a link function, or use pre-link and post-link functions instead of link.
So what's the difference between these functions? Why use it? And when?
With these questions, follow me step by step to answer these mysteries
A piece of code
To explain the differences between these functions, I will use a simple and easy-to-understand example below
1. If you have any questions, please don’t hesitate to add your comments below.
Check out the following html tag code
The code copy is as follows:
<level-one>
<level-two>
<level-three>
Hello
</level-three>
</level-two>
</level-one>
Then there is a piece of js code
The code copy is as follows:
var app = angular.module('plunker', []);
function createDirective(name){
return function(){
return {
restrict: 'E',
compile: function(tElem, tAttrs){
console.log(name + ': compile');
return {
pre: function(scope, iElem, iAttrs){
console.log(name + ': pre link');
},
post: function(scope, iElem, iAttrs){
console.log(name + ': post link');
}
}
}
}
}
}
app.directive('levelOne', createDirective('levelOne'));
app.directive('levelTwo', createDirective('levelTwo'));
app.directive('levelThree', createDirective('levelThree'));
The result is very simple: let ng handle three nested instructions, and each instruction has its own complile, pre-link, and post-link functions. Each function will print a line in the console to identify itself.
This example allows us to briefly understand the internal process of ng when processing instructions
Code output
Below is a screenshot of the output result on the console
If you want to try this example yourself, please click this plnkr and then view the results in the console.
Analysis code
The first thing to note is the order of call of these functions:
The code copy is as follows:
// COMPILE PHASE
// levelOne: compile function is called
// levelTwo: compile function is called
// levelThree: compile function is called
// PRE-LINK PHASE
// levelOne: pre link function is called
// levelTwo: pre link function is called
// levelThree: pre link function is called
// POST-LINK PHASE (Notice the reverse order)
// levelThree: post link function is called
// levelTwo: post link function is called
// levelOne: post link function is called
This example clearly shows that ng compiles all instructions before link, and then link is divided into pre-link and post-link stages.
Note that the execution order of compile and pre-link are executed sequentially, but post-link is exactly the opposite.
So the above has clearly identified different stages, but what is the difference between compile and pre-link? They are both in the same execution order, so why do we have to divide them into two different functions?
DOM
In order to dig deeper, let's simply modify the above code, which will also print element variables in the parameter list in each function
The code copy is as follows:
var app = angular.module('plunker', []);
function createDirective(name){
return function(){
return {
restrict: 'E',
compile: function(tElem, tAttrs){
console.log(name + ': compile => ' + tElem.html());
return {
pre: function(scope, iElem, iAttrs){
console.log(name + ': pre link => ' + iElem.html());
},
post: function(scope, iElem, iAttrs){
console.log(name + ': post link => ' + iElem.html());
}
}
}
}
}
}
app.directive('levelOne', createDirective('levelOne'));
app.directive('levelTwo', createDirective('levelTwo'));
app.directive('levelThree', createDirective('levelThree'));
Pay attention to the output in console.log, except for the output of the original html tag, there is basically no other change.
This should deepen our understanding of the context of these functions.
Run the code again to see
Output
Below is a screenshot of the output result on the console
If you still want to run it yourself to see the effect, you can click this plnkr and then view the output results in the console.
observe
The output of the dom result can expose some interesting things: the dom content is different in the two functions compile and pre-link
So what happened?
Compile
We already know that when ng finds that the dom build is completed, we start processing the dom.
So when ng is traversing the dom, he encounters the level-one element and learns from its definition that some necessary functions need to be executed
Because the compile function is defined in the instruction object of the level-one instruction, it will be called and passed an element object as its parameter
If you look closely, you will see that when the browser creates this element object, it is still the most original html tag.
1. In ng, the original dom is usually used to identify template element, so I used the tElem name when defining the compile function parameters, and this variable points to the template element.
Once the compile function in the levelone instruction is run, ng will recursively traverse its dom nodes in depth, and then repeat these operations on level-two and level-three.
Post-link
Before we get into the pre-link function, let's take a look at the post-link function.
2. If you only use one link function when defining the instruction, then ng will treat this function as post-link, so we need to discuss this function first.
After ng has traversed all doms and ran all compile functions, the associated post-link function is called in reverse.
The dom now starts to reverse and executes the post-link function. Therefore, this reverse call seemed a bit strange before, but it actually makes sense to do so.
When running the post-link instruction containing sub-instructions, the reverse post-link rule can ensure that the post-link of its sub-instructions has been run.
Therefore, when running the post-link function of the level-one instruction, we can ensure that the post-link of level-two and level-three has actually been run.
This is why people think post-link is the safest or default place to write business logic.
But why is the element here different from the one in compile?
Once ng calls the compile function of the instruction, an element instance object of template element will be created and a scope object is provided for it. This scope may be a new instance, or it may already exist, it may be a sub scope, or it may be an independent scope. These all depend on the scope attribute value in the instruction definition object.
So when linking occurs, this instance element and scope object are already available, and are passed as parameters by ng to the parameter list of the post-link function.
1. I personally always use the iElem name to define the parameter of a link function, and it points to the element instance
Therefore, the element parameter object of the post-link(pre-link) function is an element instance instead of a template element.
So the output in the above example is different
Pre-link
When writing a post-link function, you can ensure that when executing the post-link function, the post-link functions of all its child instructions have been executed.
In most cases, it can do better, so we usually use it to write instruction code.
However, ng provides us with an additional hook mechanism, that is, the pre-link function, which can ensure that some other code is run before executing the post-link function of all sub-instructions.
This sentence is worthy of repeated consideration
The pre-link function can ensure that it is executed on the element instance and before the post-link of its sub-instructions runs.
So it makes sense to reverse the post-link function, it executes the pre-link function in the original order.
This also means that the pre-link function runs before the pre-link function of all its sub-instructions, so the complete reason is:
The pre-link function of an element can be guaranteed to be executed before the post-link and pre-link of all its sub-instructions are run. See the figure below:
review
If we look back at the original output above, we can clearly recognize what is going on:
The code copy is as follows:
// HERE THE ELEMENTS ARE STILL THE ORIGINAL TEMPLATE ELEMENTS
// COMPILE PHASE
// levelOne: compile function is called on original DOM
// levelTwo: compile function is called on original DOM
// levelThree: compile function is called on original DOM
// AS OF HERE, THE ELEMENTS HAVE BEEN INSTANTIATED AND
// ARE BOUND TO A SCOPE
// (EG NG-REPEAT WOULD HAVE MULTIPLE INSTANCES)
// PRE-LINK PHASE
// levelOne: pre link function is called on element instance
// levelTwo: pre link function is called on element instance
// levelThree: pre link function is called on element instance
// POST-LINK PHASE (Notice the reverse order)
// levelThree: post link function is called on element instance
// levelTwo: post link function is called on element instance
// levelOne: post link function is called on element instance
summary
Looking back at the above analysis, we can describe the differences and usage of these functions:
Compile function
Use the compile function to change the original dom (template element), before ng creates the original dom instance and scope instance.
It can be applied to situations where multiple element instances need to be generated and there is only one template element. ng-repeat is the best example. It changes the original dom in the compile function stage to generate multiple original dom nodes, and then each generates element instances. Because compile will only run once, it can improve performance when you need to generate multiple element instances.
The template element and related attributes are passed as parameters to the compile function, but scope cannot be used at this time:
Here is the function look like:
The code copy is as follows:
/**
* Compile function
*
* @param tElem - template element
* @param tAttrs - attributes of the template element
*/
function(tElem, tAttrs){
// ...
};
Pre-link function
Use the pre-link function to run some business code after ng has executed the compile function, but before the post-link function of all its sub-instructions is about to be executed.
The scope object and element instance will be passed as parameters to the pre-link function:
Here is the function look like:
The code copy is as follows:
/**
* Pre-link function
*
* @param scope - scope associated with this istance
* @param iElem - instance element
* @param iAttrs - attributes of the instance element
*/
function(scope, iElem, iAttrs){
// ...
};
Post-link function
Use the post-link function to execute business logic. At this stage, it already knows that all its sub-instructions have been compiled and that the pre-link and post-link functions have been executed.
This is what is considered the safest and default writing business logic code.
The scope instance and element instance are passed as parameters to the post-link function:
Here is the function look like:
The code copy is as follows:
/**
* Post-link function
*
* @param scope - scope associated with this istance
* @param iElem - instance element
* @param iAttrs - attributes of the instance element
*/
function(scope, iElem, iAttrs){
// ...
};
Summarize
Now you should have a clear understanding of the differences between compile, pre-link, post-link, and this function.
If you haven't already, and you're a serious ng developer, then I highly recommend that you read this article again until you understand it
Understanding these concepts is very important, which can help you understand how ng native instructions work, and can also help you optimize your own custom instructions.
If you have any questions, please add your questions in the comments below
In the future, we will continue to analyze two other questions about the instructions:
1. How does directive use transclusion attributes work?
2. How is the controller function of the instruction associated?
Finally, if you find something wrong with this article, please comment me in time
Thanks!