mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-03-18 23:50:23 +00:00
fix($compile): accessing controllers of transcluded directives from children
Additional API (backwards compatible) - Injects `$transclude` (see directive controllers) as 5th argument to directive link functions. - `$transclude` takes an optional scope as first parameter that overrides the bound scope. Deprecations: - `transclude` parameter of directive compile functions (use the new parameter for link functions instead). Refactorings: - Don't use comment node to temporarily store controllers - `ngIf`, `ngRepeat`, ... now all use `$transclude` Closes #4935.
This commit is contained in:
parent
c785918cbd
commit
90f87072e8
11 changed files with 434 additions and 73 deletions
|
|
@ -178,8 +178,9 @@
|
|||
* * `$scope` - Current scope associated with the element
|
||||
* * `$element` - Current element
|
||||
* * `$attrs` - Current attributes object for the element
|
||||
* * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope:
|
||||
* `function(cloneLinkingFn)`.
|
||||
* * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope.
|
||||
* The scope can be overridden by an optional first argument.
|
||||
* `function([scope], cloneLinkingFn)`.
|
||||
*
|
||||
*
|
||||
* #### `require`
|
||||
|
|
@ -272,7 +273,7 @@
|
|||
* * `tAttrs` - template attributes - Normalized list of attributes declared on this element shared
|
||||
* between all directive compile functions.
|
||||
*
|
||||
* * `transclude` - A transclude linking function: `function(scope, cloneLinkingFn)`.
|
||||
* * `transclude` - [*DEPRECATED*!] A transclude linking function: `function(scope, cloneLinkingFn)`
|
||||
*
|
||||
* <div class="alert alert-warning">
|
||||
* **Note:** The template instance and the link instance may be different objects if the template has
|
||||
|
|
@ -281,6 +282,12 @@
|
|||
* should be done in a linking function rather than in a compile function.
|
||||
* </div>
|
||||
*
|
||||
* <div class="alert alert-error">
|
||||
* **Note:** The `transclude` function that is passed to the compile function is deperecated, as it
|
||||
* e.g. does not know about the right outer scope. Please use the transclude function that is passed
|
||||
* to the link function instead.
|
||||
* </div>
|
||||
|
||||
* A compile function can have a return value which can be either a function or an object.
|
||||
*
|
||||
* * returning a (post-link) function - is equivalent to registering the linking function via the
|
||||
|
|
@ -295,7 +302,7 @@
|
|||
* This property is used only if the `compile` property is not defined.
|
||||
*
|
||||
* <pre>
|
||||
* function link(scope, iElement, iAttrs, controller) { ... }
|
||||
* function link(scope, iElement, iAttrs, controller, transcludeFn) { ... }
|
||||
* </pre>
|
||||
*
|
||||
* The link function is responsible for registering DOM listeners as well as updating the DOM. It is
|
||||
|
|
@ -316,6 +323,10 @@
|
|||
* element defines a controller. The controller is shared among all the directives, which allows
|
||||
* the directives to use the controllers as a communication channel.
|
||||
*
|
||||
* * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope.
|
||||
* The scope can be overridden by an optional first argument. This is the same as the `$transclude`
|
||||
* parameter of directive controllers.
|
||||
* `function([scope], cloneLinkingFn)`.
|
||||
*
|
||||
*
|
||||
* #### Pre-linking function
|
||||
|
|
@ -821,7 +832,7 @@ function $CompileProvider($provide) {
|
|||
var compositeLinkFn =
|
||||
compileNodes($compileNodes, transcludeFn, $compileNodes,
|
||||
maxPriority, ignoreDirective, previousCompileContext);
|
||||
return function publicLinkFn(scope, cloneConnectFn){
|
||||
return function publicLinkFn(scope, cloneConnectFn, transcludeControllers){
|
||||
assertArg(scope, 'scope');
|
||||
// important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
|
||||
// and sometimes changes the structure of the DOM.
|
||||
|
|
@ -829,6 +840,10 @@ function $CompileProvider($provide) {
|
|||
? JQLitePrototype.clone.call($compileNodes) // IMPORTANT!!!
|
||||
: $compileNodes;
|
||||
|
||||
forEach(transcludeControllers, function(instance, name) {
|
||||
$linkNode.data('$' + name + 'Controller', instance);
|
||||
});
|
||||
|
||||
// Attach scope only to non-text nodes.
|
||||
for(var i = 0, ii = $linkNode.length; i<ii; i++) {
|
||||
var node = $linkNode[i];
|
||||
|
|
@ -940,12 +955,19 @@ function $CompileProvider($provide) {
|
|||
}
|
||||
|
||||
function createBoundTranscludeFn(scope, transcludeFn) {
|
||||
return function boundTranscludeFn(cloneFn) {
|
||||
var transcludedScope = scope.$new(),
|
||||
clone;
|
||||
transcludedScope.$$transcluded = true;
|
||||
clone = transcludeFn(transcludedScope, cloneFn);
|
||||
clone.on('$destroy', bind(transcludedScope, transcludedScope.$destroy));
|
||||
return function boundTranscludeFn(transcludedScope, cloneFn, controllers) {
|
||||
var scopeCreated = false;
|
||||
|
||||
if (!transcludedScope) {
|
||||
transcludedScope = scope.$new();
|
||||
transcludedScope.$$transcluded = true;
|
||||
scopeCreated = true;
|
||||
}
|
||||
|
||||
var clone = transcludeFn(transcludedScope, cloneFn, controllers);
|
||||
if (scopeCreated) {
|
||||
clone.on('$destroy', bind(transcludedScope, transcludedScope.$destroy));
|
||||
}
|
||||
return clone;
|
||||
};
|
||||
}
|
||||
|
|
@ -1086,9 +1108,9 @@ function $CompileProvider($provide) {
|
|||
* @returns {Function}
|
||||
*/
|
||||
function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) {
|
||||
return function(scope, element, attrs, controllers) {
|
||||
return function(scope, element, attrs, controllers, transcludeFn) {
|
||||
element = groupScan(element[0], attrStart, attrEnd);
|
||||
return linkFn(scope, element, attrs, controllers);
|
||||
return linkFn(scope, element, attrs, controllers, transcludeFn);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -1125,7 +1147,9 @@ function $CompileProvider($provide) {
|
|||
controllerDirectives = previousCompileContext.controllerDirectives,
|
||||
newIsolateScopeDirective = previousCompileContext.newIsolateScopeDirective,
|
||||
templateDirective = previousCompileContext.templateDirective,
|
||||
transcludeDirective = previousCompileContext.transcludeDirective,
|
||||
nonTlbTranscludeDirective = previousCompileContext.nonTlbTranscludeDirective,
|
||||
hasTranscludeDirective = false,
|
||||
hasElementTranscludeDirective = false,
|
||||
$compileNode = templateAttrs.$$element = jqLite(compileNode),
|
||||
directive,
|
||||
directiveName,
|
||||
|
|
@ -1176,15 +1200,18 @@ function $CompileProvider($provide) {
|
|||
}
|
||||
|
||||
if (directiveValue = directive.transclude) {
|
||||
hasTranscludeDirective = true;
|
||||
|
||||
// Special case ngIf and ngRepeat so that we don't complain about duplicate transclusion.
|
||||
// This option should only be used by directives that know how to how to safely handle element transclusion,
|
||||
// where the transcluded nodes are added or replaced after linking.
|
||||
if (!directive.$$tlb) {
|
||||
assertNoDuplicate('transclusion', transcludeDirective, directive, $compileNode);
|
||||
transcludeDirective = directive;
|
||||
assertNoDuplicate('transclusion', nonTlbTranscludeDirective, directive, $compileNode);
|
||||
nonTlbTranscludeDirective = directive;
|
||||
}
|
||||
|
||||
if (directiveValue == 'element') {
|
||||
hasElementTranscludeDirective = true;
|
||||
terminalPriority = directive.priority;
|
||||
$template = groupScan(compileNode, attrStart, attrEnd);
|
||||
$compileNode = templateAttrs.$$element =
|
||||
|
|
@ -1200,9 +1227,9 @@ function $CompileProvider($provide) {
|
|||
// - newIsolateScopeDirective or templateDirective - combining templates with
|
||||
// element transclusion doesn't make sense.
|
||||
//
|
||||
// We need only transcludeDirective so that we prevent putting transclusion
|
||||
// We need only nonTlbTranscludeDirective so that we prevent putting transclusion
|
||||
// on the same element more than once.
|
||||
transcludeDirective: transcludeDirective
|
||||
nonTlbTranscludeDirective: nonTlbTranscludeDirective
|
||||
});
|
||||
} else {
|
||||
$template = jqLite(jqLiteClone(compileNode)).contents();
|
||||
|
|
@ -1271,7 +1298,7 @@ function $CompileProvider($provide) {
|
|||
controllerDirectives: controllerDirectives,
|
||||
newIsolateScopeDirective: newIsolateScopeDirective,
|
||||
templateDirective: templateDirective,
|
||||
transcludeDirective: transcludeDirective
|
||||
nonTlbTranscludeDirective: nonTlbTranscludeDirective
|
||||
});
|
||||
ii = directives.length;
|
||||
} else if (directive.compile) {
|
||||
|
|
@ -1295,7 +1322,7 @@ function $CompileProvider($provide) {
|
|||
}
|
||||
|
||||
nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true;
|
||||
nodeLinkFn.transclude = transcludeDirective && childTranscludeFn;
|
||||
nodeLinkFn.transclude = hasTranscludeDirective && childTranscludeFn;
|
||||
|
||||
// might be normal or delayed nodeLinkFn depending on if templateUrl is present
|
||||
return nodeLinkFn;
|
||||
|
|
@ -1322,7 +1349,7 @@ function $CompileProvider($provide) {
|
|||
}
|
||||
|
||||
|
||||
function getControllers(require, $element) {
|
||||
function getControllers(require, $element, elementControllers) {
|
||||
var value, retrievalMethod = 'data', optional = false;
|
||||
if (isString(require)) {
|
||||
while((value = require.charAt(0)) == '^' || value == '?') {
|
||||
|
|
@ -1332,13 +1359,12 @@ function $CompileProvider($provide) {
|
|||
}
|
||||
optional = optional || value == '?';
|
||||
}
|
||||
value = null;
|
||||
|
||||
value = $element[retrievalMethod]('$' + require + 'Controller');
|
||||
|
||||
if ($element[0].nodeType == 8 && $element[0].$$controller) { // Transclusion comment node
|
||||
value = value || $element[0].$$controller;
|
||||
$element[0].$$controller = null;
|
||||
if (elementControllers && retrievalMethod === 'data') {
|
||||
value = elementControllers[require];
|
||||
}
|
||||
value = value || $element[retrievalMethod]('$' + require + 'Controller');
|
||||
|
||||
if (!value && !optional) {
|
||||
throw $compileMinErr('ctreq',
|
||||
|
|
@ -1349,7 +1375,7 @@ function $CompileProvider($provide) {
|
|||
} else if (isArray(require)) {
|
||||
value = [];
|
||||
forEach(require, function(require) {
|
||||
value.push(getControllers(require, $element));
|
||||
value.push(getControllers(require, $element, elementControllers));
|
||||
});
|
||||
}
|
||||
return value;
|
||||
|
|
@ -1357,7 +1383,7 @@ function $CompileProvider($provide) {
|
|||
|
||||
|
||||
function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) {
|
||||
var attrs, $element, i, ii, linkFn, controller, isolateScope;
|
||||
var attrs, $element, i, ii, linkFn, controller, isolateScope, elementControllers = {}, transcludeFn;
|
||||
|
||||
if (compileNode === linkNode) {
|
||||
attrs = templateAttrs;
|
||||
|
|
@ -1451,14 +1477,14 @@ function $CompileProvider($provide) {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
transcludeFn = boundTranscludeFn && controllersBoundTransclude;
|
||||
if (controllerDirectives) {
|
||||
forEach(controllerDirectives, function(directive) {
|
||||
var locals = {
|
||||
$scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope,
|
||||
$element: $element,
|
||||
$attrs: attrs,
|
||||
$transclude: boundTranscludeFn
|
||||
$transclude: transcludeFn
|
||||
}, controllerInstance;
|
||||
|
||||
controller = directive.controller;
|
||||
|
|
@ -1467,16 +1493,16 @@ function $CompileProvider($provide) {
|
|||
}
|
||||
|
||||
controllerInstance = $controller(controller, locals);
|
||||
|
||||
// Directives with element transclusion and a controller need to attach controller
|
||||
// to the comment node created by the compiler, but jQuery .data doesn't support
|
||||
// attaching data to comment nodes so instead we set it directly on the element and
|
||||
// remove it after we read it later.
|
||||
if ($element[0].nodeType == 8) { // Transclusion comment node
|
||||
$element[0].$$controller = controllerInstance;
|
||||
} else {
|
||||
// For directives with element transclusion the element is a comment,
|
||||
// but jQuery .data doesn't support attaching data to comment nodes as it's hard to
|
||||
// clean up (http://bugs.jquery.com/ticket/8335).
|
||||
// Instead, we save the controllers for the element in a local hash and attach to .data
|
||||
// later, once we have the actual element.
|
||||
elementControllers[directive.name] = controllerInstance;
|
||||
if (!hasElementTranscludeDirective) {
|
||||
$element.data('$' + directive.name + 'Controller', controllerInstance);
|
||||
}
|
||||
|
||||
if (directive.controllerAs) {
|
||||
locals.$scope[directive.controllerAs] = controllerInstance;
|
||||
}
|
||||
|
|
@ -1488,7 +1514,7 @@ function $CompileProvider($provide) {
|
|||
try {
|
||||
linkFn = preLinkFns[i];
|
||||
linkFn(linkFn.isolateScope ? isolateScope : scope, $element, attrs,
|
||||
linkFn.require && getControllers(linkFn.require, $element));
|
||||
linkFn.require && getControllers(linkFn.require, $element, elementControllers), transcludeFn);
|
||||
} catch (e) {
|
||||
$exceptionHandler(e, startingTag($element));
|
||||
}
|
||||
|
|
@ -1508,11 +1534,28 @@ function $CompileProvider($provide) {
|
|||
try {
|
||||
linkFn = postLinkFns[i];
|
||||
linkFn(linkFn.isolateScope ? isolateScope : scope, $element, attrs,
|
||||
linkFn.require && getControllers(linkFn.require, $element));
|
||||
linkFn.require && getControllers(linkFn.require, $element, elementControllers), transcludeFn);
|
||||
} catch (e) {
|
||||
$exceptionHandler(e, startingTag($element));
|
||||
}
|
||||
}
|
||||
|
||||
// This is the function that is injected as `$transclude`.
|
||||
function controllersBoundTransclude(scope, cloneAttachFn) {
|
||||
var transcludeControllers;
|
||||
|
||||
// no scope passed
|
||||
if (arguments.length < 2) {
|
||||
cloneAttachFn = scope;
|
||||
scope = undefined;
|
||||
}
|
||||
|
||||
if (hasElementTranscludeDirective) {
|
||||
transcludeControllers = elementControllers;
|
||||
}
|
||||
|
||||
return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1622,7 +1665,7 @@ function $CompileProvider($provide) {
|
|||
|
||||
$http.get($sce.getTrustedResourceUrl(templateUrl), {cache: $templateCache}).
|
||||
success(function(content) {
|
||||
var compileNode, tempTemplateAttrs, $template;
|
||||
var compileNode, tempTemplateAttrs, $template, childBoundTranscludeFn;
|
||||
|
||||
content = denormalizeTemplate(content);
|
||||
|
||||
|
|
@ -1667,7 +1710,7 @@ function $CompileProvider($provide) {
|
|||
var scope = linkQueue.shift(),
|
||||
beforeTemplateLinkNode = linkQueue.shift(),
|
||||
linkRootElement = linkQueue.shift(),
|
||||
controller = linkQueue.shift(),
|
||||
boundTranscludeFn = linkQueue.shift(),
|
||||
linkNode = $compileNode[0];
|
||||
|
||||
if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
|
||||
|
|
@ -1675,9 +1718,13 @@ function $CompileProvider($provide) {
|
|||
linkNode = jqLiteClone(compileNode);
|
||||
replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode);
|
||||
}
|
||||
|
||||
if (afterTemplateNodeLinkFn.transclude) {
|
||||
childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude);
|
||||
} else {
|
||||
childBoundTranscludeFn = boundTranscludeFn;
|
||||
}
|
||||
afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement,
|
||||
controller);
|
||||
childBoundTranscludeFn);
|
||||
}
|
||||
linkQueue = null;
|
||||
}).
|
||||
|
|
@ -1685,14 +1732,14 @@ function $CompileProvider($provide) {
|
|||
throw $compileMinErr('tpload', 'Failed to load template: {0}', config.url);
|
||||
});
|
||||
|
||||
return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, controller) {
|
||||
return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) {
|
||||
if (linkQueue) {
|
||||
linkQueue.push(scope);
|
||||
linkQueue.push(node);
|
||||
linkQueue.push(rootElement);
|
||||
linkQueue.push(controller);
|
||||
linkQueue.push(boundTranscludeFn);
|
||||
} else {
|
||||
afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, controller);
|
||||
afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, boundTranscludeFn);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -86,15 +86,14 @@ var ngIfDirective = ['$animate', function($animate) {
|
|||
terminal: true,
|
||||
restrict: 'A',
|
||||
$$tlb: true,
|
||||
compile: function (element, attr, transclude) {
|
||||
return function ($scope, $element, $attr) {
|
||||
link: function ($scope, $element, $attr, ctrl, $transclude) {
|
||||
var block, childScope;
|
||||
$scope.$watch($attr.ngIf, function ngIfWatchAction(value) {
|
||||
|
||||
if (toBoolean(value)) {
|
||||
if (!childScope) {
|
||||
childScope = $scope.$new();
|
||||
transclude(childScope, function (clone) {
|
||||
$transclude(childScope, function (clone) {
|
||||
block = {
|
||||
startNode: clone[0],
|
||||
endNode: clone[clone.length++] = document.createComment(' end ngIf: ' + $attr.ngIf + ' ')
|
||||
|
|
@ -115,7 +114,6 @@ var ngIfDirective = ['$animate', function($animate) {
|
|||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
};
|
||||
}];
|
||||
|
|
|
|||
|
|
@ -154,12 +154,12 @@ var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile'
|
|||
priority: 400,
|
||||
terminal: true,
|
||||
transclude: 'element',
|
||||
compile: function(element, attr, transclusion) {
|
||||
compile: function(element, attr) {
|
||||
var srcExp = attr.ngInclude || attr.src,
|
||||
onloadExp = attr.onload || '',
|
||||
autoScrollExp = attr.autoscroll;
|
||||
|
||||
return function(scope, $element) {
|
||||
return function(scope, $element, $attr, ctrl, $transclude) {
|
||||
var changeCounter = 0,
|
||||
currentScope,
|
||||
currentElement;
|
||||
|
|
@ -188,7 +188,7 @@ var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile'
|
|||
if (thisChangeId !== changeCounter) return;
|
||||
var newScope = scope.$new();
|
||||
|
||||
transclusion(newScope, function(clone) {
|
||||
$transclude(newScope, function(clone) {
|
||||
cleanupLastIncludeContent();
|
||||
|
||||
currentScope = newScope;
|
||||
|
|
|
|||
|
|
@ -201,8 +201,7 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
|
|||
priority: 1000,
|
||||
terminal: true,
|
||||
$$tlb: true,
|
||||
compile: function(element, attr, linker) {
|
||||
return function($scope, $element, $attr){
|
||||
link: function($scope, $element, $attr, ctrl, $transclude){
|
||||
var expression = $attr.ngRepeat;
|
||||
var match = expression.match(/^\s*(.+)\s+in\s+(.*?)\s*(\s+track\s+by\s+(.+)\s*)?$/),
|
||||
trackByExp, trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn,
|
||||
|
|
@ -364,7 +363,7 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
|
|||
// jshint bitwise: true
|
||||
|
||||
if (!block.startNode) {
|
||||
linker(childScope, function(clone) {
|
||||
$transclude(childScope, function(clone) {
|
||||
clone[clone.length++] = document.createComment(' end ngRepeat: ' + expression + ' ');
|
||||
$animate.enter(clone, null, jqLite(previousNode));
|
||||
previousNode = clone;
|
||||
|
|
@ -377,7 +376,6 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
|
|||
}
|
||||
lastBlockMap = nextBlockMap;
|
||||
});
|
||||
};
|
||||
}
|
||||
};
|
||||
}];
|
||||
|
|
|
|||
|
|
@ -160,10 +160,10 @@ var ngSwitchWhenDirective = ngDirective({
|
|||
transclude: 'element',
|
||||
priority: 800,
|
||||
require: '^ngSwitch',
|
||||
compile: function(element, attrs, transclude) {
|
||||
return function(scope, element, attr, ctrl) {
|
||||
compile: function(element, attrs) {
|
||||
return function(scope, element, attr, ctrl, $transclude) {
|
||||
ctrl.cases['!' + attrs.ngSwitchWhen] = (ctrl.cases['!' + attrs.ngSwitchWhen] || []);
|
||||
ctrl.cases['!' + attrs.ngSwitchWhen].push({ transclude: transclude, element: element });
|
||||
ctrl.cases['!' + attrs.ngSwitchWhen].push({ transclude: $transclude, element: element });
|
||||
};
|
||||
}
|
||||
});
|
||||
|
|
@ -172,10 +172,8 @@ var ngSwitchDefaultDirective = ngDirective({
|
|||
transclude: 'element',
|
||||
priority: 800,
|
||||
require: '^ngSwitch',
|
||||
compile: function(element, attrs, transclude) {
|
||||
return function(scope, element, attr, ctrl) {
|
||||
ctrl.cases['?'] = (ctrl.cases['?'] || []);
|
||||
ctrl.cases['?'].push({ transclude: transclude, element: element });
|
||||
};
|
||||
}
|
||||
link: function(scope, element, attr, ctrl, $transclude) {
|
||||
ctrl.cases['?'] = (ctrl.cases['?'] || []);
|
||||
ctrl.cases['?'].push({ transclude: $transclude, element: element });
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -173,8 +173,7 @@ function ngViewFactory( $route, $anchorScroll, $compile, $controller,
|
|||
terminal: true,
|
||||
priority: 400,
|
||||
transclude: 'element',
|
||||
compile: function(element, attr, linker) {
|
||||
return function(scope, $element, attr) {
|
||||
link: function(scope, $element, attr, ctrl, $transclude) {
|
||||
var currentScope,
|
||||
currentElement,
|
||||
autoScrollExp = attr.autoscroll,
|
||||
|
|
@ -200,7 +199,7 @@ function ngViewFactory( $route, $anchorScroll, $compile, $controller,
|
|||
|
||||
if (template) {
|
||||
var newScope = scope.$new();
|
||||
linker(newScope, function(clone) {
|
||||
$transclude(newScope, function(clone) {
|
||||
clone.html(template);
|
||||
$animate.enter(clone, null, currentElement || $element, function onNgViewEnter () {
|
||||
if (angular.isDefined(autoScrollExp)
|
||||
|
|
@ -235,7 +234,6 @@ function ngViewFactory( $route, $anchorScroll, $compile, $controller,
|
|||
cleanupLastView();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3438,6 +3438,113 @@ describe('$compile', function() {
|
|||
expect(log).toEqual('pre(); post(unicorn!)');
|
||||
});
|
||||
});
|
||||
|
||||
it('should copy the directive controller to all clones', function() {
|
||||
var transcludeCtrl, cloneCount = 2;
|
||||
module(function() {
|
||||
directive('transclude', valueFn({
|
||||
transclude: 'content',
|
||||
controller: function($transclude) {
|
||||
transcludeCtrl = this;
|
||||
},
|
||||
link: function(scope, el, attr, ctrl, $transclude) {
|
||||
var i;
|
||||
for (i=0; i<cloneCount; i++) {
|
||||
$transclude(cloneAttach);
|
||||
}
|
||||
|
||||
function cloneAttach(clone) {
|
||||
el.append(clone);
|
||||
}
|
||||
}
|
||||
}));
|
||||
});
|
||||
inject(function($compile) {
|
||||
element = $compile('<div transclude><span></span></div>')($rootScope);
|
||||
var children = element.children(), i;
|
||||
expect(transcludeCtrl).toBeDefined();
|
||||
|
||||
expect(element.data('$transcludeController')).toBe(transcludeCtrl);
|
||||
for (i=0; i<cloneCount; i++) {
|
||||
expect(children.eq(i).data('$transcludeController')).toBeUndefined();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should provide the $transclude controller local as 5th argument to the pre and post-link function', function() {
|
||||
var ctrlTransclude, preLinkTransclude, postLinkTransclude;
|
||||
module(function() {
|
||||
directive('transclude', valueFn({
|
||||
transclude: 'content',
|
||||
controller: function($transclude) {
|
||||
ctrlTransclude = $transclude;
|
||||
},
|
||||
compile: function() {
|
||||
return {
|
||||
pre: function(scope, el, attr, ctrl, $transclude) {
|
||||
preLinkTransclude = $transclude;
|
||||
},
|
||||
post: function(scope, el, attr, ctrl, $transclude) {
|
||||
postLinkTransclude = $transclude;
|
||||
}
|
||||
};
|
||||
}
|
||||
}));
|
||||
});
|
||||
inject(function($compile) {
|
||||
element = $compile('<div transclude></div>')($rootScope);
|
||||
expect(ctrlTransclude).toBeDefined();
|
||||
expect(ctrlTransclude).toBe(preLinkTransclude);
|
||||
expect(ctrlTransclude).toBe(postLinkTransclude);
|
||||
});
|
||||
});
|
||||
|
||||
it('should allow an optional scope argument in $transclude', function() {
|
||||
var capturedChildCtrl;
|
||||
module(function() {
|
||||
directive('transclude', valueFn({
|
||||
transclude: 'content',
|
||||
link: function(scope, element, attr, ctrl, $transclude) {
|
||||
$transclude(scope, function(clone) {
|
||||
element.append(clone);
|
||||
});
|
||||
}
|
||||
}));
|
||||
});
|
||||
inject(function($compile) {
|
||||
element = $compile('<div transclude>{{$id}}</div>')($rootScope);
|
||||
$rootScope.$apply();
|
||||
expect(element.text()).toBe($rootScope.$id);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('should expose the directive controller to transcluded children', function() {
|
||||
var capturedChildCtrl;
|
||||
module(function() {
|
||||
directive('transclude', valueFn({
|
||||
transclude: 'content',
|
||||
controller: function() {
|
||||
},
|
||||
link: function(scope, element, attr, ctrl, $transclude) {
|
||||
$transclude(function(clone) {
|
||||
element.append(clone);
|
||||
});
|
||||
}
|
||||
}));
|
||||
directive('child', valueFn({
|
||||
require: '^transclude',
|
||||
link: function(scope, element, attr, ctrl) {
|
||||
capturedChildCtrl = ctrl;
|
||||
}
|
||||
}));
|
||||
});
|
||||
inject(function($compile) {
|
||||
element = $compile('<div transclude><div child></div></div>')($rootScope);
|
||||
expect(capturedChildCtrl).toBeTruthy();
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
|
@ -3471,7 +3578,6 @@ describe('$compile', function() {
|
|||
});
|
||||
});
|
||||
|
||||
|
||||
it('should only allow one element transclusion per element', function() {
|
||||
module(function() {
|
||||
directive('first', valueFn({
|
||||
|
|
@ -3620,8 +3726,101 @@ describe('$compile', function() {
|
|||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should allow to access $transclude in the same directive', function() {
|
||||
var _$transclude;
|
||||
module(function() {
|
||||
directive('transclude', valueFn({
|
||||
transclude: 'element',
|
||||
controller: function($transclude) {
|
||||
_$transclude = $transclude;
|
||||
}
|
||||
}));
|
||||
});
|
||||
inject(function($compile) {
|
||||
element = $compile('<div transclude></div>')($rootScope);
|
||||
expect(_$transclude).toBeDefined()
|
||||
});
|
||||
});
|
||||
|
||||
it('should copy the directive controller to all clones', function() {
|
||||
var transcludeCtrl, cloneCount = 2;
|
||||
module(function() {
|
||||
directive('transclude', valueFn({
|
||||
transclude: 'element',
|
||||
controller: function() {
|
||||
transcludeCtrl = this;
|
||||
},
|
||||
link: function(scope, el, attr, ctrl, $transclude) {
|
||||
var i;
|
||||
for (i=0; i<cloneCount; i++) {
|
||||
$transclude(cloneAttach);
|
||||
}
|
||||
|
||||
function cloneAttach(clone) {
|
||||
el.after(clone);
|
||||
}
|
||||
}
|
||||
}));
|
||||
});
|
||||
inject(function($compile) {
|
||||
element = $compile('<div><div transclude></div></div>')($rootScope);
|
||||
var children = element.children(), i;
|
||||
for (i=0; i<cloneCount; i++) {
|
||||
expect(children.eq(i).data('$transcludeController')).toBe(transcludeCtrl);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should expose the directive controller to transcluded children', function() {
|
||||
var capturedTranscludeCtrl;
|
||||
module(function() {
|
||||
directive('transclude', valueFn({
|
||||
transclude: 'element',
|
||||
controller: function() {
|
||||
},
|
||||
link: function(scope, element, attr, ctrl, $transclude) {
|
||||
$transclude(scope, function(clone) {
|
||||
element.after(clone);
|
||||
});
|
||||
}
|
||||
}));
|
||||
directive('child', valueFn({
|
||||
require: '^transclude',
|
||||
link: function(scope, element, attr, ctrl) {
|
||||
capturedTranscludeCtrl = ctrl;
|
||||
}
|
||||
}));
|
||||
});
|
||||
inject(function($compile) {
|
||||
element = $compile('<div transclude><div child></div></div>')($rootScope);
|
||||
expect(capturedTranscludeCtrl).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
it('should allow access to $transclude in a templateUrl directive', function() {
|
||||
var transclude;
|
||||
module(function() {
|
||||
directive('template', valueFn({
|
||||
templateUrl: 'template.html',
|
||||
replace: true
|
||||
}));
|
||||
directive('transclude', valueFn({
|
||||
transclude: 'content',
|
||||
controller: function($transclude) {
|
||||
transclude = $transclude;
|
||||
}
|
||||
}));
|
||||
});
|
||||
inject(function($compile, $httpBackend) {
|
||||
$httpBackend.expectGET('template.html').respond('<div transclude></div>');
|
||||
element = $compile('<div template></div>')($rootScope);
|
||||
$httpBackend.flush();
|
||||
expect(transclude).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('should safely create transclude comment node and not break with "-->"',
|
||||
inject(function($rootScope) {
|
||||
|
|
|
|||
|
|
@ -148,6 +148,34 @@ describe('ngIf', function () {
|
|||
|
||||
});
|
||||
|
||||
describe('ngIf and transcludes', function() {
|
||||
it('should allow access to directive controller from children when used in a replace template', function() {
|
||||
var controller;
|
||||
module(function($compileProvider) {
|
||||
var directive = $compileProvider.directive;
|
||||
directive('template', valueFn({
|
||||
template: '<div ng-if="true"><span test></span></div>',
|
||||
replace: true,
|
||||
controller: function() {
|
||||
this.flag = true;
|
||||
}
|
||||
}));
|
||||
directive('test', valueFn({
|
||||
require: '^template',
|
||||
link: function(scope, el, attr, ctrl) {
|
||||
controller = ctrl;
|
||||
}
|
||||
}));
|
||||
});
|
||||
inject(function($compile, $rootScope) {
|
||||
var element = $compile('<div><div template></div></div>')($rootScope);
|
||||
$rootScope.$apply();
|
||||
expect(controller.flag).toBe(true);
|
||||
dealoc(element);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('ngIf animations', function () {
|
||||
var body, element, $rootElement;
|
||||
|
||||
|
|
|
|||
|
|
@ -439,6 +439,36 @@ describe('ngInclude', function() {
|
|||
});
|
||||
});
|
||||
|
||||
describe('ngInclude and transcludes', function() {
|
||||
it('should allow access to directive controller from children when used in a replace template', function() {
|
||||
var controller;
|
||||
module(function($compileProvider) {
|
||||
var directive = $compileProvider.directive;
|
||||
directive('template', valueFn({
|
||||
template: '<div ng-include="\'include.html\'"></div>',
|
||||
replace: true,
|
||||
controller: function() {
|
||||
this.flag = true;
|
||||
}
|
||||
}));
|
||||
directive('test', valueFn({
|
||||
require: '^template',
|
||||
link: function(scope, el, attr, ctrl) {
|
||||
controller = ctrl;
|
||||
}
|
||||
}));
|
||||
});
|
||||
inject(function($compile, $rootScope, $httpBackend) {
|
||||
$httpBackend.expectGET('include.html').respond('<div><div test></div></div>');
|
||||
var element = $compile('<div><div template></div></div>')($rootScope);
|
||||
$rootScope.$apply();
|
||||
$httpBackend.flush();
|
||||
expect(controller.flag).toBe(true);
|
||||
dealoc(element);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('ngInclude animations', function() {
|
||||
var body, element, $rootElement;
|
||||
|
||||
|
|
|
|||
|
|
@ -1058,6 +1058,33 @@ describe('ngRepeat', function() {
|
|||
});
|
||||
});
|
||||
|
||||
describe('ngRepeat and transcludes', function() {
|
||||
it('should allow access to directive controller from children when used in a replace template', function() {
|
||||
var controller;
|
||||
module(function($compileProvider) {
|
||||
var directive = $compileProvider.directive;
|
||||
directive('template', valueFn({
|
||||
template: '<div ng-repeat="l in [1]"><span test></span></div>',
|
||||
replace: true,
|
||||
controller: function() {
|
||||
this.flag = true;
|
||||
}
|
||||
}));
|
||||
directive('test', valueFn({
|
||||
require: '^template',
|
||||
link: function(scope, el, attr, ctrl) {
|
||||
controller = ctrl;
|
||||
}
|
||||
}));
|
||||
});
|
||||
inject(function($compile, $rootScope) {
|
||||
var element = $compile('<div><div template></div></div>')($rootScope);
|
||||
$rootScope.$apply();
|
||||
expect(controller.flag).toBe(true);
|
||||
dealoc(element);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('ngRepeat animations', function() {
|
||||
var body, element, $rootElement;
|
||||
|
|
|
|||
|
|
@ -514,6 +514,44 @@ describe('ngView', function() {
|
|||
});
|
||||
});
|
||||
|
||||
describe('ngView and transcludes', function() {
|
||||
it('should allow access to directive controller from children when used in a replace template', function() {
|
||||
var controller;
|
||||
module('ngRoute');
|
||||
module(function($compileProvider, $routeProvider) {
|
||||
$routeProvider.when('/view', {templateUrl: 'view.html'});
|
||||
var directive = $compileProvider.directive;
|
||||
directive('template', function() {
|
||||
return {
|
||||
template: '<div ng-view></div>',
|
||||
replace: true,
|
||||
controller: function() {
|
||||
this.flag = true;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
directive('test', function() {
|
||||
return {
|
||||
require: '^template',
|
||||
link: function(scope, el, attr, ctrl) {
|
||||
controller = ctrl;
|
||||
}
|
||||
};
|
||||
});
|
||||
});
|
||||
inject(function($compile, $rootScope, $httpBackend, $location) {
|
||||
$httpBackend.expectGET('view.html').respond('<div><div test></div></div>');
|
||||
var element = $compile('<div><div template></div></div>')($rootScope);
|
||||
$location.url('/view');
|
||||
$rootScope.$apply();
|
||||
$httpBackend.flush();
|
||||
expect(controller.flag).toBe(true);
|
||||
dealoc(element);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('ngView animations', function() {
|
||||
var body, element, $rootElement;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue