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

Nested iso transcludes #7499

Closed
45 changes: 31 additions & 14 deletions src/ng/compile.js
Original file line number Diff line number Diff line change
Expand Up @@ -935,8 +935,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
// 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, $node, childScope, childTranscludeFn, i, ii, n;
function compositeLinkFn(scope, nodeList, $rootElement, parentBoundTranscludeFn) {
var nodeLinkFn, childLinkFn, node, $node, childScope, i, ii, n, childBoundTranscludeFn;

// copy nodeList so that linking doesn't break due to live list updates.
var nodeListLength = nodeList.length,
Expand All @@ -958,23 +958,33 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
} else {
childScope = scope;
}
childTranscludeFn = nodeLinkFn.transclude;
if (childTranscludeFn || (!boundTranscludeFn && transcludeFn)) {
nodeLinkFn(childLinkFn, childScope, node, $rootElement,
createBoundTranscludeFn(scope, childTranscludeFn || transcludeFn)
);

// We need to create a new boundTranscludeFn if
// - a directive on this element wants to transclude
// or
// - there is no parentBoundTranscludeFn already and a transcludeFn was passed in
if ( nodeLinkFn.transcludeOnThisElement || (!parentBoundTranscludeFn && transcludeFn) ) {
childBoundTranscludeFn = createBoundTranscludeFn(scope, nodeLinkFn.transclude || transcludeFn, parentBoundTranscludeFn);
} else {
nodeLinkFn(childLinkFn, childScope, node, $rootElement, boundTranscludeFn);
childBoundTranscludeFn = parentBoundTranscludeFn;
}

nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn);

} else if (childLinkFn) {
childLinkFn(scope, node.childNodes, undefined, boundTranscludeFn);
childLinkFn(scope, node.childNodes, undefined, parentBoundTranscludeFn);
}
}
}
}

