web-dev-qa-db-ja.com

Angular 2 2回のサブスクライブが2回呼び出しを2回実行する

問題
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 :)を学ぼうとしています

21
Rico

次のように、_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_でググって詳細を調べることもできます)。

10
Jeto

あなたの観測量は寒いです:

オブザーバーがオブザーバブルにサブスクライブするたびに通知のプロデューサーが作成される場合、オブザーバブルはコールドです。たとえば、監視可能なタイマーは低温です。サブスクリプションが作成されるたびに、新しいタイマーが作成されます。

Observableをmulticastにする必要があります。

オブザーバーがオブザーバブルをサブスクライブするたびに通知のプロデューサーが作成されない場合、オブザーバブルはホットです。たとえば、fromEventを使用して作成されたオブザーバブルはホットです。イベントを生成する要素はDOMに存在します—オブザーバーがサブスクライブされたときに作成されません。

これには share 演算子を使用できますが、それでも単一のhttp呼び出しを保証することはできません。 Shareはオブザーバブルをmulticastしてサブスクライバー間で共有しますが、http呼び出しが完了すると、新しいサブスクライバーに対して新しいhttp呼び出しが行われます。

キャッシュ動作が必要な場合(呼び出しを1回実行してから、サブスクライバーがサブスクライブするたびに値を提供する)場合は、publishReplay().refCount()を使用する必要があります。

参考文献:

公開および共有演算子

2
meltedspark

上記の回答に加えて、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
         );
    }
}

Angularドキュメント。

0
StepUp