没有内置的方法,但是如果你不想等待,你可以设置一个装饰器或基类来实现它。
基类
这个解决方案适用于AOT。 然而,在旧版本的Angular中,存在一个bug,即当使用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 {
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上不起作用可能会成为某些项目的瓶颈。
示例
function applyLifeCycleObservable(
lifeCycleMethodName: string,
target: object,
propertyKey: string
): void {
const originalLifeCycleMethod = target.constructor.prototype[lifeCycleMethodName];
const instanceSubjectKey = Symbol(propertyKey);
Object.defineProperty(target, propertyKey, {
get: function() {
return (this[instanceSubjectKey] || (this[instanceSubjectKey] = new Subject<any>())).asObservable();
}
});
target.constructor.prototype[lifeCycleMethodName] = function() {
if (this[instanceSubjectKey]) {
this[instanceSubjectKey].next.call(this[instanceSubjectKey], arguments[0]);
}
if (originalLifeCycleMethod && typeof originalLifeCycleMethod === 'function') {
originalLifeCycleMethod.apply(this, arguments);
}
};
}
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是可观察的。您可以设置带有map过滤器的过滤器以创建属性值的流(例如this.onChanges.map(x => x.myInput).filter(x => x != null).subscribe(x => { ... });
)。
上面的大部分代码都是在此编辑器中输入的,仅供示例使用,因此可能存在语法错误。当我尝试使用它时,我设置了一个运行示例。打开控制台以查看事件触发。
https://codepen.io/bygrace1986/project/editor/AogqjM
@HostListener()
)。 - Günter Zöchbauer