Skip to content

Commit

Permalink
fix($compile): check clashing directives before compilation
Browse files Browse the repository at this point in the history
  • Loading branch information
jankuca authored and vojtajina committed Oct 11, 2013
1 parent b1e604e commit e5917b4
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 24 deletions.
71 changes: 50 additions & 21 deletions src/ng/compile.js
Original file line number Diff line number Diff line change
Expand Up @@ -773,6 +773,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];
Expand All @@ -792,31 +841,17 @@ 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');
}
}

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;

if (directiveValue == 'element') {
terminalPriority = directive.priority;
Expand All @@ -836,9 +871,6 @@ function $CompileProvider($provide) {
}

if (directive.template) {
assertNoDuplicate('template', templateDirective, directive, $compileNode);
templateDirective = directive;

directiveValue = (isFunction(directive.template))
? directive.template($compileNode, templateAttrs)
: directive.template;
Expand All @@ -847,6 +879,7 @@ function $CompileProvider($provide) {

if (directive.replace) {
replaceDirective = directive;

$template = jqLite('<div>' +
trim(directiveValue) +
'</div>').contents();
Expand Down Expand Up @@ -881,9 +914,6 @@ function $CompileProvider($provide) {
}

if (directive.templateUrl) {
assertNoDuplicate('template', templateDirective, directive, $compileNode);
templateDirective = directive;

if (directive.replace) {
replaceDirective = directive;
}
Expand All @@ -908,7 +938,6 @@ function $CompileProvider($provide) {
nodeLinkFn.terminal = true;
terminalPriority = Math.max(terminalPriority, directive.priority);
}

}

nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope;
Expand Down
27 changes: 24 additions & 3 deletions test/ng/compileSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1533,7 +1533,7 @@ describe('$compile', function() {
expect(function(){
$compile('<div class="iscope-a; scope-b"></div>');
}).toThrowMinErr('$compile', 'multidir', 'Multiple directives [iscopeA, scopeB] asking for new/isolated scope on: ' +
'<div class="iscope-a; scope-b ng-isolate-scope ng-scope">');
'<div class="iscope-a; scope-b">');
})
);

Expand Down Expand Up @@ -2745,7 +2745,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: {},
Expand All @@ -2761,7 +2761,28 @@ describe('$compile', function() {
expect(function() {
$compile('<div class="first second"></div>');
}).toThrowMinErr('$compile', 'multidir', 'Multiple directives [first, second] asking for transclusion on: ' +
'<div class="first second ng-isolate-scope ng-scope">');
'<div class="first second">');
});
});


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('<div class="first second"></div>');
}).toThrowMinErr('$compile', 'multidir', 'Multiple directives [first, second] asking for transclusion on: ' +
'<div class="first second">');
});
});

Expand Down

0 comments on commit e5917b4

Please sign in to comment.