im starting to work with Angular, and im trying to create a simple route guard, to redirect user to login page, if my service return unauthorized.
To do that i created this route schema ->
const routes: Routes = [
{
path: '',
component: LoggedComponent,
children: [
{path: '', component: HomeComponent}
],
canActivate: [RouteGuard]
},
{
path: '',
component: AuthComponent,
children: [
{path: '', redirectTo: '/login', pathMatch: 'full'},
{path: 'login', component: LoginComponent},
{path: 'signin', component: SigninComponent}
]
},
];
#NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
And this is my guard service ->
PS: Im setting a default value false.
import {Subject, Observable} from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class RouteGuard implements CanActivate {
authorized: Subject<boolean> = new Subject();
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot): boolean {
this.setObservable(false)
return false;
}
getObservable(): Observable<boolean> {
console.log(`starts to observe`)
return this.authorized.asObservable();
}
setObservable(newState: boolean) {
console.log(`new observable state: ${newState}`)
this.authorized.next(newState)
}
}
Ok, since the value is returning false as default, I expect the route to be automatically redirected to the AuthComponent, because Auth is the second option at my routes[]. Right?
So...
At the AuthComponent i stated to observe the authorized status:
import {RouteGuard} from '#acn-collections-ws/shared';
#Component({
selector: 'acn-collections-ws-auth',
templateUrl: './auth.component.html',
styleUrls: ['./auth.component.scss']
})
export class AuthComponent implements OnInit {
constructor(private guard: RouteGuard, router: Router) {
console.log('im here');
this.guard.getObservable().subscribe(authorized => {
})
}
ngOnInit(): void {
}
}
But AuthComponent dosent load. it seems that when the canActivate parameter returns false, it does not go to the AuthComponent, it does not load anything. When the authorized (canActivate) returns true, it runs normally. Has anyone had a similar problem and can help me?
This is how I do it when authenticating using Firebase:
export class GuardGuard implements CanActivate {
constructor(private authService: AuthService, private router: Router){}
async canActivate() {
const user = await this.authService.isLogged();
if(user){
return true;
}
else {
this.router.navigate(['login']);
return false;
}
}
}
If the user's logged return true so it loads the requested route if not redirects to the login route and return false.
And this is the routing:
import { NgModule } from '#angular/core';
import { Routes, RouterModule } from '#angular/router';
import { LoginComponent } from './modules/login/pages/login/login.component';
import { SignupComponent } from './modules/login/pages/signup/signup.component';
import { HeroComponent } from './shared/components/hero/hero.component';
import { NotFoundComponent } from './shared/components/not-found/not-found.component';
import { GuardGuard } from './shared/guards/guard.guard';
const routes: Routes = [
{ path: 'home', component: HeroComponent },
{ path: '', redirectTo: 'home', pathMatch: 'full' },
{ path: 'signup', component: SignupComponent },
{ path: 'login', component: LoginComponent },
{ path: 'tasks', loadChildren: ()=> import('./modules/task/task.module').then(m => m.TaskModule), canActivate: [GuardGuard] },
{ path: 'profile', loadChildren: ()=> import('./modules/user/user.module').then(m => m.UserModule), canActivate: [GuardGuard] },
{ path: '**', component: NotFoundComponent }
];
Related
I have an angular application where I want to use authguard to protect some routes but for some reason it does not work. I first make a post request to my spring boot backend, if that gives me success I set a value to true and check that value in my canActivate methode. But it does not work, it does not even go into that method. I thought it is called automatically if I set it in my path in app-route.module. How can I use it correctly?
Authenticationservice with authguard together:
export class AuthenticationService {
constructor(
private http: HttpClient,
private router: Router,
) { }
authenticateUser(login: LoginModel){
return this.http.post(environment.rootUrl + 'authenticate', {
username: login.username,
password: login.password,
}).subscribe({
next: (data) => {
localStorage.setItem('token', data.toString())
}, error: (error) => {
this.isAuthenticated = false
}
})
}
isUserLoggedIn(){
return !!localStorage.getItem('token')
}
}
Authguard:
#Injectable({
providedIn: 'root'
})
export class AuthGuard implements CanActivate {
constructor(
private auth: AuthenticationService,
private router: Router
) {
}
canActivate(): Promise<boolean> {
return new Promise(resolve => {
if (this.auth.isUserLoggedIn()) {
resolve(true)
} else {
this.router.navigate(['authenticate'])
resolve(false)
}
})
}
}
App-module:
#NgModule({
declarations: [AppComponent, NxWelcomeComponent],
imports: [
BrowserModule,
BrowserAnimationsModule,
NbLayoutModule,
LoginComponentModule,
AppRoutingModule,
NbThemeModule.forRoot({ name: 'default' }),
NbLayoutModule,
NbEvaIconsModule,
],
providers: [AuthGuard],
bootstrap: [AppComponent],
})
export class AppModule {}
App-routing-module:
const routes: Routes = [
{path: 'dashboard' , component: DashboardComponent, canActivate: [AuthGuard]},
{path: 'authenticate', component: LoginComponent},
{path: '' , redirectTo: 'authenticate', pathMatch: 'full'}
];
#NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
providers: [AuthGuard]
})
export class AppRoutingModule {
}
You need to route to the dashboard after you do the login:
The routes:
const routes: Routes = [
{
path: 'dashboard',
component: DashboardComponent,
canActivate: [AuthGuard],
},
{ path: '', redirectTo: 'authenticate', pathMatch: 'full' },
];
The Component:
export class AppComponent {
constructor(
private authService: AuthenticationService,
private router: Router
) {}
callServiceMethod(value: string) {
// This will need to wait for auth to finish before navigating in the actual app.
this.authService.authenticateUser(value);
if (this.authService.isAuthenticated) {
this.router.navigate(['dashboard']);
}
}
}
The Guard:
#Injectable({ providedIn: 'root' })
export class AuthGuard implements CanActivate {
constructor(private auth: AuthenticationService, private router: Router) {}
canActivate(): Promise<boolean> {
return new Promise((resolve) => {
console.log('running auth guard!');
if (this.auth.isUserLoggedIn()) {
resolve(true);
} else {
this.router.navigate(['authenticate']);
resolve(false);
}
});
}
}
Here is an updated example:
https://stackblitz.com/edit/angular-ivy-5excpc?file=src%2Fapp%2Fapp.component.ts
Your canActivate should only take your AuthGuard class which has the resolver.
{path: 'dashboard' , component: DashboardComponent, canActivate: [AuthGuard]}
I am using the same component for my router, on the first click the component affected, but on the next click the component still in the first state.
Here is the script for changing the route
<a [routerLink]="['react/1']">link 1</a>
<a [routerLink]="['react/2']">link 2</a>
Here is my router module
panel-routing.module.ts
import { NgModule } from '#angular/core';
import { Routes, RouterModule } from '#angular/router'
import { PanelCoursesComponent } from 'src/app/components/panel-courses/panel-courses.component';
import { PanelHomeComponent } from 'src/app/components/panel-home/panel-home.component';
import { PanelIntroComponent } from 'src/app/components/panel-intro/panel-intro.component';
const routes: Routes = [
{ path: '', component: PanelHomeComponent },
{ path: 'react', component: PanelIntroComponent },
{ path: 'react/:no', component: PanelCoursesComponent } //the target
]
#NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class PanelRoutingModule { }
panel-course.component.ts
import { Component, OnInit } from '#angular/core';
import { Router, ActivatedRoute } from '#angular/router'
#Component({
selector: 'app-panel-courses',
templateUrl: './panel-courses.component.html',
styleUrls: ['./panel-courses.component.scss']
})
export class PanelCoursesComponent implements OnInit {
url!: any
constructor(private route: ActivatedRoute, private router: Router) {
console.log('route')
}
ngOnInit(): void {
this.url = this.router.url
console.log(this.route.snapshot.params) //the test script
}
}
On the PanelCourseComponent I try to console log the params, but that's only executed one time on the first click.
Am I missing something?
You can use this.route.params.subscribe method for this case
Here is the example
ngOnInit(): void {
this.route.params.subscribe(params => {
console.log(params) // It will be executed whenever you click the link
})
}
by default pathMatch is set to 'prefix'. so paths will be matched against your current location and the first one witch "matches" will render its component. to make your paths match only "exact" match add pathMatch: 'full' for your routes
const routes: Routes = [
{ path: '', component: PanelHomeComponent, pathMatch: 'full' },
{ path: 'react', component: PanelIntroComponent, pathMatch: 'full' },
{ path: 'react/:no', component: PanelCoursesComponent } //the target
]
I have the following routes in the application. the problem here is if I navigate to say getEmp-by-id or page-not-found and hit refresh, then application is landing on app-home,. But I want it to stay on the same page where refresh is hit.I am not implementing any RouteGuards, simple navigations. Is there a way I can acheive this.
const appRoutes: Routes = [
{path: '', component: HomeComponent, children: [
{path: 'app-home', component: AppHomeComponent, resolve: {ApphomeResolver : AppHomeResolver}},
{path: 'getEmp-by-id', component: EmpComponent},
{path: 'page-not-found', component: pageNotFoundComponent},]
},
{path: '**', redirectTo: 'page-not-found', pathMatch: 'full'}
];
export class EmpComponent implements OnInit {
constructor(private router: Router, private route: ActivatedRoute, private alertService: AlertService, private employeeService: EmployeeService) { }
ngOnInit() {}
onSubmit() {
this.employeeService.getEmployee(empId).subscribe(
(data) => {
var responseCode = JSON.parse(data).responseCode;
var responseMessage = JSON.parse(data).responseMessage
if (responseCode === 200) {
this.router.navigate(['../emp-details'], { relativeTo: this.route });
} else {
this.router.navigate(['../page-not-found'], { relativeTo: this.route });
}
}, error => {
this.router.navigate(['../page-not-found'], { relativeTo: this.route });
});
} else {
this.alertService.error("Error");
}
}
}
One way of handling page refreshes is to using hash routing. To implement this, write the following code in app.module.ts:
import { APP_BASE_HREF, LocationStrategy, HashLocationStrategy } from '#angular/common';
#NgModule({
......
providers: [
{ provide: APP_BASE_HREF, useValue: '', }
, { provide: LocationStrategy, useClass: HashLocationStrategy }
.....
]})
export class AppModule {
}
Please note that this will add # to your route.
My component:
constructor(
private router: Router
) {
router.events.subscribe((val) => {
if (val instanceof NavigationStart && !isloggedIn) {
console.log(val);
}
});
}
If the user is logged in navigationStart, I want to prevent the user from loading the page if the user is not logged in. How to do this in angular 2?
You should create AuthGuard set it for all routes which user shouldn't land if not's login.
const routesConfig: Routes = [
{
path: 'admin',
component: AdminLayoutComponent,
canActivate: [AuthGuard],
children: [
{ path: 'dashboard', component: AdminDashboardComponent },
]
},
{path: 'admin-login', component: AdminLoginComponent},
{path: 'login', component: LoginComponent},
{path: '**', redirectTo: ''}
];
Here is simple AuthGuard.
#Injectable()
export class AuthGuard implements CanActivate {
constructor() {}
canActivate() {
let user = JSON.parse(localStorage.getItem('user'))
// here do logic for navigate to login if user don't have local storage...
return true;
}
}
For more information check this link.
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;
}
}