私は多くのAngular 2コンポーネントから数回使用されているサービスを持っています。WebAPIから顧客データをフェッチし、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リクエストの別の実行を引き起こします。ただし、データを1回だけフェッチするだけで十分です。 share()を試しても問題は解決しません。
getCustomers() {
return this.http
.get(this.baseURI + this.url)
.map((r: Response) => {
let a = r.json() as Customer[];
return a;
}).share();
}
まだ同じ問題。データを1回だけ取得するために使用する必要がある演算子はありますか?
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);
次のように、読み取り専用の目的(テンプレートでの表示など)のために、customers
から常に最新のSomeService
フィールドを公開することもできます。
public get customers(): ReadonlyArray<Customer> {
return this._customers;
}
親コンテナーを作成し、データを1回フェッチして、@ 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.fromPromis(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;
}
共有演算子は、複数のオブザーバーで同じストリームの結果を使用する可能性を与えます。それは良いかもしれませんが、getCustomers()
を呼び出すたびに新しい監視可能なストリームを生成します。このストリームを複数回サブスクライブしていないので、share()
を呼び出す意味はありません。
複数のオブザーバーとデータを共有したいが、1回のhttp呼び出しのみを行う場合は、データを含む2番目のストリームを作成し、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);
})
}
}