2

私は ngx-progressbar を使用していますが、サービス、コンポーネント、またはリゾルバ内から開始された http リクエストで正常に動作します。

http リクエスト中に (サービスなどを介して) プログレス バーを手動でトリガーする必要がないことに注意してください。自動的にトリガーされます。

残念ながら、NGXS State 内から http リクエストを行うと、期待どおりに動作しません。

スタックブリッツ:

すべてのケースにボタンを作成しました:

  • 「依頼する(店舗への発送、ゾーンなし)」

これは機能せ、プログレス バーが表示されません (または、表示されてもハングし、100% まで完了しません)。

@Action(LoadUrl)
    load({ setState }: StateContext<string>) {
        return this.http.get<any>('https://jsonplaceholder.typicode.com/posts/1').pipe(tap(res => console.log(res)));
    }
  • 「リクエストを作成(コンポーネント)」

これは期待どおりに機能し、進行状況バーが表示されます

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
// ...
  makeRequestComponent() {
    this.http.get<any>('https://jsonplaceholder.typicode.com/posts/1').pipe(tap(res => console.log(res))).subscribe();
  }
}
  • 「依頼する(店舗へ発送、ゾーンあり)」

ngx-progressbar の開発者に尋ねたところ、彼は実際に問題を発見し、http 呼び出しをゾーン内にラップすることで解決しました。これは機能し、進行状況バーが表示されます:

@State<string>({
  name: 'testState',
  defaults: ''
})
export class TestUrlState {

  constructor(private http: HttpClient, private zone: NgZone) { }

  @Action(LoadUrl)
  load({ setState }: StateContext<string>) {

    this.zone.run(() => {

      this.http.get<any>('https://reqres.in/api/users?delay=2').pipe(
        tap(res => console.log(res))
      ).subscribe();

    });
  }
}

すべての http リクエストで進行状況バーを表示したいので、これを解決する正しいアプローチは何でしょうか?

zone 内ですべての http リクエストを実行するように NGXS に指示する方法はありますか?

4

1 に答える 1

7

現在、ngxs は、パフォーマンス上の理由から、すべてのアクションを角度ゾーンの外で実行します。アクション ストリームまたは store.select をサブスクライブする場合、サブスクリプションはデフォルトで angular ゾーンで実行されます。State オブジェクト内のアクション ハンドラーは、Angular ゾーンの外で実行されます。これは ngxs のデフォルトの動作です。この「ゾーン外で実行」機能をオフにすることは有効な要件だと思います。この機能を要求する github の問題をログに記録してください。

当面の問題に戻ると、http 呼び出しを角度ゾーンに強制的に戻すために追加できる Http Interceptor を作成しました (これは、変更検出をトリガーするためにのみ必要です)。私はあなたのstackblitzをフォークし、ここに追加しました: https://stackblitz.com/edit/ngx-progressbar-inzone?file=src%2Fapp%2FNgZoneHttpInterceptor.ts

インターセプターのコードは次のとおりです。

import { Injectable, Optional, Inject, NgZone, NgModule, ModuleWithProviders } from '@angular/core';
import { HTTP_INTERCEPTORS, HttpInterceptor, HttpEvent, HttpHandler, HttpRequest } from '@angular/common/http';
import { Observable, Observer } from 'rxjs';

@NgModule({
})
export class NgZoneHttpInterceptorModule {
  static forRoot(): ModuleWithProviders {
    return {
      ngModule: NgZoneHttpInterceptorModule,
      providers: [
        { provide: HTTP_INTERCEPTORS, useClass: NgZoneHttpInterceptor, multi: true }
      ]
    };
  }
}

@Injectable()
export class NgZoneHttpInterceptor implements HttpInterceptor {

  constructor(private _ngZone: NgZone) { }

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {    
    return this._ngZone.run(() => {
      return next.handle(req).pipe(enterZone(this._ngZone));
    });
  }
}


function enterZone<T>(zone: NgZone) {
  return (source: Observable<T>) => {
    return new Observable((sink: Observer<T>) => {
      return source.subscribe({
        next(x) { zone.run(() => sink.next(x)); },
        error(e) { zone.run(() => sink.error(e)); },
        complete() { zone.run(() => sink.complete()); }
      });
    });
  };
}

お役に立てれば!

于 2018-07-09T11:25:53.717 に答える