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

fix(): fix scoping for transclusion directives inside replace directive #13244

Merged
merged 4 commits into from
Nov 10, 2015
Merged
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
89 changes: 50 additions & 39 deletions src/ng/compile.js
Original file line number Diff line number Diff line change
Expand Up @@ -1293,6 +1293,14 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
return function publicLinkFn(scope, cloneConnectFn, options) {
assertArg(scope, 'scope');

if (previousCompileContext && previousCompileContext.needsNewScope) {
// A parent directive did a replace and a directive on this element asked
// for transclusion, which caused us to lose a layer of element on which
// we could hold the new transclusion scope, so we will create it manually
// here.
scope = scope.$parent.$new();
}

options = options || {};
var parentBoundTranscludeFn = options.parentBoundTranscludeFn,
transcludeControllers = options.transcludeControllers,
Expand Down Expand Up @@ -1874,7 +1882,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
}

$compileNode.empty(); // clear contents
childTranscludeFn = compilationGenerator(mightHaveMultipleTransclusionError, $template, transcludeFn);
childTranscludeFn = compilationGenerator(mightHaveMultipleTransclusionError, $template, transcludeFn, undefined,
undefined, { needsNewScope: directive.$$isolateScope || directive.$$newScope});
childTranscludeFn.$$slots = slots;
}
}
Expand Down Expand Up @@ -1917,8 +1926,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
var templateDirectives = collectDirectives(compileNode, [], newTemplateAttrs);
var unprocessedDirectives = directives.splice(i + 1, directives.length - (i + 1));

if (newIsolateScopeDirective) {
markDirectivesAsIsolate(templateDirectives);
if (newIsolateScopeDirective || newScopeDirective) {
// The original directive caused the current element to be replaced but this element
// also needs to have a new scope, so we need to tell the template directives
// that they would need to get their scope from further up, if they require transclusion
markDirectiveScope(templateDirectives, newIsolateScopeDirective, newScopeDirective);
}
directives = directives.concat(templateDirectives).concat(unprocessedDirectives);
mergeTemplateAttributes(templateAttrs, newTemplateAttrs);
Expand Down Expand Up @@ -2072,7 +2084,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
}

function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) {
var i, ii, linkFn, controller, isolateScope, elementControllers, transcludeFn, $element,
var linkFn, isolateScope, controllerScope, elementControllers, transcludeFn, $element,
attrs, removeScopeBindingWatches, removeControllerBindingWatches;

if (compileNode === linkNode) {
Expand All @@ -2083,8 +2095,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
attrs = new Attributes($element, templateAttrs);
}

controllerScope = scope;
if (newIsolateScopeDirective) {
isolateScope = scope.$new(true);
} else if (newScopeDirective) {
controllerScope = scope.$parent;
}

if (boundTranscludeFn) {
Expand Down Expand Up @@ -2112,38 +2127,27 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
isolateScope.$on('$destroy', removeScopeBindingWatches);
}
}
if (elementControllers) {
// Initialize bindToController bindings for new/isolate scopes
var scopeDirective = newIsolateScopeDirective || newScopeDirective;
var bindings;
var controllerForBindings;
if (scopeDirective && elementControllers[scopeDirective.name]) {
bindings = scopeDirective.$$bindings.bindToController;
controller = elementControllers[scopeDirective.name];

if (controller && controller.identifier && bindings) {
controllerForBindings = controller;
removeControllerBindingWatches =
initializeDirectiveBindings(scope, attrs, controller.instance,
bindings, scopeDirective);
}

// Initialize bindToController bindings
for (var name in elementControllers) {
var controllerDirective = controllerDirectives[name];
var controller = elementControllers[name];
var bindings = controllerDirective.$$bindings.bindToController;

if (controller.identifier && bindings) {
removeControllerBindingWatches =
initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
}
for (i in elementControllers) {
controller = elementControllers[i];
var controllerResult = controller();

if (controllerResult !== controller.instance) {
// If the controller constructor has a return value, overwrite the instance
// from setupControllers and update the element data
controller.instance = controllerResult;
$element.data('$' + i + 'Controller', controllerResult);
if (controller === controllerForBindings) {
// Remove and re-install bindToController bindings
removeControllerBindingWatches && removeControllerBindingWatches();
removeControllerBindingWatches =
initializeDirectiveBindings(scope, attrs, controllerResult, bindings, scopeDirective);
}
}

var controllerResult = controller();
if (controllerResult !== controller.instance) {
// If the controller constructor has a return value, overwrite the instance
// from setupControllers
controller.instance = controllerResult;
$element.data('$' + controllerDirective.name + 'Controller', controllerResult);
removeControllerBindingWatches && removeControllerBindingWatches();
removeControllerBindingWatches =
initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
}
}

Expand Down Expand Up @@ -2213,10 +2217,15 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
}
}

function markDirectivesAsIsolate(directives) {
// mark all directives as needing isolate scope.
// Depending upon the context in which a directive finds itself it might need to have a new isolated
// or child scope created. For instance:
// * if the directive has been pulled into a template because another directive with a higher priority
// asked for element transclusion
// * if the directive itself asks for transclusion but it is at the root of a template and the original
// element was replaced. See https://github.com/angular/angular.js/issues/12936
function markDirectiveScope(directives, isolateScope, newScope) {
for (var j = 0, jj = directives.length; j < jj; j++) {
directives[j] = inherit(directives[j], {$$isolateScope: true});
directives[j] = inherit(directives[j], {$$isolateScope: isolateScope, $$newScope: newScope});
}
}

Expand Down Expand Up @@ -2363,7 +2372,9 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
var templateDirectives = collectDirectives(compileNode, [], tempTemplateAttrs);

if (isObject(origAsyncDirective.scope)) {
markDirectivesAsIsolate(templateDirectives);
// the original directive that caused the template to be loaded async required
// an isolate scope
markDirectiveScope(templateDirectives, true);
}
directives = templateDirectives.concat(directives);
mergeTemplateAttributes(tAttrs, tempTemplateAttrs);
Expand Down
Loading