问题
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?
回答1:
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:
import { Injectable } from '@angular/core';
import { CanActivate, Router, UrlTree, ActivatedRouteSnapshot } from '@angular/router';
import { Observable, of } from 'rxjs';
import { map, catchError } from 'rxjs/operators';
// import your employee service here
@Injectable()
export class RequireEmployeeProfileGuard implements CanActivate {
constructor(private router: Router, private employeeService: EmployeeService) {
}
canActivate(route: ActivatedRouteSnapshot): Observable<boolean | UrlTree> {
return this.employeeService.getEmployee(+route.paramMap.get('id')).pipe(
catchError(() => of(false)),
map(employee => !!employee || this.router.parseUrl('page-not-found'))
);
}
}
From here, just go to your Routing Module, import this guard and add it to your route like this:
{
path: 'profile/:role/:id',
component: ProfileComponent,
canActivate: [RequireEmployeeProfileGuard]
}
I would also probably define an explicitly named route for the error component too like:
{
path: 'page-not-found',
component: PageNotFoundComponent
}
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:
{
path: '**',
redirectTo: 'page-not-found'
}
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 a false
then the URLTree
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.
回答2:
You can modify your getEmployee
method to do so something like
getEmployee(id: number) {
this.employeeService.getEmployee(id).subscribe(
(employee: IEmployee) => {
if(employee){
this.displayEmployee(employee)
}else{
//redirect to page not found here
}
},
(err: any) => console.log(err)
);
}
回答3:
Add below routings to AppRouting
module
{path: '404', component: NotFoundComponent},
{path: '**', redirectTo: '/404'}
The 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 the getEmployee
method if records count is 0 or null
gotoHeroes() {
this.router.navigate(['/404']);
}
来源:https://stackoverflow.com/questions/56751852/how-to-guard-route-if-http-get-request-returns-no-data