I've been using Angular 2/4 for about a year now and I keep returning to this dilemma whether injecting Router into service can be considered as a bad practice?
This is more architectural question. I believe there's no exact answer for it, but I want to hear your opinion. So here are 2 examples.
component.ts
constructor(private router: Router) {}
someAction() {
// Some code here
this.router.navigate(['/grid']);
}
Here I think it's perfectly fine to use Router, because both Router and Component is UI layer.
auth.service.ts
and it's responsible for authentication. We want to be able to logout user out of the application and we have logout()
function to do that.auth.service.ts
constructor(private router: Router) {}
logout() {
// Cleanup token, storage, etc.
this.router.navigate(['/login']);
}
So thinking architecturally:
I was thinking about putting eventEmitter
on authService
and subscribe to it inside app.component.ts for instance, but still not sure if it's better than having it in the service.
I appreciate any comments for this case. Thanks a lot!
EDIT
Another example: UI is a calendar with tasks.
There's a service that handles all data-flow and provides data for the calendar. Calendar itself doesn't ask for the data, instead it subscribes to the data change from the service.
Now I need to route user to a different screen from this calendar. Imagine user clicks next week/month/year.
This data stored in the route URL so user can stay on the same day after page refresh, but calendar component doesn't know about the days/weeks/months.
They are incapsulated inside the service. So will you use router in service in this case?
@angular/routerlink. Implements the Angular Router service , which enables navigation from one view to the next as users perform application tasks.
simple answer is, no. It is not mandatory to have routes defined on every component you create.
A routed Angular application has one singleton instance of the Router service. When the browser's URL changes, that router looks for a corresponding Route from which it can determine the component to display.
TL;DR: Its always better to do it from the component, since you can see where are the moving parts, wether in a service it can be done, but it is hard to identify as you will always check first the component.
You can use Guards for that purpose and interceptors, i've added an error interceptor like the following, to route to logout when i get a 401:
import { Injectable } from '@angular/core';
import { HttpEvent, HttpErrorResponse, HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http';
import { Router } from '@angular/router';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/throw';
import 'rxjs/add/operator/catch';
@Injectable()
export class ErrorInterceptor implements HttpInterceptor {
constructor(private router: Router) {}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(req).catch(
(err: HttpErrorResponse) => {
if (this.router.url !== '/login' && err.status === 401) {
this.router.navigate(['/logout']);
}
return Observable.throw(err);
}
);
}
}
Provide it in your app.module or in my case i've created a core.module for all the singletons to keep clean my app.module
{
provide: HTTP_INTERCEPTORS,
useClass: ErrorInterceptor,
multi: true
}
With something like this you don't have to put routing in a service, you will get a 401 from your api when token is invalid.
You might have to work out a bit this code, tried to be as generic as possible.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With