function createBoundTranscludeFn(scope, transcludeFn) {
return function boundTranscludeFn(transcludedScope, cloneFn, controllers) {
function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn) {

// If there is a previous boundTransclude function and it has a transclusionScope then
// use this instead of the current scope
scope = previousBoundTranscludeFn && previousBoundTranscludeFn.transclusionScope || scope;

var boundTranscludeFn = function(transcludedScope, cloneFn, controllers) {
var scopeCreated = false;

if (!transcludedScope) {
Expand All @@ -985,10 +995,15 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {

var clone = transcludeFn(transcludedScope, cloneFn, controllers);
if (scopeCreated) {
clone.on('$destroy', bind(transcludedScope, transcludedScope.$destroy));
clone.on('$destroy', function() { transcludedScope.$destroy(); });
}
return clone;
};

// Store the transclusionScope for nested transclusions
boundTranscludeFn.transclusionScope = scope;

return boundTranscludeFn;
}

/**
Expand Down Expand Up @@ -1341,7 +1356,9 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
}

nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true;
nodeLinkFn.transclude = hasTranscludeDirective && childTranscludeFn;
nodeLinkFn.transcludeOnThisElement = hasTranscludeDirective;
nodeLinkFn.transclude = childTranscludeFn;

previousCompileContext.hasElementTranscludeDirective = hasElementTranscludeDirective;

// might be normal or delayed nodeLinkFn depending on if templateUrl is present
Expand Down Expand Up @@ -1760,7 +1777,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
safeAddClass(jqLite(linkNode), oldClasses);
}
if (afterTemplateNodeLinkFn.transclude) {
childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude);
childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
} else {
childBoundTranscludeFn = boundTranscludeFn;
}
Expand Down
4 changes: 2 additions & 2 deletions src/ng/directive/ngIf.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,8 @@ var ngIfDirective = ['$animate', function($animate) {

if (toBoolean(value)) {
if (!childScope) {
childScope = $scope.$new();
$transclude(childScope, function (clone) {
$transclude(function (clone, newScope) {
childScope = newScope;
clone[clone.length++] = document.createComment(' end ngIf: ' + $attr.ngIf + ' ');
// Note: We only need the first/last node of the cloned nodes.
// However, we need to keep the reference to the jqlite wrapper as it might be changed later
Expand Down
23 changes: 11 additions & 12 deletions src/ng/directive/ngInclude.js
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,6 @@ var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$animate'

return function(scope, $element, $attr, ctrl, $transclude) {
var changeCounter = 0,
currentScope,
previousElement,
currentElement;

Expand All @@ -183,10 +182,6 @@ var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$animate'
previousElement.remove();
previousElement = null;
}
if(currentScope) {
currentScope.$destroy();
currentScope = null;
}
if(currentElement) {
$animate.leave(currentElement, function() {
previousElement = null;
Expand All @@ -207,7 +202,6 @@ var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$animate'
if (src) {
$http.get(src, {cache: $templateCache}).success(function(response) {
if (thisChangeId !== changeCounter) return;
var newScope = scope.$new();
ctrl.template = response;

// Note: This will also link all children of ng-include that were contained in the original
Expand All @@ -216,16 +210,21 @@ var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$animate'
// Note: We can't remove them in the cloneAttchFn of $transclude as that
// function is called before linking the content, which would apply child
// directives to non existing elements.
var clone = $transclude(newScope, function(clone) {
var clone = $transclude(function(clone, newScope) {

cleanupLastIncludeContent();

$animate.enter(clone, null, $element, afterAnimation);
});

currentScope = newScope;
currentElement = clone;
currentElement = clone;

scope.$eval(onloadExp);

newScope.$evalAsync(function() {
newScope.$emit('$includeContentLoaded');
});
});

currentScope.$emit('$includeContentLoaded');
scope.$eval(onloadExp);
}).error(function() {
if (thisChangeId === changeCounter) cleanupLastIncludeContent();
});
Expand Down
33 changes: 14 additions & 19 deletions src/ng/directive/ngRepeat.js
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,6 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
// lastBlockMap on the next iteration.
nextBlockMap = {},
arrayLength,
childScope,
key, value, // key/value of iteration
trackById,
trackByIdFn,
Expand All @@ -273,6 +272,17 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
nextBlockOrder = [],
elementsToRemove;

var updateScope = function(scope, index) {
scope[valueIdentifier] = value;
if (keyIdentifier) scope[keyIdentifier] = key;
scope.$index = index;
scope.$first = (index === 0);
scope.$last = (index === (arrayLength - 1));
scope.$middle = !(scope.$first || scope.$last);
// jshint bitwise: false
scope.$odd = !(scope.$even = (index&1) === 0);
// jshint bitwise: true
};

if (isArrayLike(collection)) {
collectionKeys = collection;
Expand Down Expand Up @@ -340,8 +350,6 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
if (block.scope) {
// if we have already seen this object, then we need to reuse the
// associated scope/element
childScope = block.scope;

nextNode = previousNode;
do {
nextNode = nextNode.nextSibling;
Expand All @@ -354,32 +362,19 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
previousNode = getBlockEnd(block);
} else {
// new item which we don't know about
childScope = $scope.$new();
}

childScope[valueIdentifier] = value;
if (keyIdentifier) childScope[keyIdentifier] = key;
childScope.$index = index;
childScope.$first = (index === 0);
childScope.$last = (index === (arrayLength - 1));
childScope.$middle = !(childScope.$first || childScope.$last);
// jshint bitwise: false
childScope.$odd = !(childScope.$even = (index&1) === 0);
// jshint bitwise: true

if (!block.scope) {
$transclude(childScope, function(clone) {
$transclude(function(clone, scope) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly we don't need to pass in a scope here now either. The cloneAttachFn kindly provides us with the new scope so we can update it with $index, etc

block.scope = scope;
clone[clone.length++] = document.createComment(' end ngRepeat: ' + expression + ' ');
$animate.enter(clone, null, jqLite(previousNode));
previousNode = clone;
block.scope = childScope;
// Note: We only need the first/last node of the cloned nodes.
// However, we need to keep the reference to the jqlite wrapper as it might be changed later
// by a directive with templateUrl when it's template arrives.
block.clone = clone;
nextBlockMap[block.id] = block;
});
}
updateScope(block.scope, index);
}
lastBlockMap = nextBlockMap;
});
Expand Down
Loading