From b27c4f5e3b0bf37a85dc49e6df11e5de6bbcb526 Mon Sep 17 00:00:00 2001 From: jankuca Date: Tue, 1 Oct 2013 14:20:04 -0700 Subject: [PATCH] fix($compile): check clashing directives before compilation Closes #3893 --- src/ng/compile.js | 71 +++++++++++++++++++++++++++++------------- test/ng/compileSpec.js | 27 ++++++++++++++-- 2 files changed, 74 insertions(+), 24 deletions(-) diff --git a/src/ng/compile.js b/src/ng/compile.js index 040836f72dad..cc905864f93d 100644 --- a/src/ng/compile.js +++ b/src/ng/compile.js @@ -770,6 +770,55 @@ function $CompileProvider($provide) { linkFn, directiveValue; + for(var i = 0, ii = directives.length; i < ii; i++) { + directive = directives[i]; + if (terminalPriority > directive.priority) { + break; // prevent further processing of directives + } + + directiveName = directive.name; + + if (directiveValue = directive.scope) { + assertNoDuplicate('new/isolated scope', newIsolateScopeDirective, directive, $compileNode); + + // skip the check for directives with async templates, we'll check the derived sync directive when + // the template arrives + if (!directive.templateUrl && isObject(directiveValue)) { + newIsolateScopeDirective = directive; + } + } + + if (!directive.templateUrl && directive.controller) { + directiveValue = directive.controller; + controllerDirectives = controllerDirectives || {}; + assertNoDuplicate("'" + directiveName + "' controller", + controllerDirectives[directiveName], directive, $compileNode); + controllerDirectives[directiveName] = directive; + } + + if (directiveValue = directive.transclude) { + terminalPriority = directive.priority; + assertNoDuplicate('transclusion', transcludeDirective, directive, $compileNode); + transcludeDirective = directive; + } + + if (directive.template) { + assertNoDuplicate('template', templateDirective, directive, $compileNode); + templateDirective = directive; + } + + if (directive.templateUrl) { + assertNoDuplicate('template', templateDirective, directive, $compileNode); + templateDirective = directive; + } + + if (directive.terminal) { + terminalPriority = Math.max(terminalPriority, directive.priority); + } + } + + terminalPriority = -Number.MAX_VALUE; + // executes all directives on the current element for(var i = 0, ii = directives.length; i < ii; i++) { directive = directives[i]; @@ -789,13 +838,9 @@ function $CompileProvider($provide) { if (directiveValue = directive.scope) { newScopeDirective = newScopeDirective || directive; - // skip the check for directives with async templates, we'll check the derived sync directive when - // the template arrives if (!directive.templateUrl) { - assertNoDuplicate('new/isolated scope', newIsolateScopeDirective, directive, $compileNode); if (isObject(directiveValue)) { safeAddClass($compileNode, 'ng-isolate-scope'); - newIsolateScopeDirective = directive; } safeAddClass($compileNode, 'ng-scope'); } @@ -803,17 +848,7 @@ function $CompileProvider($provide) { directiveName = directive.name; - if (!directive.templateUrl && directive.controller) { - directiveValue = directive.controller; - controllerDirectives = controllerDirectives || {}; - assertNoDuplicate("'" + directiveName + "' controller", - controllerDirectives[directiveName], directive, $compileNode); - controllerDirectives[directiveName] = directive; - } - if (directiveValue = directive.transclude) { - assertNoDuplicate('transclusion', transcludeDirective, directive, $compileNode); - transcludeDirective = directive; terminalPriority = directive.priority; if (directiveValue == 'element') { $template = groupScan(compileNode, attrStart, attrEnd) @@ -832,9 +867,6 @@ function $CompileProvider($provide) { } if (directive.template) { - assertNoDuplicate('template', templateDirective, directive, $compileNode); - templateDirective = directive; - directiveValue = (isFunction(directive.template)) ? directive.template($compileNode, templateAttrs) : directive.template; @@ -843,6 +875,7 @@ function $CompileProvider($provide) { if (directive.replace) { replaceDirective = directive; + $template = jqLite('
' + trim(directiveValue) + '
').contents(); @@ -877,9 +910,6 @@ function $CompileProvider($provide) { } if (directive.templateUrl) { - assertNoDuplicate('template', templateDirective, directive, $compileNode); - templateDirective = directive; - if (directive.replace) { replaceDirective = directive; } @@ -904,7 +934,6 @@ function $CompileProvider($provide) { nodeLinkFn.terminal = true; terminalPriority = Math.max(terminalPriority, directive.priority); } - } nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope; diff --git a/test/ng/compileSpec.js b/test/ng/compileSpec.js index 580d51c3b87b..929e0e3e9db9 100755 --- a/test/ng/compileSpec.js +++ b/test/ng/compileSpec.js @@ -1519,7 +1519,7 @@ describe('$compile', function() { expect(function(){ $compile('
'); }).toThrowMinErr('$compile', 'multidir', 'Multiple directives [iscopeA, scopeB] asking for new/isolated scope on: ' + - '
'); + '
'); }) ); @@ -2700,7 +2700,7 @@ describe('$compile', function() { }); - it('should only allow one transclude per element', function() { + it('should only allow one content transclusion per element', function() { module(function() { directive('first', valueFn({ scope: {}, @@ -2716,7 +2716,28 @@ describe('$compile', function() { expect(function() { $compile('
'); }).toThrowMinErr('$compile', 'multidir', 'Multiple directives [first, second] asking for transclusion on: ' + - '
'); + '
'); + }); + }); + + + it('should only allow one element transclusion per element', function() { + module(function() { + directive('first', valueFn({ + scope: {}, + restrict: 'CA', + transclude: 'element' + })); + directive('second', valueFn({ + restrict: 'CA', + transclude: 'element' + })); + }); + inject(function($compile) { + expect(function() { + $compile('
'); + }).toThrowMinErr('$compile', 'multidir', 'Multiple directives [first, second] asking for transclusion on: ' + + '
'); }); });