問題
httpClient.get observableを2回サブスクライブします。ただし、これは私の呼び出しが2回実行されることを意味します。どうしてこれなの?
証明
subscribe()を実行するたびに、ログインページに別の行が表示されます。
コード(ログインページボタンからのonSubmit())
var httpHeaders = new HttpHeaders()
.append("Authorization", 'Basic ' + btoa(this.username + ':' + this.password));
var observable = this.httpClient.get('api/version/secured', { headers: httpHeaders});
observable.subscribe(
() => {
console.log('First request completed');
},
(error: HttpErrorResponse) => {
console.log('First request error');
}
);
observable.subscribe(
() => {
console.log('Second request completed');
},
(error: HttpErrorResponse) => {
console.log('Second request error');
}
);
コンソール
zone.js:2935 GET http://localhost:4200/api/version/secured 401 (Unauthorized)
login.component.ts:54 First request error
zone.js:2935 GET http://localhost:4200/api/version/secured 401 (Unauthorized)
login.component.ts:62 First request error
不適切な背景
ログイン機能をすべて処理するLogonServiceオブジェクトがあります。これには、ログインしているかどうかを示すブール変数が含まれています。ログイン関数を呼び出すたびに、httpClient.getのオブザーバブルにサブスクライブして、ログイン変数をtrueまたはfalseに設定します。しかし、ログイン関数は、サブスクライブされるオブザーバブルも返します。ダブルリクエストをダブルサブスクリプションにリンクするのに少し時間がかかりました。変数を使用するよりもログインを追跡するより良い方法がある場合は、お知らせください!私はangular :)を学ぼうとしています
次のように、_HttpClient.get
_の結果に対して share
演算子を使用してみてください。
_var observable = this.httpClient.get('api/version/secured', { headers: httpHeaders })
.pipe(share());
_
スクリプトの上に次のインポートを追加する必要があります。
_import { share } from 'rxjs/operators';
_
share
演算子は、オブザーバブルhot
を作成します。つまり、サブスクライバー間で共有されます。しかし、もっと多くのことがあるので、 この記事 でさらに深く掘り下げることをお勧めします(もちろん、_hot vs cold observables
_でググって詳細を調べることもできます)。
あなたの観測量は寒いです:
オブザーバーがオブザーバブルにサブスクライブするたびに通知のプロデューサーが作成される場合、オブザーバブルはコールドです。たとえば、監視可能なタイマーは低温です。サブスクリプションが作成されるたびに、新しいタイマーが作成されます。
Observableをmulticast
にする必要があります。
オブザーバーがオブザーバブルをサブスクライブするたびに通知のプロデューサーが作成されない場合、オブザーバブルはホットです。たとえば、fromEventを使用して作成されたオブザーバブルはホットです。イベントを生成する要素はDOMに存在します—オブザーバーがサブスクライブされたときに作成されません。
これには share 演算子を使用できますが、それでも単一のhttp呼び出しを保証することはできません。 Shareはオブザーバブルをmulticast
してサブスクライバー間で共有しますが、http呼び出しが完了すると、新しいサブスクライバーに対して新しいhttp呼び出しが行われます。
キャッシュ動作が必要な場合(呼び出しを1回実行してから、サブスクライバーがサブスクライブするたびに値を提供する)場合は、publishReplay().refCount()
を使用する必要があります。
上記の回答に加えて、http
サービスをオブザーバブルに割り当てて、データの取得をサブスクライブすることができます。例えば:
export class App implements OnInit {
cars$: Observable<Car[]>;
constructor(private carsService: carsService) {
}
ngOnInit() {
this.lessons$ = this.lessonsService.loadLessons().publishLast().refCount();
this.lessons$.subscribe(
() => console.log('lessons loaded'),
console.error
);
}
}