Angular BehaviorSubject onSubscribe 事件/延迟加载 api 数据

Angular BehaviorSubject onSubscribe event / Lazy loading api data

我正在尝试在 Angular 5 中实现 HATEOAS api 数据的延迟加载。假设我有一个 class Car,它包含对Engine 的实例,但只有在访问时才应加载引擎。

A class Car:

export class Car {
    private _engine = new BehaviorSubject<Engine>(null);
    engine = _engine.asObservable()

    constructor(
        private enginesService
    ) {}
}

A class Engine:

export class Engine {
    type: string;
}

一些模板可以通过组件变量 car 访问 Car 的实例,它异步访问引擎可观察对象:

<div *ngIf="car.engine | async; let engine; else loadingEngine">
    <p>{{engine.type}}</p>
</div>
<ng-template #loadingEngine>Loading engine...</ng-template>

以上,汽车不知道什么时候访问engine,也不知道引擎必须从api加载。现在我尝试仅通过 getter 方法提供对 Car.engine 的访问,这将 return car.asObservable() 并触发 api 请求,如下所示:

export class Car {
    private _engine = new BehaviorSubject<Engine>(null);

    constructor(
        private enginesService
    ) {}

    engine(): Observable<Engine> {
        this.enginesService.getEngine().subscribe( engine => {
            _engine.next(engine);
        )
        return _engine.asObservable()
    }
}

但是在使用模板中的方法时,它被反复调用,导致许多api请求,导致浏览器崩溃。

我想到了将 BehaviorSubject 包装在 class 中,它提供 onSubscribe 事件。 Car 可以订阅 onSubscribe 事件,并且会知道什么时候尝试获取引擎值。有没有其他方法知道何时访问 engine

编辑:

funkizer 把我带到了冷观测。我更新了我的 Car 实现,以将引擎作为冷可观察对象提供。现在汽车甚至不需要知道引擎是否被访问,它只提供一次 observable,每次订阅都会触发 api 调用:

export class Car {
    private _engine: Engine;
    engine: Observable<Engine> = new Observable( observer => {
        enginesService.getEngine().subscribe( engine => {
            this._engine = engine;
            observer.next(this._engine);
        }
    })

    constructor(
        private enginesService
    ) {}
}

HttpClient returns cold observables(只有当有人订阅时才会做任何事情),所以你不需要订阅 Car 中的 api 调用,以及 api 调用只会在需要时发生。您可以通过 shareReplay() 将 Observable 传递给模板,因此只有一个 api 请求。尝试:

engine$:Observable<Engine> = this.enginesService.getEngine().pipe(shareReplay(1))

<div *ngIf="car.engine$|async as engine">...

编辑: Full StackBlitz 示例:https://stackblitz.com/edit/angular-ea52a7?file=app%2Fapp.component.html