Skip to content

Commit 0598226

Browse files
pkozlowski-opensourcetbosch
authored andcommitted
fix(compiler): don't trigger duplicated directives
Fixes angular#2756 Closes angular#2568
1 parent 0b50258 commit 0598226

File tree

2 files changed

+56
-2
lines changed

2 files changed

+56
-2
lines changed

modules/angular2/src/core/compiler/compiler.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,8 @@ export class Compiler {
153153
}
154154
}
155155

156-
var boundDirectives =
157-
ListWrapper.map(directives, (directive) => this._bindDirective(directive));
156+
var boundDirectives = this._removeDuplicatedDirectives(
157+
ListWrapper.map(directives, (directive) => this._bindDirective(directive)));
158158

159159
var renderTemplate = this._buildRenderTemplate(component, view, boundDirectives);
160160
pvPromise =
@@ -167,6 +167,12 @@ export class Compiler {
167167
return pvPromise;
168168
}
169169

170+
private _removeDuplicatedDirectives(directives: List<DirectiveBinding>): List<DirectiveBinding> {
171+
var directivesMap: Map<number, DirectiveBinding> = new Map();
172+
directives.forEach((dirBinding) => { directivesMap.set(dirBinding.key.id, dirBinding); });
173+
return MapWrapper.values(directivesMap);
174+
}
175+
170176
private _compileNestedProtoViews(componentBinding, renderPv, directives): Promise<AppProtoView>|
171177
AppProtoView {
172178
var protoViews =

modules/angular2/test/core/compiler/integration_spec.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,40 @@ export function main() {
299299
.then((rootTC) => { async.done(); });
300300
}));
301301

302+
it('should execute a given directive once, even if specified multiple times',
303+
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
304+
tcb.overrideView(MyComp, new viewAnn.View({
305+
template: '<p no-duplicate></p>',
306+
directives: [
307+
DuplicateDir,
308+
DuplicateDir,
309+
[DuplicateDir, [DuplicateDir, bind(DuplicateDir).toClass(DuplicateDir)]]
310+
]
311+
}))
312+
.createAsync(MyComp)
313+
.then((rootTC) => {
314+
expect(rootTC.nativeElement).toHaveText('noduplicate');
315+
async.done();
316+
});
317+
}));
318+
319+
it('should use the last directive binding per directive',
320+
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
321+
tcb.overrideView(MyComp, new viewAnn.View({
322+
template: '<p no-duplicate></p>',
323+
directives: [
324+
bind(DuplicateDir)
325+
.toClass(DuplicateDir),
326+
bind(DuplicateDir).toClass(OtherDuplicateDir)
327+
]
328+
}))
329+
.createAsync(MyComp)
330+
.then((rootTC) => {
331+
expect(rootTC.nativeElement).toHaveText('othernoduplicate');
332+
async.done();
333+
});
334+
}));
335+
302336
it('should support directives where a selector matches property binding',
303337
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
304338
tcb.overrideView(MyComp, new viewAnn.View(
@@ -1805,3 +1839,17 @@ class ExportDir {
18051839
@Component({selector: 'comp'})
18061840
class ComponentWithoutView {
18071841
}
1842+
1843+
@Directive({selector: '[no-duplicate]'})
1844+
class DuplicateDir {
1845+
constructor(renderer: DomRenderer, private elRef: ElementRef) {
1846+
DOM.setText(elRef.nativeElement, DOM.getText(elRef.nativeElement) + 'noduplicate');
1847+
}
1848+
}
1849+
1850+
@Directive({selector: '[no-duplicate]'})
1851+
class OtherDuplicateDir {
1852+
constructor(renderer: DomRenderer, private elRef: ElementRef) {
1853+
DOM.setText(elRef.nativeElement, DOM.getText(elRef.nativeElement) + 'othernoduplicate');
1854+
}
1855+
}

0 commit comments

Comments
 (0)