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
2 Answers
Reset to default 3My 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条)