Below is a table in my Angular app. It is populated with data from employees.json
:
<tbody>
<tr *ngFor="let employee of employees">
<td (click)="viewEmployeeProfile(1, employee.id)">{{employee.fullName}}
</td>
</tr>
</tbody>
When the user clicks on a name, the employeeId
is passed to this method:
viewEmployeeProfile(roleId: number, employeeId: number) {
this._router.navigate(['/profile', roleId, employeeId]);
}
Here is the route in my AppRouting
module:
const routes: Routes = [
{
path: 'profile/:role/:id',
component: ProfileComponent,
// canActivate: [RequireEmployeeProfileGuardGuard]
},
{
path: 'page-not-found',
component: PageNotFoundComponent
},
{
path: '**',
component: PageNotFoundComponent
}
];
Example path: http://localhost:4200/profile/1/4
When the user routes to theProfile
component, this code is called:
profile.component.ts:
ngOnInit() {
this.route.paramMap.subscribe(params => {
const roleId = +params.get('role');
const empId = +params.get('id');
this.getEmployee(empId);
});
}
getEmployee(id: number) {
this.employeeService.getEmployee(id).subscribe(
(employee: IEmployee) => this.displayEmployee(employee),
(err: any) => console.log(err)
);
}
displayEmployee(employee: IEmployee) {
this.employee.fullName = employee.fullName;
}
profile.component.html:
<tr>
<td><b>Full Name</b></td>
<td>{{employee.fullName}}</td>
</tr>
And here is my employee.service
:
baseUrl = 'http://localhost:3000/employees';
getEmployee(id: number): Observable<IEmployee> {
return this.httpClient.get<IEmployee>(`${this.baseUrl}/${id}`)
.pipe(catchError(this.handleError));
}
This code is working fine, & displays data as expected.
Currently, if I navigate to a route such as http://localhost:4200/profile/1/123456789
, where that employeeId
does not exist, the Profile
component is displayed with no data.
Instead of this, I would want the user to be brought back to the PageNotFound
component.
Here are my current routes:
const routes: Routes = [
{ path: 'profile/:role/:id', component: ProfileComponent },
{ path: '**', component: PageNotFoundComponent }
];
Can someone please tell me what changes I need to make to implement this?
This is a perfect opportunity for the CanActivate guard.
Since Angular 7.1.0, route guards may now return a URLTree which gives us some really cool flexibility in our guards. Here is a nice article that goes over the changes and what they mean / how they can be used.
I would suggest you create your guard. Something like the following:
From here, just go to your Routing Module, import this guard and add it to your route like this:
I would also probably define an explicitly named route for the error component too like:
So then
'absolute-redirect-url-here'
from the guard above would become'page-not-found'
.Also, since you would still want to have a 'catch-all' case for invalid URLs, you would probably want a route like:
So how does this work?
It might look complex but it's very simple at its core really. The magic is in the
canActivate()
method that we added to the guard:We are requesting the employee profile from the
employeeService
and converting it to a boolean using the double-not operator (basically, checking "do we have a matching employee profile"). If this converts to afalse
then theURLTree
is returned which will redirect the router to the specified, absolute route.When any route is being resolved, Angular will run through all the guards attached to it in a pre-defined order. If any of the guards 'fail' then the route won't be loaded.
You can modify your
getEmployee
method to do so something likeAdd below routings to
AppRouting
moduleThe above route will catch incorrect routes at the root level and within any nested children as well.put this path as the last path in your routing module
call
gotoHeroes
method from thegetEmployee
method if records count is 0 or null