显示导航装载器微调器,延迟角度为2+

时间:2017-07-07 14:39:10

标签: javascript angular rxjs ngrx-store

我是角度2+和RxJS的新手,试图习惯RxJS。

我在路线转换时显示加载微调器,但只有当它需要超过一定量的时间时,拉特说160毫秒 我有一个load spinner作为一个单独的组件,订阅了ngrx store,所以我根据sore(showSpinner)中的值显示/隐藏load spinner

在我的app根组件中,我订阅路由器更改事件和调度操作(SHOW_SPINNER / HIDE_SPINNER)
所以问题是,有没有更简单的方法来实现它?

以下是我的代码部分

....

export const navigationStreamForSpinnerStatuses = {
  NAVIGATION_STARTED: 'NAVIGATION_STARTED',
  NAVIGATION_IN_PROGRESS: 'NAVIGATION_IN_PROGRESS',
  NAVIGATION_ENDED: 'NAVIGATION_ENDED'
};

...

private navigationStartStream;
private navigationStartStreamWithDelay;
private navigationFinishStream;

constructor(private store: Store<IAppState>, private router: Router) {
  this.navigationStartStream = router.events
    .filter(event => {
      return event instanceof NavigationStart;
    })
    .map(() => navigationStreamForSpinnerStatuses.NAVIGATION_STARTED);

  this.navigationStartStreamWithDelay = this.navigationStartStream
    .delay(160)
    .map(() => navigationStreamForSpinnerStatuses.NAVIGATION_IN_PROGRESS);

  this.navigationFinishStream = router.events
    .filter(event => {
      return event instanceof NavigationEnd || event instanceof NavigationCancel || event instanceof NavigationError;
    })
    .map(() => navigationStreamForSpinnerStatuses.NAVIGATION_ENDED);

  this.navigationStartStream
    .merge(this.navigationFinishStream)
    .merge(this.navigationStartStreamWithDelay)
    .pairwise()
    .subscribe([previousStatus, currentStatus] => {
      if (previousStatus !== navigationStreamForSpinnerStatuses.NAVIGATION_ENDED && currentStatus === navigationStreamForSpinnerStatuses.NAVIGATION_IN_PROGRESS) {
        this.store.dispatch({ type: StateLoaderSpinnerActionsTypes.SHOW_SPINNER });
      } else if (previousStatus === navigationStreamForSpinnerStatuses.NAVIGATION_IN_PROGRESS && currentStatus === navigationStreamForSpinnerStatuses.NAVIGATION_ENDED) {
        this.store.dispatch({ type: StateLoaderSpinnerActionsTypes.HIDE_SPINNER });
      }
    });
}

2 个答案:

答案 0 :(得分:1)

如果它在时间之前返回,则使用takeUntil运算符取消微调器计时器。此外,使用timer创建一个hot observable,以便在经过一段时间后触发操作。

  

takeUntil返回源可观察序列中的值,直到其他可观察序列或Promise产生值。

https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/takeuntil.md

  

timer返回一个可观察序列,该序列在dueTime过去之后产生一个值,然后在每个句点之后产生一个值。

https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/timer.md

您可以通过直接在每个流上处理调度来简化您的逻辑。

this.navigationEnd$ = router.events
  .filter(event => event instanceof NavigationEnd || event instanceof NavigationCancel || event instanceof NavigationError);

this.navigationStart$ = router.events
  .filter(event => event instanceof NavigationStart)
  .subscribe(_ => {
    Observable.timer(160)
      .takeUntil(this.navigationEnd$)
      .subscribe(_ => this.store.dispatch({ type: StateLoaderSpinnerActionsTypes.SHOW_SPINNER });
  });

this.navigationEnd$.subscribe(_ => this.store.dispatch({ type: StateLoaderSpinnerActionsTypes.HIDE_SPINNER });

所以我们所做的就是聆听导航的开始并开始timer 160毫秒。如果导航结束事件发生在计时器之前,则不会显示微调器(takeUntil)。否则,将调度存储操作并显示微调器。无论微调器是否显示,导航完成后我们都会调度hide微调器动作。

答案 1 :(得分:1)

我的方法基于Http请求,这些请求可能不适用于所有情况,因为某些应用程序将仅使用WebSockets或根本不使用外部请求。但对于大量应用程序,他们通过HttpClient(角度4.3+)获取所有数据。我写了一个HttpInterceptor就是这样做的。

import { HttpHandler, HttpInterceptor, HttpRequest, HttpResponse } from '@angular/common/http'
import { Inject, Injectable, RendererFactory2 } from '@angular/core'
import { Observable, timer } from 'rxjs'
import { filter, takeUntil, tap } from 'rxjs/operators'
import { DOCUMENT } from '@angular/common'

const reqIsSpinnable = (req: HttpRequest<any>) => {
  return req.url.includes('api/')
}

@Injectable()
export class HttpSpinnerInterceptor implements HttpInterceptor {
  constructor(@Inject(DOCUMENT) private doc: HTMLDocument, private rdf: 
    RendererFactory2) { }

  // tslint:disable-next-line:no-null-keyword
  readonly rdr = this.rdf.createRenderer(null, null)

  get spinnerElement() {
    return this.doc.querySelector('#core-spin')
  }

  startSpin() {
    this.rdr.setStyle(this.spinnerElement, 'display', 'block')
  }

  closeSpin() {
    this.rdr.setStyle(this.spinnerElement, 'display', 'none')
  }

  intercept(req: HttpRequest<any>, next: HttpHandler):
    Observable<any> {
    const responseTimer$ = next.handle(req).pipe(filter(e => e instanceof HttpResponse))
    timer(120).pipe(takeUntil(responseTimer$)).subscribe(() => this.startSpin())

    return next.handle(req).pipe(tap(evt => {
      if (reqIsSpinnable(req) && evt instanceof HttpResponse) {
        this.closeSpin()
      }
    }))

} }