Reputation: 13377
So, I have this Spinner component declared, which is just a simple preloader CSS graphic. It sits on my App component and checks to see if we are loading any htpp data. If we are, it shows the spinner. If we are not, then it hides the spinner:
<div class="spinner-container" *ngIf="loading">
<div class="spinner">
<div class="bounce1"></div>
<div class="bounce2"></div>
<div></div>
</div>
</div>
I now have a situation where we don't want to show the spinner at all when on a certain component. I did try to do this using urls:
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Router, NavigationEnd, RoutesRecognized } from '@angular/router';
import { Subscription } from 'rxjs';
import { SpinnerService } from '../services/spinner.service';
@Component({
selector: 'pyb-spinner',
templateUrl: './spinner.component.html',
styleUrls: ['./spinner.component.scss']
})
export class SpinnerComponent implements OnInit, OnDestroy {
loading: boolean;
private widgetUrls: any[] = ['scenarios', 'questions', 'answers', 'results']
private url: string
private routeSubscription: Subscription
private spinnerSubscription: Subscription
constructor(
private route: Router,
private spinnerService: SpinnerService
) { }
ngOnInit() {
this.routeSubscription = this.route.events.subscribe(event => this.getCurrentUrl(event));
this.spinnerSubscription = this.spinnerService.onLoadingChanged.subscribe(loading => this.showSpinner(loading));
}
ngOnDestroy() {
if (this.routeSubscription) this.routeSubscription.unsubscribe();
if (this.spinnerSubscription) this.spinnerSubscription.unsubscribe();
}
private showSpinner(loading: boolean) {
if (this.url) {
let urlSegments = this.url.split('/');
if (urlSegments.length > 1) {
let lastSegment = urlSegments[urlSegments.length - 1];
let index = this.widgetUrls.indexOf(lastSegment);
if (index > -1) {
this.loading = false;
return;
}
}
}
this.loading = loading;
}
private getCurrentUrl(event: any) {
if (event instanceof RoutesRecognized) {
this.url = event.url;
}
}
}
But in my case, this won't work because my component has it's routes like this:
const widgetRoutes: Routes = [
{ path: ':category', redirectTo: ':category/scenarios', pathMatch: 'full', data: { state: 'widget' } },
{ path: ':category/:path', component: WidgetComponent, data: { state: 'widget' } }
];
So you can go to /cameras
for example and it will show my preloader, but I don't want it to.
I could put an exemption in my SpinnerComponent for each and every category, but that seems crazy.
What I would like to do is check the name of the component that is resolved when a route changes and then hide the preloader if it matches my component. Is that possible?
Upvotes: 1
Views: 205
Reputation: 13377
I am going to expand on Eliseo's answer. He was right in what he was saying, but it was only a partial answer. The actual answer was to change my ngOnInit to this:
ngOnInit() {
this.spinnerSubscription = this.router.events
.pipe(
filter(event => event instanceof NavigationEnd),
map(() => this.activatedRoute),
map(route => route.firstChild),
switchMap(route => route.data),
switchMap(data => {
if (!data.disableSpinner)
return this.spinnerService.onLoadingChanged
else
return of(false);
})
).subscribe(loading => this.loading = loading);
}
Once this was done, I could use the data in my routes like this:
const routes: Routes = [
{ path: '', redirectTo: '/', pathMatch: 'full' }, // Put first so it redirects :)
{ path: '', component: HomeComponent, data: { state: 'home' } },
// Lazy load
{ path: '', loadChildren: './widget/widget.module#WidgetModule', data: { state: 'widget', disableSpinner: true } }, // Put this last as it has empty path
// 404
{ path: '**', component: HomeComponent }, // 404?
]
One thing to note here; if you are using lazy loading, you must make sure the data object is part of the initial declaration.
I did have my data
object declared in my widget routing module but it was being ignored.
I hope this helps someone else.
PS: To make sure this can actually help someone else; here is my entire component:
import { Component, OnInit, OnDestroy } from '@angular/core';
import { ActivatedRoute, Router, NavigationEnd } from '@angular/router';
import { Subscription, of } from 'rxjs';
import { SpinnerService } from '../services/spinner.service';
import { switchMap, filter, map } from 'rxjs/operators';
@Component({
selector: 'pyb-spinner',
templateUrl: './spinner.component.html',
styleUrls: ['./spinner.component.scss']
})
export class SpinnerComponent implements OnInit, OnDestroy {
loading: boolean;
private spinnerSubscription: Subscription
constructor(
private router: Router,
private activatedRoute: ActivatedRoute,
private spinnerService: SpinnerService
) { }
ngOnInit() {
this.spinnerSubscription = this.router.events
.pipe(
filter(event => event instanceof NavigationEnd),
map(() => this.activatedRoute),
map(route => route.firstChild),
switchMap(route => route.data),
switchMap(data => {
if (!data.disableSpinner)
return this.spinnerService.onLoadingChanged
else
return of(false);
})
).subscribe(loading => this.loading = loading);
}
ngOnDestroy() {
if (this.spinnerSubscription) this.spinnerSubscription.unsubscribe();
}
}
Upvotes: 0
Reputation: 57961
You can use the property "data" to add a property "noSpinner" to all the routes you don't need spinner
{ path: ':category/:path',
component: WidgetComponent,
data: { state: 'widget',noSpinner:true }
}
If you subscribe to activatedRoute.data, you get the value, If !res.noSpinner, subscribe to onLoadingChange
this.activatedRoute.data.subscribe(res=>{
console.log(res)
if (!res.noSpinner)
this.spinnerSubscription = this.spinnerService.onLoadingChanged
.subscribe(loading => this.showSpinner(loading));
})
Well, really you can use switchMap to get only one subscription
this.spinnerSubscription = this.activatedRoute.data.pipe(
switchMap(res=>{
console.log(res)
if (!res.noSpinner)
return this.spinnerService.onLoadingChanged
else
return of(false);
}))
.subscribe(loading => this.showSpinner(loading));
Upvotes: 2