Angular Resolver-阻止后续请求,直到第一次解决

问题描述

我有一个UserProfileResolverUserProfileComponent提供数据。我注意到我可以多次单击导航链接,发送大量HTTP请求。

是否有一种标准方法可以防止在初始请求完成之前发出后续请求?

enter image description here

nav

<li class="nav-item">
  <a [routerLink]="['/user-profile/'+userId]" class="nav-link" routerLinkActive="active">
    <i class="fa fa-user" aria-hidden="true"></i>
    <ng-container i18n>Edit Profile</ng-container>
  </a>
</li>

路由模块

const routes: Routes = [
  {
    path: ':id',component: UserProfileComponent,resolve: {data: UserProfileResolver},},];

解析器

export class UserProfileResolver {

  constructor(private readonly userService: UserService,private readonly facilityService: FacilityService) {
  }

  public resolve(route: ActivatedRouteSnapshot,state: RouterStateSnapshot): Observable<any> {

    return new Observable((observer) => {

      forkJoin([
        this.userService.getSingle(route.paramMap.get('id')),this.facilityService.getAll(),]).pipe(
        map(([user,facilities]) => {
          return {user,facilities};
        }),catchError(() => of(undefined)),// dont resolve on error
        tap(data => {
          if (data !== undefined) {
            observer.next(data);
          } else {
            observer.unsubscribe();
          }
          observer.complete();
        }),).subscribe();
    });
  }
}

解决方法

您可以为Angular使用某些 Block UI 实现,例如ng-block-ui,可以将其配置为自动在httpRequests上启动,并防止用户在响应解析之前进行任何其他交互;

您可以通过以下命令进行安装:

npm install ng-block-ui --save

并按如下所示导入Angular应用程序:

// All other imports
import { BlockUIModule } from 'ng-block-ui';
 
@NgModule({
  imports: [
    BlockUIModule.forRoot()
  ],...
})
export class AppModule { }

这是Stackblitz上的一个简单示例。

,

所以我最终只是禁用了按钮,直到导航解决

Nav

<li class="nav-item">
  <a (click)="userProfileClick()" [class.disabled]="userProfileNavigating"
     [routerLink]="['/user-profile/'+userId]" class="nav-link" routerLinkActive="active">
    <i aria-hidden="true" class="fa fa-user"></i>
    <ng-container i18n>Edit Profile</ng-container>
  </a>
</li>

侧面导航

export class SideNavComponent {

  public userId: string;

  public userProfileNavigating: boolean;

  constructor(public readonly authService: AuthService,private readonly router: Router,) {
    this.userId = authService.userId;

    // noticed i could click a router link a bunch of times before it resolved
    router.events.pipe(filter(e => e instanceof NavigationEnd || e instanceof NavigationCancel)).subscribe(e => {
      this.userProfileNavigating = false;
    });
  }

  userProfileClick() {
    if (this.router.routerState.snapshot.url !== `/user-profile/${this.userId}`) {
      this.userProfileNavigating = true;
    }
  }
}

解析器

已针对Angular 10更新,这通过使用EMPTY来取消解析器导航

export class UserProfileResolver implements Resolve<any> {

  constructor(private readonly userService: UserService,private readonly facilityService: FacilityService) {
  }

  public resolve(route: ActivatedRouteSnapshot,state: RouterStateSnapshot):
    Observable<{ user: User; facilities: Array<Facility> }> {
    return forkJoin([
      this.userService.getSingle(route.paramMap.get('id')),this.facilityService.getAll(),]).pipe(
      map(([user,facilities]) => {
        return {user,facilities};
      }),catchError(() => EMPTY),);
  }
}