How to guard route if HTTP GET request returns no data?

可紊 提交于 2019-12-11 06:05:09

问题


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 Profilecomponent 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!