Skip to content
This repository has been archived by the owner on Apr 12, 2024. It is now read-only.

Support modifying the DOM structure in link function of a directive #1773

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 65 additions & 60 deletions src/ng/compile.js
Original file line number Diff line number Diff line change
Expand Up @@ -367,68 +367,73 @@ function $CompileProvider($provide) {
* @returns {?function} A composite linking function of all of the matched directives or null.
*/
function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority) {
var linkFns = [],
nodeLinkFn, childLinkFn, directives, attrs, linkFnFound;

for(var i = 0; i < nodeList.length; i++) {
attrs = new Attributes();

// we must always refer to nodeList[i] since the nodes can be replaced underneath us.
directives = collectDirectives(nodeList[i], [], attrs, maxPriority);

nodeLinkFn = (directives.length)
? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement)
: null;

childLinkFn = (nodeLinkFn && nodeLinkFn.terminal || !nodeList[i].childNodes.length)
? null
: compileNodes(nodeList[i].childNodes,
nodeLinkFn ? nodeLinkFn.transclude : transcludeFn);

linkFns.push(nodeLinkFn);
linkFns.push(childLinkFn);
linkFnFound = (linkFnFound || nodeLinkFn || childLinkFn);
}

// return a linking function if we have found anything, null otherwise
return linkFnFound ? compositeLinkFn : null;

function compositeLinkFn(scope, nodeList, $rootElement, boundTranscludeFn) {
var nodeLinkFn, childLinkFn, node, childScope, childTranscludeFn;

for(var i = 0, n = 0, ii = linkFns.length; i < ii; n++) {
node = nodeList[n];
nodeLinkFn = linkFns[i++];
childLinkFn = linkFns[i++];

if (nodeLinkFn) {
if (nodeLinkFn.scope) {
childScope = scope.$new(isObject(nodeLinkFn.scope));
jqLite(node).data('$scope', childScope);
} else {
childScope = scope;
}
childTranscludeFn = nodeLinkFn.transclude;
if (childTranscludeFn || (!boundTranscludeFn && transcludeFn)) {
nodeLinkFn(childLinkFn, childScope, node, $rootElement,
(function(transcludeFn) {
return function(cloneFn) {
var transcludeScope = scope.$new();

return transcludeFn(transcludeScope, cloneFn).
bind('$destroy', bind(transcludeScope, transcludeScope.$destroy));
var linkFns = [],
nodeLinkFn, childLinkFn, directives, attrs, linkFnFound;

for(var i = 0; i < nodeList.length; i++) {
attrs = new Attributes();

// we must always refer to nodeList[i] since the nodes can be replaced underneath us.
directives = collectDirectives(nodeList[i], [], attrs, maxPriority);

nodeLinkFn = (directives.length)
? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement)
: null;

childLinkFn = (nodeLinkFn && nodeLinkFn.terminal || !nodeList[i].childNodes.length)
? null
: compileNodes(nodeList[i].childNodes,
nodeLinkFn ? nodeLinkFn.transclude : transcludeFn);

linkFns.push(nodeLinkFn);
linkFns.push(childLinkFn);
linkFnFound = (linkFnFound || nodeLinkFn || childLinkFn);
}

// return a linking function if we have found anything, null otherwise
return linkFnFound ? compositeLinkFn : null;

function compositeLinkFn(scope, nodeList, $rootElement, boundTranscludeFn) {
var nodeLinkFn, childLinkFn, node, childScope, childTranscludeFn;
// Copy nodeList so that linking doesn't break due to live list updates.
var stableNodeList = [];
for (var i = 0, ii = nodeList.length; i < ii; i++) {
stableNodeList.push(nodeList[i]);
}

for(var i = 0, n = 0, ii = linkFns.length; i < ii; n++) {
node = stableNodeList[n];
nodeLinkFn = linkFns[i++];
childLinkFn = linkFns[i++];

if (nodeLinkFn) {
if (nodeLinkFn.scope) {
childScope = scope.$new(isObject(nodeLinkFn.scope));
jqLite(node).data('$scope', childScope);
} else {
childScope = scope;
}
childTranscludeFn = nodeLinkFn.transclude;
if (childTranscludeFn || (!boundTranscludeFn && transcludeFn)) {
nodeLinkFn(childLinkFn, childScope, node, $rootElement,
(function(transcludeFn) {
return function(cloneFn) {
var transcludeScope = scope.$new();

return transcludeFn(transcludeScope, cloneFn).
bind('$destroy', bind(transcludeScope, transcludeScope.$destroy));
};
})(childTranscludeFn || transcludeFn)
);
} else {
nodeLinkFn(childLinkFn, childScope, node, undefined, boundTranscludeFn);
}
} else if (childLinkFn) {
childLinkFn(scope, node.childNodes, undefined, boundTranscludeFn);
}
}
}
}
);
} else {
nodeLinkFn(childLinkFn, childScope, node, undefined, boundTranscludeFn);
}
} else if (childLinkFn) {
childLinkFn(scope, node.childNodes, undefined, boundTranscludeFn);
}
}
}
}


/**
Expand Down
18 changes: 18 additions & 0 deletions test/ng/compileSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,24 @@ describe('$compile', function() {
expect(log).toEqual('LOG; LOG');
});
});

it('should allow modifying the DOM structure in post link', function() {
module(function() {
directive('removeNode', valueFn({
link: function($scope, $element, $attrs) {
$element.remove();
}
}));
});
inject(function($compile, $rootScope) {
element = jqLite('<div><div remove-node></div><div>{{test}}</div></div>');
$rootScope.test = 'Hello';
$compile(element)($rootScope);
$rootScope.$digest();
expect(element.children().length).toBe(1);
expect(element.text()).toBe('Hello');
});
})
});

describe('compiler control', function() {
Expand Down