I have an Angular 6 app that uses provider information to determine the functionality (channels) of the app. I am working with an object that I receive from a server after a user logs on and that provides me with boolean values for the respective channels.
providerInfos = {
channels: {
a: true,
b: true,
c: true
}
}
In the index.html I have
<base href="/home/">
and in my app-routing.module I have
const routes: Routes = [
{ path: '', redirectTo: 'a', pathMatch: 'full' }, // Set /a as "start page".
{ path: 'a', component: AComponent, runGuardsAndResolvers: 'always' },
{ path: 'a/:id', component: ADetailsComponent },
{ path: 'b', component: BComponent },
{ path: 'c', component: CComponent }
];
So when the app is initialized, A is loaded (somewhere.io/home/a). This was good so far, but due to a further development of the app it turned out that A can't be given as a channel either. This has created a problem for me which I have not been able to solve yet. The more I think about it and look at my code, the more I think that my previous approach is basically wrong.
When initializing the app I want to be able to display B or C as the first or only view (somewhere.io/home/whatever), if A is not part of the app's functionality. In principle I want to provide the app with routes according to the functionality defined for a user via his provider information, at least with
path: '', redirectTo: 'whatever', pathMatch: 'full'
if only one functionality is given.
I can customize the functionality via the provider information, so that the menu in the sidebar is customized and I make the components that are not part of the functionality inaccessible via *ngIf, if someone gets the idea to enter paths in the URL bar. But I can't manage to adjust the routes accordingly.
How would you deal with this problem. Is there best practice for this?
You can use a route guard to achieve this functionality. Not only do route guards prevent a user from accessing a route, they also allow you to tell the router where to navigate to by returning a UrlTree
.
Given the following routes:
const routes = [
{ path: '', redirectTo: 'a', pathMatch: 'full' },
{ path: 'a', component: ComponentA, canActivate: [AuthGuard] },
{ path: 'b', component: ComponentB }
];
I can set up my AuthGuard
as follows:
@Injectable({
providedIn: 'root'
})
export class AuthGuard implements CanActivate {
constructor(private router: Router, private userService: UserService) {
}
canActivate() : boolean | UrlTree {
const user = this.userService.getCurrentUser();
const userCanAccessA = false; // TODO: implement
if (userCanAccessA) {
return true;
}
// redirect to /b
return this.router.parseUrl('/b');
}
}
I am not recommending this example as a good implementation, I am merely demonstrating the capabilities of a route guard. Your requirements will determing how your routes are structured, and what the redirect logic is.
It can be tempting to make guards do a lot of work. Instead, I would keep each guard focussed on performing a specific task. You can then compose guard logic by combining guards if required.
Note that in my demo below, component A is never constructed, since AuthGuard
prevents access before routing occurs.