I have route configuration set up via #NgModule. And I have a service that identifies what parts of the application should be shown for the user depending on certain conditions. I need to call that service and setup the routes according to the returned value.
Problem: Route configuration is setup inside an annotation and I can't get how to call the service in such setup.
To be more specific here is the example configuration I want to enhance.
My current routing setup:
const appRoutes: Routes = [
{
path: '',
redirectTo: 'first-route',
pathMatch: 'full'
},
{
path: 'first-route',
component: FirstComponent,
pathMatch: 'full'
},
{
path: 'second-route',
component: SecondComponent,
pathMatch: 'full'
},
...
];
#NgModule({
imports: [RouterModule.forChild(appRoutes)],
exports: [RouterModule]
})
export class MyRoutingModule {
}
The service that should change the route setup:
#Injectable()
export class MyService {
getAccessibleRoutes(): Observable<string[]> {...}
}
Question: How can I make a service call and change the routes?
Note: I also looked on "Dynamically adding routes in Angular" and "How we can add new routes dynamically into RouterModule(#NgModule imports)" but I haven't found clear answer there.
If I correctly understood your problem, I think you probably can consider using route guards to reach you goal. I suggest you to use guards feature to specify the conditions of accessing your routes, instead of changing the list of routes.
Please check this link for more information about route guards:
https://codecraft.tv/courses/angular/routing/router-guards/
I hope this will help you.
import { Injectable } from '#angular/core';
import { CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot } from '#angular/router';
import { YourSecurityService } from './your-security.service';
#Injectable()
export class YourRouteGuardService implements CanActivate {
constructor(
private router: Router,
private yourSecurityService: YourSecurityService) {
}
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot): boolean {
console.log(state.url); // HERE YOU CAN GET REQUESTED ROUTE
if (this.yourSecurityService.checkIfUserHaveAccess())
return true;
this.router.navigate(['your-route-to-redirect']);
return false;
}
}
Next you should apply your guard to your route:
const appRoutes: Routes = [
{
path: 'someroute',
component: RouteComponent,
canActivate: [YourRouteGuardService]
},
...
]
Related
I have already implemented ngx-translate succesfully. Now, I want to change the base href of my Angular project, depending on the language I choose from my header menu.
Currently, my URL looks like this: "localhost:4200". Then, when you launch the project, it must show something like this: "localhost:4200/en" or like this: "localhost:4200/es", depending on the choosen language.
My index html has this:
<base href="/"/>
And my header component ts file has a function that changes the language using ngx-translate. As you can see, I tried to use 'replaceState' to show the choosen language in the URL, and it worked, but it disappears once I navigate to another route.
import { Component, OnInit } from '#angular/core';
//For translate language
import { TranslateService } from '#ngx-translate/core';
import { Router, Event, NavigationStart, NavigationEnd } from '#angular/router';
import { Location } from '#angular/common';
#Component({
selector: 'app-header',
templateUrl: './header.component.html',
styleUrls: ['./header.component.scss'],
})
export class HeaderComponent implements OnInit {
constructor(private translate: TranslateService,
private router: Router,
private location: Location,
)
{ translate.addLangs(['es','en']);
translate.setDefaultLang('es');
}
ngOnInit(): void {
}
useLanguage(language: string): void {
this.translate.use(language);
// alert(language);
// location.replace("https://www.google.com");
// return;
const modified_path = language;
this.location.replaceState(modified_path);
}
}
It looks like you are trying to achieve some kind of routing using base href. I would use base href only if I need multiple instances of my application. E.g. each of them in a subfolder.
Maybe you should give try on Angular Router (https://angular.io/guide/router-reference) if you want just one instance of the application handling different languages.
The idea is to have a route on the root level that represents the language and a language guard that ensures only valid languages are called.
This would look something like this:
const routes2: Routes = [
{
path: ':language',
canActivate: [LanguageGuard],
children: [
{
path: 'home',
component: HomeComponent,
},
{
path: 'some-page',
component: SomePageComponent,
},
]
},
{
path: '**',
redirectTo: '/en',
},
];
I've came across this specfic problem in my Angular project architecture:
I have one component that need to load different service depending on current URL. It's done by resolving service like in example below.
ChartRoutingModule
const routes: Routes = [
{
path: 'doctorspecialities',
component: ChartComponent,
resolve: {
service: DoctorSpecialitiesServiceResolver,
},
},
]
#NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
providers: [DoctorSpecialitiesServiceResolver],
})
export class ChartRoutingModule {}
When user enter specific url the service is resolved:
DoctorSpecialitiesServiceResolver
#Injectable()
export class DoctorSpecialitiesServiceResolver implements Resolve<any> {
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
return new DoctorSpecialitiesService();
}
}
And the service is in ActivatedRoute. Everything works fine for now. But I need to inject HttpClient into this service. Adding private _http: HttpClient in constructor of DoctorSpecialitiesService results in this error:
An argument for '_http' was not provided.
I think I should pass this dependency in Resolver but I have no idea where should I declare it. Couldn't find anything that helps me. Could you give me a hand with this?
You can have the HttpClient injected in your resolver, then pass that to the DoctorSpecialitiesService constructor like so:
#Injectable()
export class DoctorSpecialitiesServiceResolver implements Resolve<any> {
constructor(private http: HttpClient) { }
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
return new DoctorSpecialitiesService(this.http);
}
}
I'm building a Ionic app using Angular and calling an API to get some results. According to a particular property of the response object (such as "mode"="0" or mode="1" I need to change paths defined in app routing module, in particular to dynamically change the home page.
I'd like appcomponent (starting component) to call the API and to check the mode, then passing some routes according to that property.
E.g.:
I'd like to have something like:
if (mydata['mode']==="0") {
this.appRoutes = [
{
path: '',
redirectTo: 'firstPath',
pathMatch: 'full'
},
{
path: 'firstPath',
loadChildren: './firstpath.module#FistPathModule'
},
{
path: 'secondPath',
loadChildren: './secondpath.module#SecondPathModule'
}
]
} else if (my_data['mode']==="1") {
this.appRoutes = [
{
path: '',
redirectTo: 'secondPath',
pathMatch: 'full'
},
{
path: 'secondPath',
loadChildren: './secondpath.module#SecondPathModule'
},
]
}
Is there a way to do something like this inside the app-routing.module?
Hiding firstPath in the second case is possible as well?
This is not a correct way to make changes in app-routing file for such requirement.
You can use Guard to implement this.
In your child route file do like this:
{
path: '',
component: HomePage,
canActivate: [AuthGuard]
}
Auth Guard file:
import { Injectable } from '#angular/core';
import { ActivatedRouteSnapshot, RouterStateSnapshot, CanActivate } from '#angular/router';
#Injectable({
providedIn: 'root'
})
export class AuthGuard implements CanActivate {
mode: number; **// You can save this mode variable into a common service and use it.**
constructor() { }
canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): any {
if(this.mode == 1) {
return false;
}else {
return true;
}
});
}
}
I'm having trouble capturing the original navigating route using a guard in Angular 2.
My site consists of a core module protected by an authorization guard, and a login page that's unprotected.
The core module has it's own sub routes defined in it's own app routing file, and any undefined routes are redirected to the root path.
Here's my top level routing module.
import { NgModule } from '#angular/core';
import { RouterModule, Routes } from '#angular/router';
import { AuthGuard } from './auth';
const routes: Routes = [
// Login module is public
{ path: 'login', loadChildren: 'app/auth/auth.module#AuthModule' },
// Core route protected by auth guard
{ path: '', loadChildren: 'app/core/core.module#CoreModule', canLoad: [AuthGuard] },
// otherwise redirect to home
{ path: '**', redirectTo: '' }
];
#NgModule({
imports: [ RouterModule.forRoot(routes) ],
exports: [ RouterModule ]
})
export class AppRoutingModule {}
And here is the AuthGuard class.
import { Injectable } from '#angular/core';
import { Router, CanLoad, Route } from '#angular/router';
import { AuthService } from './auth.service';
#Injectable()
export class AuthGuard implements CanLoad {
constructor(
private authService: AuthService,
private router: Router
) {}
canLoad(route: Route): boolean {
this.authService.redirectUrl = `/${route.path}`;
console.log('path:' + route.path);
if (this.authService.isLoggedIn()) {
return true;
} else {
this.router.navigate(['/login']);
return false;
}
}
}
This is a pretty straightforward login/redirect scheme, however the route.path value is always empty, regardless of what URL I navigate to. I have a hunch that it has something to do with the { path: '**', redirectTo: '' } route but I'm not sure.
I don't want to use canActivate because I only want the main module loaded if the user is actually logged in.
What I expected was that if I navigate to /foobar then route.path would be set to foobar in the AuthGuard class but it is not. It is always empty, thus I am unable to do a correct redirect after the user logs in.
Try adding the pathMatch: 'full' like this:
{path: '**', redirectTo: '', pathMatch: 'full'}
or
import {CanActivate, RouterStateSnapshot, ActivatedRouteSnapshot} from "#angular/router";
import { Subscription, Observable } from "rxjs/Rx";
export class HomepageGuard implements CanActivate {
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | boolean {
console.log(`[homepage.guard.ts]-[canActivate()]`);
console.log(route);
console.log(state);
// are you allowed to continue
return true;
}
}
I've a small question regarding Angular 2 router using version 3.0.0-rc.1 I want to navigate to different home component based on user role such as AdminComponent or UserComponent. Can anyone please help in modifying below routes so that I can achieve the desired functionality?
{path: 'login', component: LoginComponent}, // <--- This redirects to '/' in case user is logged in
{
path: '',
component: HomeComponent,
canActivate: [AuthGuardService], // <--- Check if user is logged in, else redirect to login
children: [
{
path: '',
component: AdminComponent // <--- Want to navigate here if user role is 'admin'
},
{
path: '',
component: UserComponent // <--- Want to navigate here if user role is 'user'
}
]
}
AuthGuardService.ts
import {Injectable} from "#angular/core";
import {CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot} from "#angular/router";
import {AuthService} from "./auth.service";
#Injectable()
export class AuthGuardService implements CanActivate {
constructor(private authService: AuthService, private router: Router) {
}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
if (this.authService.isLoggedIn()) {
return true;
}
// Store the attempted URL for redirecting
this.authService.redirectUrl = state.url;
// Navigate to the login page with extras
this.router.navigate(['/login']);
return false;
}
}
AuthService.ts
import {Injectable} from "#angular/core";
#Injectable()
export class AuthService {
redirectUrl: string;
logout() {
localStorage.clear();
}
isLoggedIn() {
return localStorage.getItem('token') !== null;
}
isAdmin() {
return localStorage.getItem('role') === 'admin';
}
}
Thanks.
You can achieve it by below way.
{path: 'login', component: LoginComponent}, // <--- This redirects to '/' in case user is logged in
{
path: '',
component: HomeComponent,
canActivate: [AuthGuardService],
}
this is your home component html(home.component.html)
<app-admin *ngIf="user_role==='admin'"></app-admin>
<app-user *ngIf="user_role==='user'"></app-user>
make sure you are assigning user_role in your typescript file of home component
this is your admin component html(admin.component.html)
<div>
this is admin component
</div>
this is your user component html(user.component.html)
<div>
this is user component
</div>
Hope, This will help you.
The problem is that you can't have two routes with the same path. The easiest change you can make is to change the path to something like this:
{
path: 'admin',
component: AdminComponent
},
{
path: 'user',
component: UserComponent
}
This is probably the best option because since you want your components to be different based on the user role. You might also want other components to be different and you can do that easily by adding children to the admin or the user routes.
In your AuthGuard you still only return true, but you make two other guards for the admin and user routes. Which check if the user is or isn't the admin.
And you redirect to the correct route by checking the user role once he loges in and then in the component you do router.navigate(['/admin']) or router.navigate(['/user'])