当然,您的代码将不起作用,因为您试图返回尚不存在的东西。最直接的方法(但无论如何都不是最好的)是将展开的 observable 存储在服务变量中,然后提供一种检索它的方法。在下面,我使用的是简化/示例数据:
// Keep data locally.
private data;
// Listen to observable in constructor, and store locally.
constructor() {
this.item = this.af.database.object(path, {
preserveSnapshot: true
});
this.item.subscribe(snapshot => {
this.data = snapshot.val();
});
}
// Retrieve the data.
getData() { return this.data; }
但是请注意,使用快照的整个想法是不必要的。 AngularFire 返回可以直接订阅的 observables。因此:
constructor() {
this.item = this.af.database.object(path);
this.item.subscribe(data => this.data = data);
}
或者更简单,只是
constructor() {
this.af.database.object(path).subscribe(data => this.data = data);
}
但是,这种方法有一个严重的缺陷。调用访问器的人将获得 latest 值,但是当他们持有该值时,如果新值到达,他们将永远不会知道它,并且将使用旧值。或者,如果有人在 observable 第一次触发之前调用了访问器,这完全有可能,他们最终会得到undefined,然后无法检索后续值。
因此,服务应该简单地返回 observable,组件会使用它。
// SERVICE
getData() { return this.af.database.object(path); }
// COMPONENT
public data;
ngOnInit() {
this.service.getData().subscribe(data => this.data = data);
}
<div>The data is {{data}}</div>
换句话说,我们“解包”ngOnInit 中的 observable,并将解包后的值存储在本地。这将像你想要的那样工作。但是,data 的初始值将是未定义的,直到 observable 发出它的第一个值,所以如果您尝试访问数据的属性,假设它是一个对象:
<div>The name is {{data.name}}</div>
尝试访问 undefined 上的 name 属性时会出错。最明显的处理方法是使用? 运算符,如
<div> The name is {{data?.name}}</div>
但是,您可能不想在模板中的任何地方都这样做,请记住 AOT 不支持 ?。不愉快的替代方法是将其包装在 <div *ngIf="data"> 中。
因此,首选的方法是将可观察对象作为可观察对象保留到最后一刻,并仅在必要时将其解包(订阅),您可以按以下方式进行操作(使用命名变量的约定以最终形式保存可观察对象$ 为清楚起见):
// COMPONENT
public data$: Observable<any>;
ngOnInit() {
this.data$ = this.service.getData();
}
<div>The data is {{data$ | async}}</div>
使用隐式订阅的async 管道。如果你想检索data 上的属性,那么
<div>The name is {{(data$ | async).name}}</div>
一切都会按预期进行。如果你想处理 observable 还没有发出的情况,那么
The name is
<div *ngIf="data$ | async as data"; else loading">{{data.name}}</div>
<ng-template #loading>not loaded yet</ng-template>
如果您想以某种方式预处理或操作 observable 的值,而不是在服务或组件中解包它,然后对解包的值执行操作,请使用 map 操作 observable 本身:
public firstName$;
ngOnInit() {
this.firstName$ = this.service.getData().map(data => data.name.split(' ')[0]);
}
然后在您的模板中使用映射的可观察对象
The first name is
<div *ngIf="firstName$ | async as firstName"; else loading">{{firstName}}</div>
<ng-template #loading>not loaded yet</ng-template>
总而言之,使用 AngularFire 可观察对象以及所有可观察对象的首选模式是尽可能长时间地将它们作为可观察对象,通过将它们映射(或过滤)到新的可观察对象来操作它们,避免打开它们并存储如果可能的话,它们的值在本地,最后在实际需要值的点订阅,这在模板中很常见,订阅可以由async 管道处理。
使用async 管道还有另一个主要优势。如果您在组件逻辑中显式订阅,则必须记住该订阅,然后在ngOnDestroy 中取消订阅以避免内存泄漏。相比之下,Angular 会自动为您清理使用 async 管道隐式完成的所有订阅。