javascript - Show navigation loader spinner with delay in angular 2+ - Stack Overflow

I'm new to angular 2+ and RxJS, trying to get used to RxJS.I show load spinner on route transition

I'm new to angular 2+ and RxJS, trying to get used to RxJS.

I show load spinner on route transitions, but only if it takes more then certain amount time, lats say 160ms
I have a load spinner as a separate ponent, with subscription to ngrx store, so I show/hide load spinner based on value in the sore (showSpinner)

In my app root ponent, I subscribe to router change events, and dispatch actions (SHOW_SPINNER/HIDE_SPINNER)
So the question is, Is there a simpler way to achieve it?

Here are parts of my code

....

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 });
      }
    });
}

I'm new to angular 2+ and RxJS, trying to get used to RxJS.

I show load spinner on route transitions, but only if it takes more then certain amount time, lats say 160ms
I have a load spinner as a separate ponent, with subscription to ngrx store, so I show/hide load spinner based on value in the sore (showSpinner)

In my app root ponent, I subscribe to router change events, and dispatch actions (SHOW_SPINNER/HIDE_SPINNER)
So the question is, Is there a simpler way to achieve it?

Here are parts of my code

....

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 });
      }
    });
}
Share Improve this question edited Jul 7, 2017 at 20:26 Mykhailo I asked Jul 7, 2017 at 14:39 Mykhailo IMykhailo I 431 silver badge5 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 3

My approach is based on Http requests, which may not work in every case since some apps will only use WebSockets or no external requests at all. But for a large set of apps, they are getting all their data via HttpClient (angular 4.3+). I wrote an HttpInterceptor that does just this.

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

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()
      }
    }))

} }

Utilize the takeUntil operator to cancel your spinner timer if it returns before the time. Additionally, create a hot observable using timer to trigger an action after a certain time has passed.

takeUntil Returns the values from the source observable sequence until the other observable sequence or Promise produces a value.

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

timer Returns an observable sequence that produces a value after dueTime has elapsed and then after each period.

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

You can simplify your logic here by handling the dispatch directly on each stream.

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 });

So what we've done is listen to the start of the navigation and start a timer for 160ms. If the navigation end event happens before the timer, no spinner will show (takeUntil). Otherwise, the store action will be dispatched and the spinner will show. Regardless of whether or not the spinner is showing, after the navigation finishes we dispatch the hide spinner action.

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745004460a4605693.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信