Ответ 1
Текущий маршрутизатор Angular обеспечивает навигационные события. Вы можете подписаться на них и соответственно внести изменения в пользовательский интерфейс. Не забудьте подсчитать в других событиях, таких как NavigationCancel
и NavigationError
, чтобы остановить ваш счетчик в случае неудачных переходов маршрутизатора.
app.component.ts - ваш корневой компонент
...
import {
Router,
// import as RouterEvent to avoid confusion with the DOM Event
Event as RouterEvent,
NavigationStart,
NavigationEnd,
NavigationCancel,
NavigationError
} from '@angular/router'
@Component({})
export class AppComponent {
// Sets initial value to true to show loading spinner on first load
loading = true
constructor(private router: Router) {
router.events.subscribe((event: RouterEvent) => {
this.navigationInterceptor(event)
})
}
// Shows and hides the loading spinner during RouterEvent changes
navigationInterceptor(event: RouterEvent): void {
if (event instanceof NavigationStart) {
this.loading = true
}
if (event instanceof NavigationEnd) {
this.loading = false
}
// Set loading state to false in both of the below events to hide the spinner in case a request fails
if (event instanceof NavigationCancel) {
this.loading = false
}
if (event instanceof NavigationError) {
this.loading = false
}
}
}
app.component.html - ваш корневой вид
<div class="loading-overlay" *ngIf="loading">
<!-- show something fancy here, here with Angular 2 Material loading bar or circle -->
<md-progress-bar mode="indeterminate"></md-progress-bar>
</div>
Улучшенный отклик производительности. Если вы заботитесь о производительности, есть лучший способ, но немного более утомительно для реализации, но улучшение производительности будет стоить дополнительной работы. Вместо того чтобы использовать *ngIf
для условного отображения счетчика, мы могли бы использовать Angular NgZone
и Renderer
для включения/выключения счетчика, который будет обходить обнаружение изменений Angular, когда мы изменим состояние счетчика. Я нашел это, чтобы сделать анимацию более плавной по сравнению с использованием *ngIf
или async
.
Это похоже на мой предыдущий ответ с некоторыми настройками:
app.component.ts - ваш корневой компонент
...
import {
Router,
// import as RouterEvent to avoid confusion with the DOM Event
Event as RouterEvent,
NavigationStart,
NavigationEnd,
NavigationCancel,
NavigationError
} from '@angular/router'
import {NgZone, Renderer, ElementRef, ViewChild} from '@angular/core'
@Component({})
export class AppComponent {
// Instead of holding a boolean value for whether the spinner
// should show or not, we store a reference to the spinner element,
// see template snippet below this script
@ViewChild('spinnerElement')
spinnerElement: ElementRef
constructor(private router: Router,
private ngZone: NgZone,
private renderer: Renderer) {
router.events.subscribe((event: RouterEvent) => {
this._navigationInterceptor(event)
})
}
// Shows and hides the loading spinner during RouterEvent changes
private _navigationInterceptor(event: RouterEvent): void {
if (event instanceof NavigationStart) {
// We wanna run this function outside of Angular zone to
// bypass change detection
this.ngZone.runOutsideAngular(() => {
// For simplicity we are going to turn opacity on / off
// you could add/remove a class for more advanced styling
// and enter/leave animation of the spinner
this.renderer.setElementStyle(
this.spinnerElement.nativeElement,
'opacity',
'1'
)
})
}
if (event instanceof NavigationEnd) {
this._hideSpinner()
}
// Set loading state to false in both of the below events to
// hide the spinner in case a request fails
if (event instanceof NavigationCancel) {
this._hideSpinner()
}
if (event instanceof NavigationError) {
this._hideSpinner()
}
}
private _hideSpinner(): void {
// We wanna run this function outside of Angular zone to
// bypass change detection,
this.ngZone.runOutsideAngular(() => {
// For simplicity we are going to turn opacity on / off
// you could add/remove a class for more advanced styling
// and enter/leave animation of the spinner
this.renderer.setElementStyle(
this.spinnerElement.nativeElement,
'opacity',
'0'
)
})
}
}
app.component.html - ваш корневой вид
<div class="loading-overlay" #spinnerElement style="opacity: 0;">
<!-- md-spinner is short for <md-progress-circle mode="indeterminate"></md-progress-circle> -->
<md-spinner></md-spinner>
</div>