Javascript 以角度2获取一次可观测数据
我有一个服务,我的Angular 2组件多次使用。它从Web API获取客户数据,并返回一个可观察的:Javascript 以角度2获取一次可观测数据,javascript,angular,rxjs,observable,Javascript,Angular,Rxjs,Observable,我有一个服务,我的Angular 2组件多次使用。它从Web API获取客户数据,并返回一个可观察的: getCustomers() { return this.http .get(this.baseURI + this.url) .map((r: Response) => { let a = r.json() as Customer[]; return a;
getCustomers() {
return this.http
.get(this.baseURI + this.url)
.map((r: Response) => {
let a = r.json() as Customer[];
return a;
});
}
this.customerService.getCustomers().subscribe(v => this.items = v);
我将此服务注入到我的根组件中,并且在每个想要访问客户的组件中,我只订阅了可观察到的服务:
getCustomers() {
return this.http
.get(this.baseURI + this.url)
.map((r: Response) => {
let a = r.json() as Customer[];
return a;
});
}
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();
}
还是一样的问题。任何我必须使用的操作符只能获取一次数据的建议?共享操作符允许对多个观察者使用同一流的结果。这可能很好,但是每次调用
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);
})
}
}
@Injectable()
福巴级{
公共数据流:主题=新主题();
构造函数(私有http:http){}
公众客户(){
返回此文件。http
.get(this.baseURI+this.url)
.map((response:response)=>response.json())
.map((数据)=>{
this.dataStream.next(数据);
返回数据;
})
}
}
@组件({})
类BarFooHttpCaller{
构造函数(私有foobar:foobar){}
恩戈尼尼特(){
this.foobar.getCustomers().subscribe(()=>{console.log('httpdone')});
this.foobar.dataStream.subscribe((数据)=>{
console.log('新数据',数据);
})
}
}
@组件({})
类OtherBarFoo{
构造函数(私有foobar:foobar){}
恩戈尼尼特(){
this.foobar.dataStream.subscribe((数据)=>{
console.log('新数据',数据);
})
}
}
我将创建一个父容器,提取一次数据,然后使用@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;
})
}
}
@组件({
选择器:“BarFooHttpCaller”,
模板:''
})
类BarFooHttpCaller{
私人资料:任何;
构造函数(专用foobar:foobar){
this.data={};
}
恩戈尼尼特(){
this.foobar.getCustomers().subscribe(()=>{
console.log('httpdone')
});
this.foobar.dataStream.subscribe((数据)=>{
console.log('新数据',数据);
这个数据=数据;
})
}
}
儿童:
import { Component, Input } from '@angular/core';
@Component({
selector: 'child',
template: ´<div>{{data}}</div>´
})
export class Child {
@Input() data: any;
}
从'@angular/core'导入{Component,Input};
@组成部分({
选择器:'子',
模板:'{{data}}}'
})
导出类子类{
@输入()数据:任意;
}
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();
});
});
}
}
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();
}
}
您还可以通过以下方式从SomeService
公开始终最新的customers
字段,以实现只读目的(如在模板中显示):
public get customers(): ReadonlyArray<Customer> {
return this._customers;
}
public get customers():ReadonlyArray{
把这个还给你的客户;
}
如果希望多个子对象订阅同一个observable,但只执行observable一次,则可以执行以下操作
请注意,这确实符合可观察的设计,因为我们在服务层(observable.fromPromis(stream.toPromise())中执行可观察,而执行应该从组件subscribing.View获得更多信息
//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;
}
//声明要侦听的可观察对象
私有数据可观察:可观察;
getData(slug:string):可观察{
//如果observable不存在/未运行,请创建一个新的
如果(!this.dataObservable){
让stream=this.http.get(slug+“/api/Endpoint”)
.map(此.extractData)
.最后(()=>{
//现在已经听过了,请清除可观察到的内容
this.staffDataObservable=null;
});
//立即执行http请求
this.dataObservable=Observable.fromPromise(stream.toPromise());
}
返回this.staffDataObservable;
}
无需自定义实现。管道可以实现以下功能:
getCustomers$(): Observable<Customer> {
return this.http
.get<Customer>(this.baseURI + this.url)
.pipe(shareReplay(1));
}
getCustomers$():可观察{
返回此文件。http
.get(this.baseURI+this.url)
.管道(1);
}
我在这里做了几件事:
shareReplay(1)
pipe,确保请求只执行一次(只需回答问题)map
并键入get
调用$
的后缀方法名称,表示返回了可观察的
好的,但在这种情况下,我必须从调用getCustomers().subscribe(…)的组件开始。在我的应用程序中,我永远不知道首先访问哪个组件(一个用户可能输入另一个URL而不是另一个用户)。当我在服务本身中调用订阅时,它实际上不起作用。在这种情况下,我应该如何修改您的示例?您有多种可能性,将数据存储在数据流之外,并验证它是否已馈送,如果是,则返回数据流而不是http流。您还可以进行一次简单的去Bounce操作,以防止多次调用o getCustomers()因此,您可以避免处理并发性。另一种方法是在顶级路由组件中调用该方法,然后在child中订阅。只需稍加修改即可。您必须记住,在您的承诺中,只创建了一个可观察对象,而没有订阅。因此,您的解决方案仅在您订阅抄写此创建的observable并在订阅中调用“resolve…”。@David你是如何做到这一点的?我通常会做整个
this.customers?Obs