在Angular 2中,使用observable获取一次数据
Fetch data once with Observables in Angular 2
我有一个服务,它在我的Angular 2组件中被多次使用。它从Web API中获取客户数据并返回一个Observable:
getCustomers() {
return this.http
.get(this.baseURI + this.url)
.map((r: Response) => {
let a = r.json() as Customer[];
return a;
});
}
我把这个服务注入到我的根组件中,在每个想要访问客户的组件中,我只订阅这个Observable:
this.customerService.getCustomers().subscribe(v => this.items = v);
然而,每个订阅了我的Observable的组件都会导致http请求的另一次执行。但是只获取一次数据就足够了。如果我尝试share(),它不能解决我的问题:
getCustomers() {
return this.http
.get(this.baseURI + this.url)
.map((r: Response) => {
let a = r.json() as Customer[];
return a;
}).share();
}
还是同样的问题。任何建议,哪些运营商我必须使用只获取数据一次?
1)您可以简单地将下载的数据保存在您的服务中:
export class CustomersService {
protected _customers: Array<Customer>;
constructor(public http: Http) {}
public getCustomers(): Observable<Array<Customer>> {
return new Observable(observer => {
if (this._customers) {
observer.next(this._customers);
return observer.complete();
}
this.http
.get(this.baseURI + this.url)
.map((r: Response) => (r.json() as Array<Customer>))
.subscribe((customers: Array<Customer>) => {
this._customers = customers;
observer.next(this.customers);
observer.complete();
});
});
}
}
2)取refresh
参数的更短方法:
export class CustomersService {
protected _customers: Array<Customer>;
constructor(public http: Http) {}
public getCustomers(refresh?: boolean): Observable<Array<Customer>> {
if (!refresh && this._customers) {
return Observable.of(this._customers);
}
return this.http
.get(this.baseURI + this.url)
.map((c: Response) => (c.json() as Array<Customer>))
.do((customers: Array<Customer>) => {
this._customers = customers;
});
});
}
}
3)利用ReplaySubject
:
export class CustomersService {
protected _customers$: ReplaySubject<Array<Customer>> = new ReplaySubject(1);
protected _customersInitialized: boolean;
constructor(public http: Http) {}
public getCustomers(refresh?: boolean): Observable<Array<Customer>> {
if (refresh || !this._customersInitialized) {
this._customersInitialized = true;
this.http
.get(this.baseURI + this.url)
.map((c: Response) => (c.json() as Array<Customer>))
.subscribe((customers: Array<Customer>) => {
this._customers$.next(customers);
});
}
return this._customers$.asObservable().skip(+refresh).distinctUntilChanged();
}
}
然后:
this.customersService.getCustomers()
.subscribe(customers => this.customers = customers);
您还可以从SomeService
中公开始终最新的customers
字段,用于只读目的(如在模板中显示),如下所示:
public get customers(): ReadonlyArray<Customer> {
return this._customers;
}
我将创建一个父容器,获取一次数据,并使用@Input将其传递给子组件。
父:@Component({
selector: 'BarFooHttpCaller',
template: ´<child *ngIf="data.length > 0" [data]></child>´
})
class BarFooHttpCaller {
private data: any;
constructor(private foobar:Foobar) {
this.data = {};
}
ngOnInit() {
this.foobar.getCustomers().subscribe(() => {
console.log('httpdone')
});
this.foobar.dataStream.subscribe((data) => {
console.log('new data', data);
this.data = data;
})
}
}
孩子:
import { Component, Input } from '@angular/core';
@Component({
selector: 'child',
template: ´<div>{{data}}</div>´
})
export class Child {
@Input() data: any;
}
如果你想让多个子对象订阅同一个可观察对象,但只执行一次可观察对象,你可以这样做:
注意,这确实遵循了可观察对象的设计,因为我们是在服务层执行可观察对象(observable . frompromise (stream.toPromise())),而执行应该从组件订阅中完成。查看https://www.bennadel.com/blog/3184-creating-leaky-abstractions-with-rxjs-in-angular-2-1-1.htm获取更多信息。
//declare observable to listen to
private dataObservable: Observable<any>;
getData(slug: string): Observable<any> {
//If observable does not exist/is not running create a new one
if (!this.dataObservable) {
let stream = this.http.get(slug + "/api/Endpoint")
.map(this.extractData)
.finally(() => {
//Clear the observable now that it has been listened to
this.staffDataObservable = null;
});
//Executes the http request immediately
this.dataObservable = Observable.fromPromise(stream.toPromise());
}
return this.staffDataObservable;
}
share操作符允许对多个观察者使用同一个流的结果。它可能是好的,但是你每次调用getCustomers()
都会生成一个新的可观察流,没有必要调用share()
,因为你没有多次订阅这个流。
如果你想与多个观察者共享数据,但只做一个http调用,你只需要创建第二个流,由http流提供,包含数据。之后,您的所有组件都可以订阅它。
代码可以是这样的
@Injectable()
class FooBar {
public dataStream:Subject<any> = new Subject();
constructor(private http:Http) {}
public getCustomers() {
return this.http
.get(this.baseURI + this.url)
.map((response:Response) => response.json())
.map((data) => {
this.dataStream.next(data);
return data;
})
}
}
@Component({})
class BarFooHttpCaller {
constructor(private foobar:Foobar) {}
ngOnInit() {
this.foobar.getCustomers().subscribe(() => { console.log('http done') });
this.foobar.dataStream.subscribe((data) => {
console.log('new data', data);
})
}
}
@Component({})
class OtherBarFoo {
constructor(private foobar:Foobar) {}
ngOnInit() {
this.foobar.dataStream.subscribe((data) => {
console.log('new data', data);
})
}
}
无需自定义实现。管道可以做到这一点:
getCustomers$(): Observable<Customer> {
return this.http
.get<Customer>(this.baseURI + this.url)
.pipe(shareReplay(1));
}
我在这里做了几件事:
- 添加
shareReplay(1)
管道,以确保请求只完成一次(只需要回答问题) - 删除
map
,使get
呼叫类型为 - 用
$
后缀方法名,表示返回Observable
- 如何在chrome扩展中存储数据/结果,以及如何使用setTimeout使其只被调用一次
- 数据互绑定问题:转换器只运行一次,无法绑定元素的 ID
- Jquery 循环一次或在数据数组中显示一次数据
- 如果发现任何数据,如何停止每7秒运行一次的函数
- Ajax 使用函数每秒获取一次 php 数据
- jqueryhttp将数据发布到整个数组,一次发送一个
- 将数据加载到本地存储一次
- 在用户为下一次输入插入数据后清空文本字段
- 如何在用户滚动到页面底部时仅加载一次数据
- 一次打开一行详细信息数据表
- 如何使用上一次调用的数据进行第二次 JS .post 调用
- Socket.io - 每 34 毫秒接收一次数据
- Canvas getImageData() 为了获得最佳性能.提取所有数据或一次提取一个数据
- 数据表每 30 秒自动刷新一次不起作用
- 如何在索引数据库中仅添加一次初始数据
- 在滚动时没有获取新数据,而是在滚动时一次又一次地添加相同的 json 数据
- 如何每 n 分钟使用数据更新一次 Web 应用程序
- 如何一次获取一页的数据
- 我如何让我的angularjs工厂只获取http数据一次
- AngularJS应用程序:从JSON加载数据一次,然后在多个控制器中使用它