web-dev-qa-db-ja.com

Angular 6サブスクライブ内の変数を変更した後、ビューが更新されない

サブスクライブ内で変数が変更されたときにビューが更新されないのはなぜですか?

私はこのコードを持っています:

example.component.ts

_testVariable: string;

ngOnInit() {
    this.testVariable = 'foo';

    this.someService.someObservable.subscribe(
        () => console.log('success'),
        (error) => console.log('error', error),
        () => {
            this.testVariable += '-bar';

            console.log('completed', this.testVariable);
            // prints: foo-Hello-bar
        }
    );

    this.testVariable += '-Hello';
}
_

example.component.html

_{{testVariable}}
_

ただし、ビューにはfoo-Helloと表示されます。

なぜ表示されない:foo-Hello-bar

サブスクライブ内でChangeDetectorRef.detectChanges()を呼び出すと、適切な値が表示されますが、なぜこれを行う必要があるのですか?

私はすべてのサブスクライブからこのメソッドを呼び出すべきではありません。正しい方法はありますか?

Angular/rxjs 5から6への更新で何か見落としましたか?

現在、私はAngularバージョン6.0.2およびrxjs 6.0.0です。同じコードは、Angular 5.2およびrxjs 5.5.10 detectChangesを呼び出します。

15
Danny Mencos

私が知る限り、Angularは「Angularゾーン」のデータを変更した場合にのみビューを更新します。この例の非同期呼び出しはこれに適格ではありません。 Angularゾーンに配置するか、rxjsを使用するか、コードの一部を新しいコンポーネントに抽出して、この問題を解決できます。すべてを説明します。

編集:すべてのソリューションがもう機能していないようです。ほとんどのユーザーにとって、最初のソリューション「Angular Zone」が仕事をします。

1 Angular Zone

このサービスの最も一般的な用途は、AngularがUIの更新やエラー処理を処理する必要のない1つ以上の非同期タスクで構成される作業を開始するときのパフォーマンスを最適化することです。そのようなタスクはrunOutsideAngularを介して開始でき、必要に応じて、これらのタスクはrunを介してAngularゾーンに再入できます。 https://angular.io/api/core/NgZone

重要な部分は「実行」機能です。 NgZoneを挿入し、NgZoneオブジェクトのrunコールバックに値の更新を入れることができます。

_constructor(private ngZone: NgZone ) { }
testVariable: string;

ngOnInit() {
   this.testVariable = 'foo';

   this.someService.someObservable.subscribe(
      () => console.log('success'),
      (error) => console.log('error', error),
      () => {
      this.ngZone.run( () => {
         this.testVariable += '-bar';
      });
      }
   );
}
_

this 回答によれば、アプリケーション全体が変更を検出するのに対し、ChangeDetectorRef.detectChangesアプローチはコンポーネントとその子孫の変更のみを検出します。

2 RxJS

別の方法は、rxjsを使用してビューを更新することです。最初に ReplaySubject をサブスクライブすると、最新の値が提供されます。 BehaviorSubject は基本的に同じですが、デフォルト値を定義できます(この例では意味がありますが、常に正しい選択である必要はありません)。この最初の放出後は、基本的に通常のリプレイサブジェクトです。

_this.testVariable = 'foo';
testEmitter$ = new BehaviorSubject<string>(this.testVariable);


ngOnInit() {

   this.someService.someObservable.subscribe(
      () => console.log('success'),
      (error) => console.log('error', error),
      () => {
         this.testVariable += '-bar';
         this.testEmitter.next(this.testVariable);
      }
   );
}
_

ビューでは、 async パイプを使用してサブジェクトをサブスクライブできます。

_{{testEmitter$ | async}}
_

3新しいコンポーネントへのコードの抽出

文字列を別のコンポーネントに送信すると、それも更新されます。新しいコンポーネントで@Input()セレクターを使用する必要があります。

したがって、新しいコンポーネントには次のようなコードがあります。

_@Input() testVariable = '';
_

そして、testVariableは、以前のように中かっこでHTMLに割り当てられます。

親HTMLビューで、親要素の変数を子要素に渡すことができます。

_<app-child [testVariable]="testVariable"></app-child>
_

このようにして、Angularゾーンにいます。

4個人的な好み

私の個人的な好みは、rxjsまたはコンポーネントの方法を使用することです。 NGZoneでdetectChangesを使用することは、私にとってよりハッキングを感じます。

24
besserwisser