
does angular create new DOM tree after compile phase or continue working with original modified DOM?
Question:
For example, I have the following HTML:
<div dir-parent>
<div dir-child1></div>
</div>
During compilation phase directive dirParent
will receive template DOM element that has inside of it the div
with a dirChild1
directive. If I don't change this html
inside dirParent
's compile function, then compile function is then called for dirChild1
directive. However, if I remove <div dir-child1="" some-child1-attr></div>
inside dirParent
's compile function, then angular continues processing this <em>modified</em> DOM that no longer has a div with dir-child1
directive and so compile function for dir-child1
directive is never executed.
I assumed that once angular is done executing compile functions it builds new DOM from html
<em>modified</em> during compile functions. Is this assumption correct? This <em>modified</em> DOM seems to be referred as <em>instance</em> DOM elements and is available during pre-link
phase, so I assume that this is the phase when angular constructs instance elements DOM tree.
I've spent 3 hours investigating Angular's source code (it's written in so hard to read manner) and found the answer to my question. Angular doesn't distinguish between template and instance elements inside, it works with the same tree it has when it begins compiling. Here is the essence of the source code that demonstrates that:
var element = $("body");
compile(element)($rootScope);
function compile(DOMElementsTree) {
var compositeLinkFn = compileNodes(DOMElementsTree);
return function publicLinkFn(scope) {
var $linkNode = DOMElementsTree;
compositeLinkFn(scope, $linkNode);
return $linkNode;
}
}
function compileNodes(nodesList) {
var linkFns = [];
nodesList.forEach(function (node) {
var nodeLinkFn = applyDirectivesToNode(node);
var childNodes = nodesList.childNodes;
var childLinkFn = !!childNodes.length ? compileNodes(childNodes) : null;
if (nodeLinkFn || childLinkFn) {
linkFns.push(i, nodeLinkFn, childLinkFn);
}
});
return function compositeLinkFn(scope, nodeList) {
linkFns.forEach(function (linkFn) {
var nodeLinkFn = linkFn[1];
var childNodeLinkFn = linkFn[2];
if (nodeLinkFn) {
nodeLinkFn(childLinkFn, scope, nodeList);
} else if (childNodeLinkFn) {
childLinkFn(scope, nodeList)
}
});
}
}
function applyDirectivesToNode() {
// this is where compile functions of all directives on a node are executed
}
function nodeLinkFn() {
// here pre link and post link functions are executed
}
function childLinkFn() {
// here pre link and post link functions are executed
}
You can see that applyDirectivesToNode
which executes compile
functions of a directive doesn't return any new DOM node, instead it modifies DOMElementsTree
that it receives by references and compositeLinkFn
continues working with this DOM instance.
Initially I had this piece of sample code in my question taking <a href="https://docs.angularjs.org/guide/compiler#how-directives-are-compiled" rel="nofollow">from here</a>:
var $compile = ...; // injected into your code
var scope = ...;
var parent = ...; // DOM element where the compiled template can be appended
var html = '<div ng-bind="exp"></div>';
// Step 1: parse HTML into DOM element
var template = angular.element(html);
// Step 2: compile the template
var linkFn = $compile(template);
// Step 3: link the compiled template with the scope.
var element = linkFn(scope);
// Step 4: Append to DOM (optional)
parent.appendChild(element);
So in this extract the last step deals with the result of the publicLinkFn
function, namely $linkNode
in my code above.
The DOM
element is indeed created during step1, however it is only available in the browser memory but not reflected in the actual page DOM.
Since the element has been created from Javascript (var html = ...
) once it is compiled and linked, you need to put it back to the page using append
so the user can actually see and interact with it.
<strong>Update</strong> I don't understand what you are asking exactly, but I don't think you will find an answer on StackOverflow. What you need is a forum or more lecture about the internal functionning of AngularJS. I highly recommend you to read this article: <a href="http://www.jvandemo.com/the-nitty-gritty-of-compile-and-link-functions-inside-angularjs-directives/" rel="nofollow">http://www.jvandemo.com/the-nitty-gritty-of-compile-and-link-functions-inside-angularjs-directives/</a>
<strong>Update2</strong> From my understanding (and accoding to the article cited above), Angular will first compile the template from dir-parent
and then compile its children <em>using that same template</em>. If you have modified this template during the top compile phase, for instance by removing dir-child1
, then indeed there is nothing else to compile and Angular will then proceed with the pre-link
and finally post-link
.