feat(pipes): changed PipeTransform to make onDestroy optional

BREAKING CHANGE:

Before:
  Angular called onDestroy on all pipes.

After:
  Angular calls onDestroy only on pipes that have the onDestroy method.
This commit is contained in:
vsavkin 2015-08-12 10:46:06 -07:00 committed by Victor Savkin
parent ac311911c0
commit 839edaa15b
8 changed files with 60 additions and 18 deletions

View File

@ -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';
/**

View File

@ -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();
}
}
}

View File

@ -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 {

View File

@ -48,7 +48,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector<any> {
_destroyPipes() {
for (var i = 0; i < this.localPipes.length; ++i) {
if (isPresent(this.localPipes[i])) {
this.localPipes[i].onDestroy();
ChangeDetectionUtil.callPipeOnDestroy(this.localPipes[i]);
}
}
}

View File

@ -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;

View File

@ -0,0 +1,3 @@
export function implementsOnDestroy(pipe: any): boolean {
return pipe.constructor.prototype.onDestroy;
}

View File

@ -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>): any; }
transform(value: any, args: List<any>): 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>): any { return _abstract(); }
}

View File

@ -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 {