I'm currently writing some Angular directives that change their templates depending on certain attributes passed. They look structurally like this:
app.directive('mydir', function ($compile) {
return {
restrict: 'E',
replace: true,
transclude: true,
compile: function (elem, attrs, transclude) {
var replacement;
// operations
return function (scope, lElem, lAttrs) {
transclude(scope, function (clone, scope) {
// more operations
});
elem.replaceWith($compile(replacement)(scope)); // IMPORTANT
};
}
};
});
and are on this Plunkr. Now, as written (well, fleshed out), these directives don't work if you try to nest them. The inner directive ends up with its transcluded elements entirely erased. However, if I replace elem.replaceWith(...)
with lElem.replaceWith(...)
(on line 36 of the Javascript file in the Plunkr) everything is fine and I can nest as many times as I want. Why is this happening? What is the difference here? I understand that elem
is not compiled and lElem
is, but what does that actually mean and how is it affecting my code? Furthermore, isn't it discouraged to do DOM manipulations in the link
function?
What I've tried. I've tried to debug this myself and it looks like for the inner directive the compile function is called twice (I'd assume once from whatever angular is doing and once from when I use $compile
on it), that the first time it compiles everything is correct, and the second time everything is wrong. But I can't get it to only compile once without removing the $compile
statement, which breaks everything! It also looks like Angular compiles everything "on the way up" (i.e. children first) and links everything "on the way down" (i.e. parents first), but I can't confirm that. On a hunch I tried returning a prelink function instead of a postlink function, to no avail. I've also tried putting the elem.replaceWith(...)
call before the link
function and compiling the children inside the link
function, but that didn't work either.
What I think is happening. I think that putting the transclusion in the link function is messing everything up (though I am forced to because compile
has no scope) by taking the old clone inside the directive, and removing its children for some reason (elem
and lElem
have no children when referenced, for instance). I'm not sure how to confirm this or how to fix it once it's confirmed. I preliminarily did some testing by injecting $rootScope
and doing all the linking, etc in the compile function, and nothing worked. I'm not sure if there are any differences between compiling that and compiling with the child scope, though.