diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/ng/compile.js | 141 | ||||
| -rw-r--r-- | src/ng/directive/ngIf.js | 6 | ||||
| -rw-r--r-- | src/ng/directive/ngInclude.js | 6 | ||||
| -rw-r--r-- | src/ng/directive/ngRepeat.js | 6 | ||||
| -rw-r--r-- | src/ng/directive/ngSwitch.js | 16 | ||||
| -rw-r--r-- | src/ngRoute/directive/ngView.js | 6 |
6 files changed, 110 insertions, 71 deletions
diff --git a/src/ng/compile.js b/src/ng/compile.js index 039c211c..4d83f379 100644 --- a/src/ng/compile.js +++ b/src/ng/compile.js @@ -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); } }; } diff --git a/src/ng/directive/ngIf.js b/src/ng/directive/ngIf.js index 6e845e28..dcb3825d 100644 --- a/src/ng/directive/ngIf.js +++ b/src/ng/directive/ngIf.js @@ -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) { } } }); - }; } }; }]; diff --git a/src/ng/directive/ngInclude.js b/src/ng/directive/ngInclude.js index 7ca9a48b..7f395f96 100644 --- a/src/ng/directive/ngInclude.js +++ b/src/ng/directive/ngInclude.js @@ -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; diff --git a/src/ng/directive/ngRepeat.js b/src/ng/directive/ngRepeat.js index c7d0c005..a5614bf4 100644 --- a/src/ng/directive/ngRepeat.js +++ b/src/ng/directive/ngRepeat.js @@ -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; }); - }; } }; }]; diff --git a/src/ng/directive/ngSwitch.js b/src/ng/directive/ngSwitch.js index 11ef7b71..d4387ae2 100644 --- a/src/ng/directive/ngSwitch.js +++ b/src/ng/directive/ngSwitch.js @@ -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 }); + } }); diff --git a/src/ngRoute/directive/ngView.js b/src/ngRoute/directive/ngView.js index a8a136df..3271ac0c 100644 --- a/src/ngRoute/directive/ngView.js +++ b/src/ngRoute/directive/ngView.js @@ -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(); } } - }; } }; } |
