web-dev-qa-db-ja.com

非同期パイプを使用する場合、単一のngIfで2つ以上の(ブール)オブザーバブルを結合します

オブザーバブルがなければ、HTMLテンプレートに次の行を書くことができます。

<div *ngIf="(myVarA || myVarB) && myVarC !== x"></div>

すべてのmyVar変数が実際に観測可能になった場合、この行をどのように変換しますか?

<div *ngIf="((myVarA | async) || (myVarB | async)) && (myVarC | async) !== x">

動作しません。

別の質問では( 2つの非同期サブスクリプションを1つに入れるAngular * ngIfステートメント )2つ以上のオブザーバブルを1つのngIfに結合する可能性

<div *ngIf="{ a: myVarA | async, b: myVarB | async } as result"></div>

ただし、ngIfの評価に使用される式でブール演算子(またはそのことについては演算子)を使用する可能性はありません。

どうすればこの問題に取り組むことができますか?私のすべてのObservableはその下でBehaviorSubjectを使用していることに注意してください。基本的に私が欲しいのは、テンプレート内でcombineLatest演算子を使用することだと思います。

ボーナス:式全体が後でテンプレートで使用するためにtrueと評価された場合(myVarA | async as varAのように)、myVarAの単一の値を抽出する方法はありますか?

8
Sebastian

combineLatestの使用はどうですか?

例えば:

import { combineLatest } from 'rxjs/observable/combineLatest';
import { Observable } from 'rxjs/Observable';    

@Component({...})
export class FooComponent {
  obs1$: Observable<bolean>;
  obs2$: Observable<bolean>;
  obs3$: Observable<bolean>;

  constructor(){
    // set observables
  }

  get combined$(){
    return combineLatest(
      this.obs1$,
      this.obs2$
      this.obs3$,
      (one,two,three)=>(one || two) && three);
  }
}

// template
<div *ngIf="combined$ | async">

ガイダンスについては、次のフィドルを確認してください。

https://jsfiddle.net/uehasmb6/11/

combineLatest演算子の詳細 ここ

更新:しかし、それでもすべてのロジックをテンプレート内に保持したい場合は、次のようなことを試すことができます。

<div *ngIf="((myVarA | async) || (myVarB | async)) && ((myVarC | async) !== x)">

しかし、私はこれに対してあなたに忠告します。 HTMLテンプレートを可能な限りクリーンに保つことは グッドプラクティス です。

12
Jota.Toledo

一般的なANDのヘルパー監視可能な「ジェネレーター」を作成します/ OR/ALLロジック

最初はpaymentSelected == false && mode == 'addPayment'を配置するのは簡単ですが、新しい条件を追加する必要がある場合は、いくつかの場所でUIを更新する必要があります。

showPaymentPanel$と呼ばれるオブザーバブルを公開することをお勧めします。そうすると、.tsとテンプレートファイルの両方で、それが何のためにあるのかが明確になります:*ngIf="showPaymentPanel$ | async"。これにより、テストも簡単になります。

しかし、私は次のような多くのコードで終わっていました:

showTokenizedPaymentMethods$ = combineLatest(this.hasTokenizedPaymentMethods$, 
                                             this.showAvailablePaymentMethods$).
                              pipe(map(([ hasTokenizedMethods, showAvailableMethods ]) => 
                              showAvailableMethods && hasTokenizedMethods));

そして、それは混乱です!複数の非同期パイプよりもさらに悪い可能性があります!

そこで、ヘルパー関数を作成して新しいオブザーバブルを生成 :(グローバルにどこか)

export const allTrue = (...observables: Array<ObservableInput<boolean>> ) => combineLatest(observables).pipe(map(values => values.every(v => v == true) ), distinctUntilChanged());
export const allFalse = (...observables: Array<ObservableInput<boolean>> ) => combineLatest(observables).pipe(map(values => values.every(v => v == false) ), distinctUntilChanged());
export const anyTrue = (...observables: Array<ObservableInput<boolean>> ) => combineLatest(observables).pipe(map(values => values.find(v => v == true) != undefined ), distinctUntilChanged());
export const anyFalse = (...observables: Array<ObservableInput<boolean>> ) => combineLatest(observables).pipe(map(values => values.find(v => v == false) != undefined), distinctUntilChanged());

注:これらはパイプで使用される演算子ではありません。

Tsファイルで、次のようにオブザーバブル(UI固有の名前)を作成します。

public showPaymentPanel$ = allTrue(this.hasTokenizedPaymentMethods$, this.showAvailableMethods$);

通常、既存のオブザーバブルが存在する場合でも、UIオブザーバブルを作成します。

public showAccountDetails$ = this.isLoggedIn$;     // this condition is always subject to change

それらを作成することもできます。

public showSomethingElse$ = allTrue(this.showPaymentPanel$, this.whateverItIs$);

次のようにグループ化されたUIにそれらを公開することがあります。

public ui = { this.showPaymentPanel$, this.showSomethingElse$ );

次に使用法:

`*ngIf="ui.showPaymentPanel$ | async"` 

uiのみを公開する必要があるため、テンプレートでは、何を許可するかが非常に明確になります)

できるだけ1本のパイプに制限してください!

2
Simon_Weaver

Jota.Toledoの答えはまったく問題ありませんが、ngIfの後に式の一部にアクセスする方法についての私の「ボーナス」の質問をフォローアップしたいと思います。

基本的に私がしているのは、Jota.Toledoが説明したように、クラス内の2つ以上のオブザーバブルを結合することだけです。ただし、1つのオブザーバブルはブール値ではありませんが、ngIfの後に使用可能にする必要があるアイテムが含まれています。

次に、次のことを行うのは簡単です。

newObs = combineLatest(
  this.itemObs,
  this.boolObs1
  this.boolObs3,
  (item, bool1, bool2) => ((item || bool1) && bool2) ? item : null
);

ngIf内のnull以外のオブジェクトの真実性に依存できるためです。 ngIfは次のようになります

<div *ngIf="newObs | async as item"></div>

もちろん、ngIfの後に複数のアイテムが必要な場合、これは機能しません。この場合、ネストされたdivセクションで2つのngIfを使用する必要があります。

0
Sebastian