我是角度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 });
}
});
}
答案 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()
}
}))
} }