From 0598226e240b4cbb60f03e6f1e3affca45f68211 Mon Sep 17 00:00:00 2001 From: Pawel Kozlowski Date: Tue, 16 Jun 2015 20:16:08 +0200 Subject: [PATCH] fix(compiler): don't trigger duplicated directives Fixes #2756 Closes #2568 --- .../angular2/src/core/compiler/compiler.ts | 10 +++- .../test/core/compiler/integration_spec.ts | 48 +++++++++++++++++++ 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/modules/angular2/src/core/compiler/compiler.ts b/modules/angular2/src/core/compiler/compiler.ts index cf772db5ed..f1292c845f 100644 --- a/modules/angular2/src/core/compiler/compiler.ts +++ b/modules/angular2/src/core/compiler/compiler.ts @@ -153,8 +153,8 @@ export class Compiler { } } - var boundDirectives = - ListWrapper.map(directives, (directive) => this._bindDirective(directive)); + var boundDirectives = this._removeDuplicatedDirectives( + ListWrapper.map(directives, (directive) => this._bindDirective(directive))); var renderTemplate = this._buildRenderTemplate(component, view, boundDirectives); pvPromise = @@ -167,6 +167,12 @@ export class Compiler { return pvPromise; } + private _removeDuplicatedDirectives(directives: List): List { + var directivesMap: Map = new Map(); + directives.forEach((dirBinding) => { directivesMap.set(dirBinding.key.id, dirBinding); }); + return MapWrapper.values(directivesMap); + } + private _compileNestedProtoViews(componentBinding, renderPv, directives): Promise| AppProtoView { var protoViews = diff --git a/modules/angular2/test/core/compiler/integration_spec.ts b/modules/angular2/test/core/compiler/integration_spec.ts index 75f629ad12..5638fd7098 100644 --- a/modules/angular2/test/core/compiler/integration_spec.ts +++ b/modules/angular2/test/core/compiler/integration_spec.ts @@ -299,6 +299,40 @@ export function main() { .then((rootTC) => { async.done(); }); })); + it('should execute a given directive once, even if specified multiple times', + inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => { + tcb.overrideView(MyComp, new viewAnn.View({ + template: '

', + directives: [ + DuplicateDir, + DuplicateDir, + [DuplicateDir, [DuplicateDir, bind(DuplicateDir).toClass(DuplicateDir)]] + ] + })) + .createAsync(MyComp) + .then((rootTC) => { + expect(rootTC.nativeElement).toHaveText('noduplicate'); + async.done(); + }); + })); + + it('should use the last directive binding per directive', + inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => { + tcb.overrideView(MyComp, new viewAnn.View({ + template: '

', + directives: [ + bind(DuplicateDir) + .toClass(DuplicateDir), + bind(DuplicateDir).toClass(OtherDuplicateDir) + ] + })) + .createAsync(MyComp) + .then((rootTC) => { + expect(rootTC.nativeElement).toHaveText('othernoduplicate'); + async.done(); + }); + })); + it('should support directives where a selector matches property binding', inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => { tcb.overrideView(MyComp, new viewAnn.View( @@ -1805,3 +1839,17 @@ class ExportDir { @Component({selector: 'comp'}) class ComponentWithoutView { } + +@Directive({selector: '[no-duplicate]'}) +class DuplicateDir { + constructor(renderer: DomRenderer, private elRef: ElementRef) { + DOM.setText(elRef.nativeElement, DOM.getText(elRef.nativeElement) + 'noduplicate'); + } +} + +@Directive({selector: '[no-duplicate]'}) +class OtherDuplicateDir { + constructor(renderer: DomRenderer, private elRef: ElementRef) { + DOM.setText(elRef.nativeElement, DOM.getText(elRef.nativeElement) + 'othernoduplicate'); + } +}