diff --git a/modules/angular2/src/core/compiler/element_injector.ts b/modules/angular2/src/core/compiler/element_injector.ts index 99ae9d9162..a9bf3390b0 100644 --- a/modules/angular2/src/core/compiler/element_injector.ts +++ b/modules/angular2/src/core/compiler/element_injector.ts @@ -433,6 +433,7 @@ export class ElementInjector extends TreeNode implements Depend this._preBuiltObjects = null; this._strategy.callOnDestroy(); this._strategy.dehydrate(); + this._clearQueryLists(); } afterContentChecked(): void { @@ -837,6 +838,12 @@ export class ElementInjector extends TreeNode implements Depend var nestedView = view.getNestedView(view.elementOffset + this.getBoundElementIndex()); return isPresent(nestedView) ? nestedView.rootElementInjectors : []; } + + private _clearQueryLists(): void { + if (isPresent(this._query0) && this._query0.originator === this) this._query0.reset(); + if (isPresent(this._query1) && this._query1.originator === this) this._query1.reset(); + if (isPresent(this._query2) && this._query2.originator === this) this._query2.reset(); + } } interface _ElementInjectorStrategy { @@ -1163,4 +1170,9 @@ export class QueryRef { private _aggregateDirective(inj: ElementInjector, aggregator: any[]): void { inj.addDirectivesMatchingQuery(this.query, aggregator); } + + reset(): void { + this.list.reset([]); + this.list.removeAllCallbacks(); + } } diff --git a/modules/angular2/src/core/compiler/query_list.dart b/modules/angular2/src/core/compiler/query_list.dart index 4250ced31b..f2a22bde61 100644 --- a/modules/angular2/src/core/compiler/query_list.dart +++ b/modules/angular2/src/core/compiler/query_list.dart @@ -89,13 +89,6 @@ class QueryList extends Object _dirty = true; } - // TODO(rado): hook up with change detection after #995. - void fireCallbacks() { - if (_dirty) { - _callbacks.forEach((c) => c()); - _dirty = false; - } - } void onChange(callback) { _callbacks.add(callback); @@ -105,6 +98,10 @@ class QueryList extends Object _callbacks.remove(callback); } + void removeAllCallbacks() { + this._callbacks = []; + } + int get length => _results.length; T get first => _results.first; T get last => _results.last; @@ -116,4 +113,12 @@ class QueryList extends Object // Note: we need to return a list instead of iterable to match JS. return this._results.map(fn).toList(); } + + // Internal to the framework. + void fireCallbacks() { + if (_dirty) { + _callbacks.forEach((c) => c()); + _dirty = false; + } + } } diff --git a/modules/angular2/src/core/compiler/query_list.ts b/modules/angular2/src/core/compiler/query_list.ts index 659a5f66fe..9ac86dbd8a 100644 --- a/modules/angular2/src/core/compiler/query_list.ts +++ b/modules/angular2/src/core/compiler/query_list.ts @@ -86,17 +86,13 @@ export class QueryList { this._dirty = true; } - fireCallbacks(): void { - if (this._dirty) { - ListWrapper.forEach(this._callbacks, (c) => c()); - this._dirty = false; - } - } onChange(callback: () => void): void { this._callbacks.push(callback); } removeCallback(callback: () => void): void { ListWrapper.remove(this._callbacks, callback); } + removeAllCallbacks(): void { this._callbacks = []; } + toString(): string { return this._results.toString(); } get length(): number { return this._results.length; } @@ -106,4 +102,12 @@ export class QueryList { map(fn: (item: T) => U): U[] { return this._results.map(fn); } [Symbol.iterator](): any { return this._results[Symbol.iterator](); } + + // Internal to the framework. + fireCallbacks(): void { + if (this._dirty) { + ListWrapper.forEach(this._callbacks, (c) => c()); + this._dirty = false; + } + } } diff --git a/modules/angular2/test/core/compiler/query_integration_spec.ts b/modules/angular2/test/core/compiler/query_integration_spec.ts index 34809788a8..63920aabe0 100644 --- a/modules/angular2/test/core/compiler/query_integration_spec.ts +++ b/modules/angular2/test/core/compiler/query_integration_spec.ts @@ -212,6 +212,34 @@ export function main() { view.detectChanges(); }); })); + + it('should correctly clean-up when destroyed together with the directives it is querying', + inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => { + var template = + '
'; + + tcb.overrideTemplate(MyComp, template) + .createAsync(MyComp) + .then((view) => { + view.componentInstance.shouldShow = true; + view.detectChanges(); + + var q: NeedsQuery = view.componentViewChildren[1].getLocal('q'); + expect(q.query.length).toEqual(1); + + view.componentInstance.shouldShow = false; + view.detectChanges(); + + view.componentInstance.shouldShow = true; + view.detectChanges(); + + var q2: NeedsQuery = view.componentViewChildren[1].getLocal('q'); + + expect(q2.query.length).toEqual(1); + + async.done(); + }); + })); }); describe("querying by var binding", () => { diff --git a/modules/angular2/test/core/compiler/query_list_spec.ts b/modules/angular2/test/core/compiler/query_list_spec.ts index c3738dada6..8b233600b3 100644 --- a/modules/angular2/test/core/compiler/query_list_spec.ts +++ b/modules/angular2/test/core/compiler/query_list_spec.ts @@ -92,6 +92,19 @@ export function main() { queryList.fireCallbacks(); expect(fires).toEqual(1); }); + + it('should support removing all callbacks', () => { + var fires = 0; + var callback = () => fires += 1; + queryList.onChange(callback); + + queryList.add('one'); + queryList.removeAllCallbacks(); + + queryList.fireCallbacks(); + + expect(fires).toEqual(0); + }); }); }); }