web-dev-qa-db-ja.com

Angular 2-(observableData | async)がまだ解決されていない場合、読み込み情報を表示します

タイトルのとおり、rxjs Observablesの力を活用したいと思います。

私が今していること:

// dataview.html
<div *ngIf="isLoading">Loading data...div>
<ul *ngIf="!isLoading">
    <li *ngFor="let d of data">{{ d.value }}</li>
</ul>


// dataview.ts

data: any[] = [];
isLoading: boolean = false;

getData() {

this.isLoading = true;
this._api.getData().subscribe(
        data => {
            this.data = data;
            this.isLoading = false;
        },
        error => {
            this.error = error;
            this.isLoading = false;
        });
}

私がやりたいこと:

1。テンプレートでasyncパイプを使用

  1. dataを監視可能な配列にする

  2. ユーザーの読み込み情報を引き続き表示します

私はクリーンなコードの大ファンなので、rxjsとAngular 2?

18

これが私のやり方です。また、$とで変数名をストリームであることを思い出させます。

// dataview.html
<div *ngIf="isLoading$ | async">Loading data...</div>
<ul *ngIf="!(isLoading$ | async)">
    <li *ngFor="let d of data">{{ d.value }}</li>
</ul>


// dataview.ts

data: any[] = [];
isLoading$: BehaviorSubject<boolean> = new BehaviorSubject(false);

getData() {

this.isLoading$.next(true);

this._api.getData().subscribe(
        data => {
            this.data = data;
        },
        error => {
            this.error = error;
        },
        complete => {
            this.isLoading$.next(false);
        });
}
15
Kliment

私は次のことを思いつきました:

export enum ObsStatus {
  SUCCESS = 'Success',
  ERROR = 'Error',
  LOADING = 'Loading',
}

export interface WrapObsWithStatus<T> {
  status: ObsStatus;
  value: T;
  error: Error;
}

export function wrapObsWithStatus<T>(obs: Observable<T>): Observable<WrapObsWithStatus<T>> {
  return obs.pipe(
    map(x => ({ status: ObsStatus.SUCCESS, value: x, error: null })),
    startWith({ status: ObsStatus.LOADING, value: null, error: null }),
    catchError((err: Error) => {
      return of({ status: ObsStatus.ERROR, value: null, error: err });
    })
  );
}

そして、あなたのコンポーネントで:

[〜#〜] ts [〜#〜]

public ObsStatus: typeof ObsStatus = ObsStatus;

public obs$: Observable<WrapObsWithStatus<YOUR_TYPE_HERE>> = wrapObsWithStatus(this.myService.getObs());

[〜#〜] html [〜#〜]

<div *ngIf="obs$ | async as obs" [ngSwitch]="obs.status">
  <div *ngSwitchCase="ObsStatus.SUCCESS">
    Success! {{ obs.value }}
  </div>

  <div *ngSwitchCase="ObsStatus.ERROR">
    Error! {{ obs.error }}
  </div>

  <div *ngSwitchCase="ObsStatus.LOADING">
    Loading!
  </div>
</div>
12
maxime1992

私は非同期パイプを使用してそれを行いました。しかし、このアプローチでも、エラーを処理するために手動でキャッチする必要がありました。詳細は here を参照してください。

app.component.html

<div class="wrapper">
    <div class="form-group" *ngIf="pickupLocations$ | async as pickupLocations; else loading">    
        <ul class="dropdown-menu" *ngIf="pickupLocations.length">
            <li *ngFor="let location of pickupLocations">
                <strong>{{location.Key}}</strong>
            </li>
        </ul>
        <span *ngIf="!pickupLocations.length">There are no locations to display</span>
    </div>

    <ng-template #loading>
        <i class="fa fa-circle-o-notch fa-spin fa-3x fa-fw"></i>
        <span class="sr-only">Loading...</span>
    </ng-template>
</div>

app.component.ts

this.pickupLocations$ = this.apiService.getPickupLocations(storeId);
4
trungk18

これが、検索結果を表示するための現在の最善の試みです。

何らかの方法でObservableを拡張してisLoadingプロパティを含めるか、またはタプルを返すことを考えましたが、結局のところ、オブザーバブルのペアを返すヘルパー関数(サービス内)が最もクリーンな方法のようです。あなたのように私はいくつかの「魔法」を探していましたが、これ以上の方法はありません。


したがって、この例では、検索条件を含むFormGroup(標準の反応形式)を持っています。

_{ email: string, name: string } 
_

フォームのvalueChangesオブザーバブルが変更されたときに、検索条件を取得します。

コンポーネントコンストラクタ

注:検索は、基準が変更されるまで実際には実行されません。そのため、これはコンストラクター内にあります。

_// get debounced data from search UI
var customerSearchCriteria = this.searchForm.valueChanges.debounceTime(1000);

// create a pair of observables using a service (data + loading state)
this.customers = this.customersService.searchCustomers(customerSearchCriteria);

// this.customers.data => an observable containing the search results array
// this.customers.isLoading => an observable for whether the search is running or not
_

検索サービス

_public searchCustomers(searchCriteria: Observable<CustomersSearch>):
                       { data: Observable<CustomerSearchResult[]>, 
                         isLoading: Observable<boolean> }
{
    // Observable to track loading state
    var isLoading$ = new BehaviorSubject(false);

    // Every time the search criteria changes run the search
    var results$ = searchCriteria
                    .distinctUntilChanged()
                    .switchMap(criteria =>
                    {
                        // update isLoading = true
                        isLoading$.next(true);

                        // run search
                        var search$ = this.client.search(new CustomersSearch(criteria)).shareReplay();

                        // when search complete set isLoading = false
                        search$.subscribe({ complete: () => isLoading$.next(false) });

                        return search$;
                    })
                    .shareReplay();

    return { data: results$, isLoading: isLoading$ };
}
_

これを汎用的にする方法を見つける必要がありますが、それは非常に簡単です。また、isLoadingを気にしない場合は、searchCustomers(criteria).dataを実行するだけで、データにアクセスできます。

編集:2回検索が実行されないように、ShareReplyを追加する必要があります。

コンポーネントHTML

通常どおりオブザーバブルとして_customers.data_と_customers.isLoading_の両方を使用します。 customersは、2つの監視可能なプロパティを持つオブジェクトにすぎないことに注意してください。

_<div *ngIf="customers.isLoading | async">Loading data...</div>
<ul *ngIf="!(customers.isLoading | async)">
    <li *ngFor="let d of customers.data | async">{{ d.email }}</li>
</ul>
_

また、両方のオブザーバブルに対してasyncパイプが必要であることにも注意してください。 isLoadingは少し不格好に見えますが、とにかくプロパティよりもオブザーバブルを使用する方が速いと思います。これには改良点があるかもしれませんが、私はまだ専門家ではありませんが、改善を歓迎します。

1
Simon_Weaver

多分これはあなたのために働くかもしれません。これは、オブザーバブルが存在し、非同期データがある場合のデータを示しています。それ以外の場合は、ロードテンプレートを示します。

<ul *ngIf="data$ && (data$ | async);else loading">
    <li *ngFor="let d of data$ | async">{{ d.value }}</li>
</ul>
<ng-template #loading>Loading...</ng-template>

0
Wouter Klene

メンバープロパティなしでこれを行う1つの方法は、テンプレートで非同期の監視可能な結果を​​評価することです:!(yourAsyncData$ | async)または!(yourAsyncData$ | async)?.length

例:<p-dataView #dv [value]="bikes$ | async" [loading]="!(bikes$ | async)"> ... </p-dataview>

0
Alex Fuentes