gpt4 book ai didi

javascript - Anuglar2 生命周期事件作为 rxjs Observable

转载 作者:可可西里 更新时间:2023-11-01 02:41:52 24 4
gpt4 key购买 nike

是否有构建方法来获取像 OnDestroy 这样的 angular2 生命周期事件作为 rxjs Observable

我想订阅这样的可观察对象:

ngOnInit() {
MyService.myCustomFunction()
.takeUntil(NgOnDestroy) //NgOnDestroy would be the lifecycle observable
.subscribe(() => {
//any code
});
}

这似乎比以下更直观且更易读:

private customObservable: Observable;

ngOnDestroy() {
this.customObservable.unsubscribe();
}

ngOnInit() {
this.customObservable = MyService.myCustomFunction()
.subscribe(() => {
//any code
});
}

最佳答案

没有内置方法,但如果您不想等待,可以设置装饰器或基类来完成。

基类

此解决方案适用于 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(() => {});
}
}

由于您正在继承,请确保如果您倾向于实现您还调用基类方法的生命周期接口(interface)之一(例如 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) => {
});
}
}

关于这个解决方案,有一些我认为可以合理遵循的规则:

  1. 将您的属性命名为 Angular 将调用以通知您的对象生命周期事件的方法名称以外的任何名称(例如,不要将属性命名为 ngOnInit)。这是因为装饰器会将属性创建为 getter,并且必须在类上创建该方法以拦截生命周期事件。如果忽略它,则会出现运行时错误。
  2. 如果您从使用生命周期属性装饰器的类继承并且子类为相应事件实现 Angular 接口(interface),则子类必须调用父类上的方法(例如 ngOnInit() { super.ngOnInit(); }).如果你忽略它,那么你的 observable 将不会发出,因为父类上的方法被隐藏了。
  3. 您可能想做这样的事情而不是实现 Angular 接口(interface):this.onInit.subscribe(() => this.ngOnInit())。不。这不是魔法。 Angular 只是检查函数是否存在。因此,将您在 subscribe 中调用的方法命名为不同于 Angular 接口(interface)会让您执行的操作。如果您忽略它,那么您将创建一个无限循环。

如果需要,您仍然可以为生命周期事件实现标准 Angular 接口(interface)。装饰器将覆盖它,但它会在可观察对象上发出,然后调用您的原始实现。或者,您可以只订阅相应的可观察对象。

--

需要注意的一个好处是它基本上允许您的@Input 属性是可观察的,因为 ngOnChanges 现在是可观察的。您可以使用映射设置过滤器以在属性值上创建流(例如 this.onChanges.map(x => x.myInput).filter(x => x != null).subscribe(x = > { ... });).

上面的很多代码都是在这个编辑器中键入的示例,因此可能存在语法错误。这是我在玩它时设置的一个运行示例。打开控制台以查看触发的事件。

https://codepen.io/bygrace1986/project/editor/AogqjM

关于javascript - Anuglar2 生命周期事件作为 rxjs Observable,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40737752/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com