没有内置方法,但如果您不想等待,可以设置装饰器或基类来完成。
基类
此解决方案适用于 AOT。 但是,在旧版本的 Angular 中,存在一个错误,即在使用 AOT 时未注册基类上的生命周期事件。它至少似乎在 4.4.x+ 中工作。您可以在此处获取更多信息,看看您的版本是否会受到影响:https://github.com/angular/angular/issues/12922
示例
import { SimpleChanges, OnChanges, OnInit, DoCheck, AfterContentInit, AfterContentChecked, AfterViewInit, AfterViewChecked, OnDestroy } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';
import 'rxjs/add/operator/takeUntil';
import 'rxjs/add/operator/take';
const onChangesKey = Symbol('onChanges');
const onInitKey = Symbol('onInit');
const doCheckKey = Symbol('doCheck');
const afterContentInitKey = Symbol('afterContentInit');
const afterContentCheckedKey = Symbol('afterContentChecked');
const afterViewInitKey = Symbol('afterViewInit');
const afterViewCheckedKey = Symbol('afterViewChecked');
const onDestroyKey = Symbol('onDestroy');
export abstract class LifeCycleComponent implements OnChanges, OnInit, DoCheck, AfterContentInit, AfterContentChecked, AfterViewInit, AfterViewChecked, OnDestroy {
// all observables will complete on component destruction
protected get onChanges(): Observable<SimpleChanges> { return this.getObservable(onChangesKey).takeUntil(this.onDestroy); }
protected get onInit(): Observable<void> { return this.getObservable(onInitKey).takeUntil(this.onDestroy).take(1); }
protected get doCheck(): Observable<void> { return this.getObservable(doCheckKey).takeUntil(this.onDestroy); }
protected get afterContentInit(): Observable<void> { return this.getObservable(afterContentInitKey).takeUntil(this.onDestroy).take(1); }
protected get afterContentChecked(): Observable<void> { return this.getObservable(afterContentCheckedKey).takeUntil(this.onDestroy); }
protected get afterViewInit(): Observable<void> { return this.getObservable(afterViewInitKey).takeUntil(this.onDestroy).take(1); }
protected get afterViewChecked(): Observable<void> { return this.getObservable(afterViewCheckedKey).takeUntil(this.onDestroy); }
protected get onDestroy(): Observable<void> { return this.getObservable(onDestroyKey).take(1); }
ngOnChanges(changes: SimpleChanges): void { this.emit(onChangesKey, changes); };
ngOnInit(): void { this.emit(onInitKey); };
ngDoCheck(): void { this.emit(doCheckKey); };
ngAfterContentInit(): void { this.emit(afterContentInitKey); };
ngAfterContentChecked(): void { this.emit(afterContentCheckedKey); };
ngAfterViewInit(): void { this.emit(afterViewInitKey); };
ngAfterViewChecked(): void { this.emit(afterViewCheckedKey); };
ngOnDestroy(): void { this.emit(onDestroyKey); };
private getObservable(key: symbol): Observable<any> {
return (this[key] || (this[key] = new Subject<any>())).asObservable();
}
private emit(key: symbol, value?: any): void {
const subject = this[key];
if (!subject) return;
subject.next(value);
}
}
用法
import { Component, OnInit } from '@angular/core';
import { LifeCycleComponent } from './life-cycle.component';
import { MyService } from './my.service'
@Component({
template: ''
})
export class TestBaseComponent extends LifeCycleComponent implements OnInit {
constructor(private myService: MyService) {
super();
}
ngOnInit() {
super.ngOnInit();
this.myService.takeUntil(this.onDestroy).subscribe(() => {});
}
}
由于您正在继承,请确保如果您倾向于实现其中一个生命周期接口,您还可以调用基类方法(例如ngOnInit() { super.ngOnInit(); })。
装饰器
此解决方案不适用于 AOT。就我个人而言,我更喜欢这种方法,但它不能与 AOT 一起使用对于某些项目来说是一种破坏交易的方式。
示例
/**
* Creates an observable property on an object that will
* emit when the corresponding life-cycle event occurs.
* The main rules are:
* 1. Don't name the property the same as the angular interface method.
* 2. If a class inherits from another component where the parent uses this decorator
* and the child implements the corresponding interface then it needs to call the parent method.
* @param {string} lifeCycleMethodName name of the function that angular calls for the life-cycle event
* @param {object} target class that contains the decorated property
* @param {string} propertyKey name of the decorated property
*/
function applyLifeCycleObservable(
lifeCycleMethodName: string,
target: object,
propertyKey: string
): void {
// Save a reference to the original life-cycle callback so that we can call it if it exists.
const originalLifeCycleMethod = target.constructor.prototype[lifeCycleMethodName];
// Use a symbol to make the observable for the instance unobtrusive.
const instanceSubjectKey = Symbol(propertyKey);
Object.defineProperty(target, propertyKey, {
get: function() {
// Get the observable for this instance or create it.
return (this[instanceSubjectKey] || (this[instanceSubjectKey] = new Subject<any>())).asObservable();
}
});
// Add or override the life-cycle callback.
target.constructor.prototype[lifeCycleMethodName] = function() {
// If it hasn't been created then there no subscribers so there is no need to emit
if (this[instanceSubjectKey]) {
// Emit the life-cycle event.
// We pass the first parameter because onChanges has a SimpleChanges parameter.
this[instanceSubjectKey].next.call(this[instanceSubjectKey], arguments[0]);
}
// If the object already had a life-cycle callback then invoke it.
if (originalLifeCycleMethod && typeof originalLifeCycleMethod === 'function') {
originalLifeCycleMethod.apply(this, arguments);
}
};
}
// Property Decorators
export function OnChangesObservable(target: any, propertyKey: string) {
applyLifeCycleObservable('ngOnChanges', target, propertyKey);
}
export function OnInitObservable(target: any, propertyKey: string) {
applyLifeCycleObservable('ngOnInit', target, propertyKey);
}
export function DoCheckObservable(target: any, propertyKey: string) {
applyLifeCycleObservable('ngDoCheck', target, propertyKey);
}
export function AfterContentInitObservable(target: any, propertyKey: string) {
applyLifeCycleObservable('ngAfterContentInit', target, propertyKey);
}
export function AfterContentCheckedObservable(target: any, propertyKey: string) {
applyLifeCycleObservable('ngAfterContentChecked', target, propertyKey);
}
export function AfterViewInitObservable(target: any, propertyKey: string) {
applyLifeCycleObservable('ngAfterViewInit', target, propertyKey);
}
export function AfterViewCheckedObservable(target: any, propertyKey: string) {
applyLifeCycleObservable('ngAfterViewChecked', target, propertyKey);
}
export function OnDestroyObservable(target: any, propertyKey: string) {
applyLifeCycleObservable('ngOnDestroy', target, propertyKey);
}
用法
import { Component, OnInit, Input, SimpleChange } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import {
OnChangesObservable,
OnInitObservable,
DoCheckObservable,
AfterContentInitObservable,
AfterContentCheckedObservable,
AfterViewInitObservable,
AfterViewCheckedObservable,
OnDestroyObservable
} from './life-cycle.decorator';
import { MyService } from './my.service'
@Component({
template: ''
})
export class TestDecoratorComponent implements OnInit {
@OnChangesObservable
onChanges: Observable<SimpleChanges>;
@OnInitObservable
onInit: Observable<void>;
@DoCheckObservable
doCheck: Observable<void>;
@AfterContentInitObservable
afterContentInit: Observable<void>;
@AfterContentCheckedObservable
afterContentChecked: Observable<void>;
@AfterViewInitObservable
afterViewInit: Observable<void>;
@AfterViewCheckedObservable
afterViewChecked: Observable<void>;
@OnDestroyObservable
onDestroy: Observable<void>;
@Input()
input: string;
constructor(private myService: MyService) {
}
ngOnInit() {
this.myService.takeUntil(this.onDestroy).subscribe(() => {});
this.onChanges
.map(x => x.input)
.filter(x => x != null)
.takeUntil(this.onDestroy)
.subscribe((change: SimpleChange) => {
});
}
}
关于这个解决方案有一些我认为可以遵循的规则:
- 将您的属性命名为除 Angular 将调用以通知您的对象生命周期事件的方法名称之外的任何名称(例如,不要将属性命名为 ngOnInit)。这是因为装饰器会将属性创建为 getter,并且必须在类上创建该方法以拦截生命周期事件。如果您忽略这一点,您将收到运行时错误。
- 如果您从使用生命周期属性装饰器的类继承,并且子类为相应事件实现了 angular 接口,则子类必须调用父类上的方法(例如
ngOnInit() { super.ngOnInit(); })。如果您忽略这一点,那么您的 observable 将不会发出,因为父类上的方法被遮蔽了。
- 您可能很想这样做而不是实现 Angular 接口:
this.onInit.subscribe(() => this.ngOnInit())。别。它不是魔法。 Angular 只是检查函数是否存在。因此,将您调用的方法命名为 subscribe 的名称,而不是 Angular 接口让您执行的操作。如果您忽略这一点,那么您将创建一个无限循环。
如果您愿意,您仍然可以为生命周期事件实现标准的 Angular 接口。装饰器将覆盖它,但它会在 observable 上发出,然后调用您的原始实现。或者,您可以只订阅相应的 observable。
--
需要注意的一个好处是它基本上允许您的 @Input 属性是可观察的,因为 ngOnChanges 现在是可观察的。您可以使用映射设置过滤器以在属性值上创建流(例如this.onChanges.map(x => x.myInput).filter(x => x != null).subscribe(x => { ... });)。
上面的很多代码都是在这个编辑器中输入的,因此可能存在语法错误。这是我在玩它时设置的一个运行示例。打开控制台查看事件触发。
https://codepen.io/bygrace1986/project/editor/AogqjM