diff --git a/modules/angular2/src/change_detection/change_detection.ts b/modules/angular2/src/change_detection/change_detection.ts index b2846ca573..c3fc2846d0 100644 --- a/modules/angular2/src/change_detection/change_detection.ts +++ b/modules/angular2/src/change_detection/change_detection.ts @@ -44,7 +44,7 @@ export {DynamicChangeDetector} from './dynamic_change_detector'; export {ChangeDetectorRef} from './change_detector_ref'; export {IterableDiffers, IterableDiffer, IterableDifferFactory} from './differs/iterable_differs'; export {KeyValueDiffers, KeyValueDiffer, KeyValueDifferFactory} from './differs/keyvalue_differs'; -export {PipeTransform, BasePipeTransform} from './pipe_transform'; +export {PipeTransform, PipeOnDestroy, BasePipeTransform} from './pipe_transform'; export {WrappedValue} from './change_detection_util'; /** diff --git a/modules/angular2/src/change_detection/change_detection_util.ts b/modules/angular2/src/change_detection/change_detection_util.ts index 9b38195cf7..f6d6777b6d 100644 --- a/modules/angular2/src/change_detection/change_detection_util.ts +++ b/modules/angular2/src/change_detection/change_detection_util.ts @@ -2,6 +2,7 @@ import {CONST_EXPR, isPresent, isBlank, BaseException, Type} from 'angular2/src/ import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection'; import {ProtoRecord} from './proto_record'; import {CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED, ON_PUSH} from './constants'; +import {implementsOnDestroy} from './pipe_lifecycle_reflector'; /** @@ -180,4 +181,10 @@ export class ChangeDetectionUtil { null : protos[selfIndex - 1]; // self index is shifted by one because of context } + + static callPipeOnDestroy(pipe: any): void { + if (implementsOnDestroy(pipe)) { + pipe.onDestroy(); + } + } } diff --git a/modules/angular2/src/change_detection/codegen_name_util.ts b/modules/angular2/src/change_detection/codegen_name_util.ts index 6e797e6423..aba7ad6573 100644 --- a/modules/angular2/src/change_detection/codegen_name_util.ts +++ b/modules/angular2/src/change_detection/codegen_name_util.ts @@ -146,9 +146,13 @@ export class CodegenNameUtil { * Generates statements destroying all pipe variables. */ genPipeOnDestroy(): string { - return ListWrapper.join(ListWrapper.map(ListWrapper.filter(this.records, (r) => { - return r.isPipeRecord(); - }), (r) => { return `${this.getPipeName(r.selfIndex)}.onDestroy();`; }), '\n'); + return ListWrapper.join( + ListWrapper.map( + ListWrapper.filter(this.records, (r) => { return r.isPipeRecord(); }), + (r) => { + return `${this.utilName}.callPipeOnDestroy(${this.getPipeName(r.selfIndex)});`; + }), + '\n'); } getPipeName(idx: int): string { diff --git a/modules/angular2/src/change_detection/dynamic_change_detector.ts b/modules/angular2/src/change_detection/dynamic_change_detector.ts index 6aa0d14a9f..597bfd1d12 100644 --- a/modules/angular2/src/change_detection/dynamic_change_detector.ts +++ b/modules/angular2/src/change_detection/dynamic_change_detector.ts @@ -48,7 +48,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector { _destroyPipes() { for (var i = 0; i < this.localPipes.length; ++i) { if (isPresent(this.localPipes[i])) { - this.localPipes[i].onDestroy(); + ChangeDetectionUtil.callPipeOnDestroy(this.localPipes[i]); } } } diff --git a/modules/angular2/src/change_detection/pipe_lifecycle_reflector.dart b/modules/angular2/src/change_detection/pipe_lifecycle_reflector.dart new file mode 100644 index 0000000000..59ae1619e4 --- /dev/null +++ b/modules/angular2/src/change_detection/pipe_lifecycle_reflector.dart @@ -0,0 +1,5 @@ +library angular2.core.compiler.pipe_lifecycle_reflector; + +import 'package:angular2/src/change_detection/pipe_transform.dart'; + +bool implementsOnDestroy(Object pipe) => pipe is PipeOnDestroy; \ No newline at end of file diff --git a/modules/angular2/src/change_detection/pipe_lifecycle_reflector.ts b/modules/angular2/src/change_detection/pipe_lifecycle_reflector.ts new file mode 100644 index 0000000000..e0728348ce --- /dev/null +++ b/modules/angular2/src/change_detection/pipe_lifecycle_reflector.ts @@ -0,0 +1,3 @@ +export function implementsOnDestroy(pipe: any): boolean { + return pipe.constructor.prototype.onDestroy; +} \ No newline at end of file diff --git a/modules/angular2/src/change_detection/pipe_transform.ts b/modules/angular2/src/change_detection/pipe_transform.ts index 6e88a1f1b6..eeed58e581 100644 --- a/modules/angular2/src/change_detection/pipe_transform.ts +++ b/modules/angular2/src/change_detection/pipe_transform.ts @@ -7,19 +7,36 @@ import {ABSTRACT, BaseException, CONST, Type} from 'angular2/src/facade/lang'; * * ``` * class DoublePipe implements PipeTransform { - * onDestroy() {} - * * transform(value, args = []) { * return `${value}${value}`; * } * } * ``` */ -export interface PipeTransform { - onDestroy(): void; +export interface PipeTransform { transform(value: any, args: List): any; } - transform(value: any, args: List): any; -} +/** + * An interface that stateful pipes should implement. + * + * #Example + * + * ``` + * class StatefulPipe implements PipeTransform, PipeOnDestroy { + * connection; + * + * onDestroy() { + * this.connection.release(); + * } + * + * transform(value, args = []) { + * this.connection = createConnection(); + * // ... + * return someValue; + * } + * } + * ``` + */ +export interface PipeOnDestroy { onDestroy(): void; } /** * Provides default implementation of the `onDestroy` method. @@ -35,7 +52,7 @@ export interface PipeTransform { * ``` */ @CONST() -export class BasePipeTransform implements PipeTransform { +export class BasePipeTransform implements PipeTransform, PipeOnDestroy { onDestroy(): void {} transform(value: any, args: List): any { return _abstract(); } } diff --git a/modules/angular2/test/change_detection/change_detector_spec.ts b/modules/angular2/test/change_detection/change_detector_spec.ts index ee946898dd..f49dba5d97 100644 --- a/modules/angular2/test/change_detection/change_detector_spec.ts +++ b/modules/angular2/test/change_detection/change_detector_spec.ts @@ -30,6 +30,7 @@ import { DirectiveRecord, DirectiveIndex, PipeTransform, + PipeOnDestroy, CHECK_ALWAYS, CHECK_ONCE, CHECKED, @@ -768,7 +769,7 @@ export function main() { expect(cd.hydrated()).toBe(true); }); - it('should destroy all active pipes during dehyration', () => { + it('should destroy all active pipes implementing onDestroy during dehyration', () => { var pipe = new PipeWithOnDestroy(); var registry = new FakePipes('pipe', () => pipe); var cd = _createChangeDetector('name | pipe', new Person('bob'), registry).changeDetector; @@ -779,6 +780,15 @@ export function main() { expect(pipe.destroyCalled).toBe(true); }); + it('should not call onDestroy all pipes that do not implement onDestroy', () => { + var pipe = new CountingPipe(); + var registry = new FakePipes('pipe', () => pipe); + var cd = _createChangeDetector('name | pipe', new Person('bob'), registry).changeDetector; + + cd.detectChanges(); + expect(() => cd.dehydrate()).not.toThrow(); + }); + it('should throw when detectChanges is called on a dehydrated detector', () => { var context = new Person('Bob'); var val = _createChangeDetector('name', context); @@ -844,11 +854,10 @@ export function main() { class CountingPipe implements PipeTransform { state: number = 0; - onDestroy() {} transform(value, args = null) { return `${value} state:${this.state ++}`; } } -class PipeWithOnDestroy implements PipeTransform { +class PipeWithOnDestroy implements PipeTransform, PipeOnDestroy { destroyCalled: boolean = false; onDestroy() { this.destroyCalled = true; } @@ -856,12 +865,10 @@ class PipeWithOnDestroy implements PipeTransform { } class IdentityPipe implements PipeTransform { - onDestroy() {} transform(value, args = null) { return value; } } class WrappedPipe implements PipeTransform { - onDestroy() {} transform(value, args = null) { return WrappedValue.wrap(value); } } @@ -872,7 +879,6 @@ class MultiArgPipe implements PipeTransform { var arg3 = args.length > 2 ? args[2] : 'default'; return `${value} ${arg1} ${arg2} ${arg3}`; } - onDestroy(): void {} } class FakePipes implements Pipes {