refactor(pipes): use angular lifecycle hooks instead of PipeOnDestroy
BREAKING CHANGE: Previously, pipes that wanted to be notified when they were destroyed would implement the PipeOnDestroy interface and name the callback `onDestroy`. This change removes the PipeOnDestroy interface and instead uses Angular's lifecycle interface `OnDestroy`, with the `ngOnDestroy` method. Before: ``` import {Pipe, PipeOnDestroy} from 'angular2/angular2'; @Pipe({pure: false}) export class MyPipe implements PipeOnDestroy { onDestroy() {} } ``` After: import {Pipe, OnDestroy} from 'angular2/angular2'; @Pipe({pure: false}) export class MyPipe implements PipeOnDestroy { ngOnDestroy() {} }
This commit is contained in:
parent
604c8bbad5
commit
fcc7ce225e
@ -4,7 +4,7 @@ import {
|
|||||||
Pipe,
|
Pipe,
|
||||||
Injectable,
|
Injectable,
|
||||||
ChangeDetectorRef,
|
ChangeDetectorRef,
|
||||||
PipeOnDestroy,
|
OnDestroy,
|
||||||
PipeTransform,
|
PipeTransform,
|
||||||
WrappedValue
|
WrappedValue
|
||||||
} from 'angular2/core';
|
} from 'angular2/core';
|
||||||
@ -55,7 +55,7 @@ var _observableStrategy = new ObservableStrategy();
|
|||||||
*/
|
*/
|
||||||
@Pipe({name: 'async', pure: false})
|
@Pipe({name: 'async', pure: false})
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AsyncPipe implements PipeTransform, PipeOnDestroy {
|
export class AsyncPipe implements PipeTransform, OnDestroy {
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_latestValue: Object = null;
|
_latestValue: Object = null;
|
||||||
/** @internal */
|
/** @internal */
|
||||||
@ -70,7 +70,7 @@ export class AsyncPipe implements PipeTransform, PipeOnDestroy {
|
|||||||
public _ref: ChangeDetectorRef;
|
public _ref: ChangeDetectorRef;
|
||||||
constructor(_ref: ChangeDetectorRef) { this._ref = _ref; }
|
constructor(_ref: ChangeDetectorRef) { this._ref = _ref; }
|
||||||
|
|
||||||
onDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
if (isPresent(this._subscription)) {
|
if (isPresent(this._subscription)) {
|
||||||
this._dispose();
|
this._dispose();
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,6 @@ export {
|
|||||||
WrappedValue,
|
WrappedValue,
|
||||||
SimpleChange,
|
SimpleChange,
|
||||||
PipeTransform,
|
PipeTransform,
|
||||||
PipeOnDestroy,
|
|
||||||
IterableDiffers,
|
IterableDiffers,
|
||||||
IterableDiffer,
|
IterableDiffer,
|
||||||
IterableDifferFactory,
|
IterableDifferFactory,
|
||||||
|
@ -39,7 +39,7 @@ export {DynamicChangeDetector} from './dynamic_change_detector';
|
|||||||
export {ChangeDetectorRef} from './change_detector_ref';
|
export {ChangeDetectorRef} from './change_detector_ref';
|
||||||
export {IterableDiffers, IterableDiffer, IterableDifferFactory} from './differs/iterable_differs';
|
export {IterableDiffers, IterableDiffer, IterableDifferFactory} from './differs/iterable_differs';
|
||||||
export {KeyValueDiffers, KeyValueDiffer, KeyValueDifferFactory} from './differs/keyvalue_differs';
|
export {KeyValueDiffers, KeyValueDiffer, KeyValueDifferFactory} from './differs/keyvalue_differs';
|
||||||
export {PipeTransform, PipeOnDestroy} from './pipe_transform';
|
export {PipeTransform} from './pipe_transform';
|
||||||
export {WrappedValue, SimpleChange} from './change_detection_util';
|
export {WrappedValue, SimpleChange} from './change_detection_util';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -195,7 +195,7 @@ export class ChangeDetectionUtil {
|
|||||||
|
|
||||||
static callPipeOnDestroy(selectedPipe: SelectedPipe): void {
|
static callPipeOnDestroy(selectedPipe: SelectedPipe): void {
|
||||||
if (implementsOnDestroy(selectedPipe.pipe)) {
|
if (implementsOnDestroy(selectedPipe.pipe)) {
|
||||||
(<any>selectedPipe.pipe).onDestroy();
|
(<any>selectedPipe.pipe).ngOnDestroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
library angular2.core.compiler.pipe_lifecycle_reflector;
|
library angular2.core.compiler.pipe_lifecycle_reflector;
|
||||||
|
|
||||||
import 'package:angular2/src/core/change_detection/pipe_transform.dart';
|
import 'package:angular2/src/core/linker/interfaces.dart';
|
||||||
|
|
||||||
bool implementsOnDestroy(Object pipe) => pipe is PipeOnDestroy;
|
bool implementsOnDestroy(Object pipe) => pipe is OnDestroy;
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
export function implementsOnDestroy(pipe: any): boolean {
|
export function implementsOnDestroy(pipe: any): boolean {
|
||||||
return pipe.constructor.prototype.onDestroy;
|
return pipe.constructor.prototype.ngOnDestroy;
|
||||||
}
|
}
|
||||||
|
@ -31,55 +31,3 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export interface PipeTransform { transform(value: any, args: any[]): any; }
|
export interface PipeTransform { transform(value: any, args: any[]): any; }
|
||||||
|
|
||||||
/**
|
|
||||||
* To create a stateful Pipe, you should implement this interface and set the `pure`
|
|
||||||
* parameter to `false` in the {@link PipeMetadata}.
|
|
||||||
*
|
|
||||||
* A stateful pipe may produce different output, given the same input. It is
|
|
||||||
* likely that a stateful pipe may contain state that should be cleaned up when
|
|
||||||
* a binding is destroyed. For example, a subscription to a stream of data may need to
|
|
||||||
* be disposed, or an interval may need to be cleared.
|
|
||||||
*
|
|
||||||
* ### Example ([live demo](http://plnkr.co/edit/i8pm5brO4sPaLxBx56MR?p=preview))
|
|
||||||
*
|
|
||||||
* In this example, a pipe is created to countdown its input value, updating it every
|
|
||||||
* 50ms. Because it maintains an internal interval, it automatically clears
|
|
||||||
* the interval when the binding is destroyed or the countdown completes.
|
|
||||||
*
|
|
||||||
* ```
|
|
||||||
* import {Pipe, PipeTransform} from 'angular2/angular2'
|
|
||||||
* @Pipe({name: 'countdown', pure: false})
|
|
||||||
* class CountDown implements PipeTransform, PipeOnDestroy {
|
|
||||||
* remainingTime:Number;
|
|
||||||
* interval:SetInterval;
|
|
||||||
* onDestroy() {
|
|
||||||
* if (this.interval) {
|
|
||||||
* clearInterval(this.interval);
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* transform(value: any, args: any[] = []) {
|
|
||||||
* if (!parseInt(value, 10)) return null;
|
|
||||||
* if (typeof this.remainingTime !== 'number') {
|
|
||||||
* this.remainingTime = parseInt(value, 10);
|
|
||||||
* }
|
|
||||||
* if (!this.interval) {
|
|
||||||
* this.interval = setInterval(() => {
|
|
||||||
* this.remainingTime-=50;
|
|
||||||
* if (this.remainingTime <= 0) {
|
|
||||||
* this.remainingTime = 0;
|
|
||||||
* clearInterval(this.interval);
|
|
||||||
* delete this.interval;
|
|
||||||
* }
|
|
||||||
* }, 50);
|
|
||||||
* }
|
|
||||||
* return this.remainingTime;
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* Invoking `{{ 10000 | countdown }}` would cause the value to be decremented by 50,
|
|
||||||
* every 50ms, until it reaches 0.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
export interface PipeOnDestroy { onDestroy(): void; }
|
|
||||||
|
@ -226,6 +226,56 @@ export interface DoCheck { ngDoCheck(); }
|
|||||||
*
|
*
|
||||||
* bootstrap(App).catch(err => console.error(err));
|
* bootstrap(App).catch(err => console.error(err));
|
||||||
* ```
|
* ```
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* To create a stateful Pipe, you should implement this interface and set the `pure`
|
||||||
|
* parameter to `false` in the {@link PipeMetadata}.
|
||||||
|
*
|
||||||
|
* A stateful pipe may produce different output, given the same input. It is
|
||||||
|
* likely that a stateful pipe may contain state that should be cleaned up when
|
||||||
|
* a binding is destroyed. For example, a subscription to a stream of data may need to
|
||||||
|
* be disposed, or an interval may need to be cleared.
|
||||||
|
*
|
||||||
|
* ### Example ([live demo](http://plnkr.co/edit/i8pm5brO4sPaLxBx56MR?p=preview))
|
||||||
|
*
|
||||||
|
* In this example, a pipe is created to countdown its input value, updating it every
|
||||||
|
* 50ms. Because it maintains an internal interval, it automatically clears
|
||||||
|
* the interval when the binding is destroyed or the countdown completes.
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* import {OnDestroy, Pipe, PipeTransform} from 'angular2/angular2'
|
||||||
|
* @Pipe({name: 'countdown', pure: false})
|
||||||
|
* class CountDown implements PipeTransform, OnDestroy {
|
||||||
|
* remainingTime:Number;
|
||||||
|
* interval:SetInterval;
|
||||||
|
* ngOnDestroy() {
|
||||||
|
* if (this.interval) {
|
||||||
|
* clearInterval(this.interval);
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* transform(value: any, args: any[] = []) {
|
||||||
|
* if (!parseInt(value, 10)) return null;
|
||||||
|
* if (typeof this.remainingTime !== 'number') {
|
||||||
|
* this.remainingTime = parseInt(value, 10);
|
||||||
|
* }
|
||||||
|
* if (!this.interval) {
|
||||||
|
* this.interval = setInterval(() => {
|
||||||
|
* this.remainingTime-=50;
|
||||||
|
* if (this.remainingTime <= 0) {
|
||||||
|
* this.remainingTime = 0;
|
||||||
|
* clearInterval(this.interval);
|
||||||
|
* delete this.interval;
|
||||||
|
* }
|
||||||
|
* }, 50);
|
||||||
|
* }
|
||||||
|
* return this.remainingTime;
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* Invoking `{{ 10000 | countdown }}` would cause the value to be decremented by 50,
|
||||||
|
* every 50ms, until it reaches 0.
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
export interface OnDestroy { ngOnDestroy(); }
|
export interface OnDestroy { ngOnDestroy(); }
|
||||||
|
|
||||||
|
@ -96,13 +96,13 @@ export function main() {
|
|||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("onDestroy", () => {
|
describe("ngOnDestroy", () => {
|
||||||
it("should do nothing when no subscription",
|
it("should do nothing when no subscription",
|
||||||
() => { expect(() => pipe.onDestroy()).not.toThrow(); });
|
() => { expect(() => pipe.ngOnDestroy()).not.toThrow(); });
|
||||||
|
|
||||||
it("should dispose of the existing subscription", inject([AsyncTestCompleter], (async) => {
|
it("should dispose of the existing subscription", inject([AsyncTestCompleter], (async) => {
|
||||||
pipe.transform(emitter);
|
pipe.transform(emitter);
|
||||||
pipe.onDestroy();
|
pipe.ngOnDestroy();
|
||||||
|
|
||||||
ObservableWrapper.callEmit(emitter, message);
|
ObservableWrapper.callEmit(emitter, message);
|
||||||
|
|
||||||
@ -182,9 +182,9 @@ export function main() {
|
|||||||
}, timer)
|
}, timer)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe("onDestroy", () => {
|
describe("ngOnDestroy", () => {
|
||||||
it("should do nothing when no source",
|
it("should do nothing when no source",
|
||||||
() => { expect(() => pipe.onDestroy()).not.toThrow(); });
|
() => { expect(() => pipe.ngOnDestroy()).not.toThrow(); });
|
||||||
|
|
||||||
it("should dispose of the existing source", inject([AsyncTestCompleter], (async) => {
|
it("should dispose of the existing source", inject([AsyncTestCompleter], (async) => {
|
||||||
pipe.transform(completer.promise);
|
pipe.transform(completer.promise);
|
||||||
@ -194,7 +194,7 @@ export function main() {
|
|||||||
|
|
||||||
TimerWrapper.setTimeout(() => {
|
TimerWrapper.setTimeout(() => {
|
||||||
expect(pipe.transform(completer.promise)).toEqual(new WrappedValue(message));
|
expect(pipe.transform(completer.promise)).toEqual(new WrappedValue(message));
|
||||||
pipe.onDestroy();
|
pipe.ngOnDestroy();
|
||||||
expect(pipe.transform(completer.promise)).toBe(null);
|
expect(pipe.transform(completer.promise)).toBe(null);
|
||||||
async.done();
|
async.done();
|
||||||
}, timer);
|
}, timer);
|
||||||
|
@ -10,20 +10,20 @@ import {
|
|||||||
afterEach
|
afterEach
|
||||||
} from 'angular2/testing_internal';
|
} from 'angular2/testing_internal';
|
||||||
|
|
||||||
import {Injector, Inject, provide, Pipe, PipeTransform} from 'angular2/core';
|
import {Injector, Inject, provide, Pipe, PipeTransform, OnDestroy} from 'angular2/core';
|
||||||
import {ProtoPipes, Pipes} from 'angular2/src/core/pipes/pipes';
|
import {ProtoPipes, Pipes} from 'angular2/src/core/pipes/pipes';
|
||||||
import {PipeProvider} from 'angular2/src/core/pipes/pipe_provider';
|
import {PipeProvider} from 'angular2/src/core/pipes/pipe_provider';
|
||||||
|
|
||||||
class PipeA implements PipeTransform {
|
class PipeA implements PipeTransform, OnDestroy {
|
||||||
transform(a, b) {}
|
transform(a, b) {}
|
||||||
onDestroy() {}
|
ngOnDestroy() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
class PipeB implements PipeTransform {
|
class PipeB implements PipeTransform, OnDestroy {
|
||||||
dep;
|
dep;
|
||||||
constructor(@Inject("dep") dep: any) { this.dep = dep; }
|
constructor(@Inject("dep") dep: any) { this.dep = dep; }
|
||||||
transform(a, b) {}
|
transform(a, b) {}
|
||||||
onDestroy() {}
|
ngOnDestroy() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
|
@ -35,7 +35,6 @@ import {
|
|||||||
DirectiveRecord,
|
DirectiveRecord,
|
||||||
DirectiveIndex,
|
DirectiveIndex,
|
||||||
PipeTransform,
|
PipeTransform,
|
||||||
PipeOnDestroy,
|
|
||||||
ChangeDetectionStrategy,
|
ChangeDetectionStrategy,
|
||||||
WrappedValue,
|
WrappedValue,
|
||||||
DynamicProtoChangeDetector,
|
DynamicProtoChangeDetector,
|
||||||
@ -48,6 +47,7 @@ import {
|
|||||||
|
|
||||||
import {SelectedPipe, Pipes} from 'angular2/src/core/change_detection/pipes';
|
import {SelectedPipe, Pipes} from 'angular2/src/core/change_detection/pipes';
|
||||||
import {JitProtoChangeDetector} from 'angular2/src/core/change_detection/jit_proto_change_detector';
|
import {JitProtoChangeDetector} from 'angular2/src/core/change_detection/jit_proto_change_detector';
|
||||||
|
import {OnDestroy} from 'angular2/src/core/linker/interfaces';
|
||||||
|
|
||||||
import {getDefinition} from './change_detector_config';
|
import {getDefinition} from './change_detector_config';
|
||||||
import {createObservableModel} from './change_detector_spec_util';
|
import {createObservableModel} from './change_detector_spec_util';
|
||||||
@ -1228,7 +1228,7 @@ export function main() {
|
|||||||
expect(cd.hydrated()).toBe(true);
|
expect(cd.hydrated()).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should destroy all active pipes implementing onDestroy during dehyration', () => {
|
it('should destroy all active pipes implementing ngOnDestroy during dehyration', () => {
|
||||||
var pipe = new PipeWithOnDestroy();
|
var pipe = new PipeWithOnDestroy();
|
||||||
var registry = new FakePipes('pipe', () => pipe);
|
var registry = new FakePipes('pipe', () => pipe);
|
||||||
var cd = _createChangeDetector('name | pipe', new Person('bob'), registry).changeDetector;
|
var cd = _createChangeDetector('name | pipe', new Person('bob'), registry).changeDetector;
|
||||||
@ -1239,7 +1239,7 @@ export function main() {
|
|||||||
expect(pipe.destroyCalled).toBe(true);
|
expect(pipe.destroyCalled).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not call onDestroy all pipes that do not implement onDestroy', () => {
|
it('should not call ngOnDestroy all pipes that do not implement ngOnDestroy', () => {
|
||||||
var pipe = new CountingPipe();
|
var pipe = new CountingPipe();
|
||||||
var registry = new FakePipes('pipe', () => pipe);
|
var registry = new FakePipes('pipe', () => pipe);
|
||||||
var cd = _createChangeDetector('name | pipe', new Person('bob'), registry).changeDetector;
|
var cd = _createChangeDetector('name | pipe', new Person('bob'), registry).changeDetector;
|
||||||
@ -1365,9 +1365,9 @@ class CountingPipe implements PipeTransform {
|
|||||||
transform(value, args = null) { return `${value} state:${this.state ++}`; }
|
transform(value, args = null) { return `${value} state:${this.state ++}`; }
|
||||||
}
|
}
|
||||||
|
|
||||||
class PipeWithOnDestroy implements PipeTransform, PipeOnDestroy {
|
class PipeWithOnDestroy implements PipeTransform, OnDestroy {
|
||||||
destroyCalled: boolean = false;
|
destroyCalled: boolean = false;
|
||||||
onDestroy() { this.destroyCalled = true; }
|
ngOnDestroy() { this.destroyCalled = true; }
|
||||||
|
|
||||||
transform(value, args = null) { return null; }
|
transform(value, args = null) { return null; }
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,8 @@ import {
|
|||||||
Inject,
|
Inject,
|
||||||
Host,
|
Host,
|
||||||
SkipSelf,
|
SkipSelf,
|
||||||
SkipSelfMetadata
|
SkipSelfMetadata,
|
||||||
|
OnDestroy
|
||||||
} from 'angular2/core';
|
} from 'angular2/core';
|
||||||
|
|
||||||
import {NgIf, NgFor} from 'angular2/common';
|
import {NgIf, NgFor} from 'angular2/common';
|
||||||
@ -1999,8 +2000,8 @@ class SomeViewport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Pipe({name: 'double'})
|
@Pipe({name: 'double'})
|
||||||
class DoublePipe implements PipeTransform {
|
class DoublePipe implements PipeTransform, OnDestroy {
|
||||||
onDestroy() {}
|
ngOnDestroy() {}
|
||||||
transform(value, args = null) { return `${value}${value}`; }
|
transform(value, args = null) { return `${value}${value}`; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +108,7 @@ var NG_ALL = [
|
|||||||
'ApplicationRef.tick()',
|
'ApplicationRef.tick()',
|
||||||
*/
|
*/
|
||||||
'AsyncPipe',
|
'AsyncPipe',
|
||||||
'AsyncPipe.onDestroy()',
|
'AsyncPipe.ngOnDestroy()',
|
||||||
'AsyncPipe.transform()',
|
'AsyncPipe.transform()',
|
||||||
'Attribute',
|
'Attribute',
|
||||||
'Attribute.attributeName',
|
'Attribute.attributeName',
|
||||||
@ -1425,7 +1425,6 @@ var NG_ALL = [
|
|||||||
'OnChanges:dart',
|
'OnChanges:dart',
|
||||||
'OnDestroy:dart',
|
'OnDestroy:dart',
|
||||||
'OnInit:dart',
|
'OnInit:dart',
|
||||||
'PipeOnDestroy:dart',
|
|
||||||
'PipeTransform:dart',
|
'PipeTransform:dart',
|
||||||
'reflector',
|
'reflector',
|
||||||
'RenderBeginCmd:dart',
|
'RenderBeginCmd:dart',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user