web-dev-qa-db-ja.com

Angular 7.1でグローバルローダーを実装する方法

私はこのように実装されているグローバルローダーを持っています:

CoreModule:

router.events.pipe(
  filter(x => x instanceof NavigationStart)
).subscribe(() => loaderService.show());

router.events.pipe(
  filter(x => x instanceof NavigationEnd || x instanceof NavigationCancel || x instanceof NavigationError)
).subscribe(() => loaderService.hide());

LoaderService:

@Injectable({
    providedIn: 'root'
})
export class LoaderService {

    overlayRef: OverlayRef;
    componentFactory: ComponentFactory<LoaderComponent>;
    componentPortal: ComponentPortal<LoaderComponent>;
    componentRef: ComponentRef<LoaderComponent>;

    constructor(
        private overlay: Overlay,
        private componentFactoryResolver: ComponentFactoryResolver
    ) {
        this.overlayRef = this.overlay.create(
            {
                hasBackdrop: true,
                positionStrategy: this.overlay.position().global().centerHorizontally().centerVertically()
            }
        );

        this.componentFactory = this.componentFactoryResolver.resolveComponentFactory(LoaderComponent);

        this.componentPortal = new ComponentPortal(this.componentFactory.componentType);
    }

    show(message?: string) {
        this.componentRef = this.overlayRef.attach<LoaderComponent>(this.componentPortal);
        this.componentRef.instance.message = message;
    }

    hide() {
        this.overlayRef.detach();
    }
}

Angular 7.0.2で実行した場合、(私が欲しかった)動作は次のとおりです:

  • ルートに添付されたデータを解決している間、およびレイジーモジュールをロードしている間にローダーを表示する
  • リゾルバーなしでルートにナビゲートするときにローダーを表示しない

Angular 7.2に更新しました。現在の動作は次のとおりです。

  • ルートに添付されたデータを解決している間、およびレイジーモジュールをロードしている間にローダーを表示する
  • リゾルバーなしでルートにナビゲートするときに、LoaderComponentなしでオーバーレイを表示します

NavigationStartおよびNavigationEndイベントにいくつかのログを追加しましたが、NavigationEndNavigationStartの直後(通常)にトリガーされ、オーバーレイが表示されないことがわかりました。約0.5秒後。

私はCHANGELOG.mdを読みましたが、この問題を説明するものは何も見つかりませんでした。どんなアイデアでも大歓迎です。

編集:

さらに調査した後、package.jsonを次のように設定して、以前の動作を復元しました。

"@angular/cdk": "~7.0.0",
"@angular/material": "~7.0.0",

これの代わりに:

"@angular/cdk": "~7.2.0",
"@angular/material": "~7.2.0",

バージョン7.1.0でリリースされたコミットの誤りを確認し、関連する GitHubの問題 に問題を投稿しました。 Overlayのフェードアウトアニメーションを修正します。

望ましい動作を実現するためのv7.1 +準拠の方法は何ですか?私によると、最善の方法は、必要な場合にのみローダーを表示することですが、NavigationStartは必要な情報を保持していません。デバウンス動作が発生するのを避けたいのですが。

16
Guerric P

これは、delayがUXに関して優れたソリューションであることを認識した後の結果です。これにより、ローダーは、ローダーを表示する価値があるときにのみローダーが表示できるようになりました。

counter = 0;

router.events.pipe(
  filter(x => x instanceof NavigationStart),
  delay(200),
).subscribe(() => {
  /*
  If this condition is true, then the event corresponding to the end of this NavigationStart
  has not passed yet so we show the loader
  */
  if (this.counter === 0) {
    loaderService.show();
  }
  this.counter++;
});

router.events.pipe(
  filter(x => x instanceof NavigationEnd || x instanceof NavigationCancel || x instanceof NavigationError)
).subscribe(() => {
  this.counter--;
  loaderService.hide();
});
1
Guerric P

例外リストを使用してシステムにローダーを実装する方法:

export class LoaderInterceptor implements HttpInterceptor {
  requestCount = 0;

  constructor(private loaderService: LoaderService) {
  }

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

    if (!(REQUEST_LOADER_EXCEPTIONS.find(r => request.url.includes(r)))) {
      this.loaderService.setLoading(true);
      this.requestCount++;
    }

    return next.handle(request).pipe(
      tap(res => {
        if (res instanceof HttpResponse) {
          if (!(REQUEST_LOADER_EXCEPTIONS.find(r => request.url.includes(r)))) {
            this.requestCount--;
          }
          if (this.requestCount <= 0) {
            this.loaderService.setLoading(false);
          }
        }
      }),
      catchError(err => {
        this.loaderService.setLoading(false);
        this.requestCount = 0;
        throw err;
      })
    );
  }
}

ローダーサービスは次のとおりです(300ミリ秒の遅延により、応答が速い場合にローダーが画面上で点滅するのを防ぎます)。

export class LoaderService {
  loadingRequest = new BehaviorSubject(false);
  private timeout: any;

  setLoading(val: boolean): void {
    if (!val) {
      this.timeout = setTimeout(() => {
        this.loadingRequest.next(val);
      }, 300);
    } else {
      clearTimeout(this.timeout);
      this.loadingRequest.next(val);
    }
  }
}
0
hardfi