0

我已经使用常见的“我可以离开页面”对话框实现了一个 canDeactivate 守卫。在大多数情况下,它都有效。如果我确认,我可以留下一个页面,如果我取消,我可以留下。这里是警卫:

import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree, CanDeactivate, Router } from '@angular/router';
import { ModalComponent } from '@ccp/app/controls/layout/modal/modal.component';
import { Observable } from 'rxjs';
 
 
export interface CanComponentDeactivate {
  canDeactivate: () => Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree;
}
 
@Injectable({
  providedIn: 'root'
})
export class CanLeaveGuard implements CanDeactivate<CanComponentDeactivate> {
  constructor(private router: Router) { }
 
  canDeactivate(
    component: CanComponentDeactivate,
    currentRoute: ActivatedRouteSnapshot,
    currentState: RouterStateSnapshot,
    nextState: RouterStateSnapshot ): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
 
      // the nextState is an optional url state that allows us to see the requested url
      // we can use this say for example to detect if the user want to route to a previous
      // page, if he does we can avoid displaying the poppin by conditionally returning true.
 
     return component.canDeactivate ? component.canDeactivate() : true;
  }
}

这里是当守卫调用指定组件中的方法时调用的canDeactivate方法

  public shouldUserLeavePreRegFlow(dialogComponent: ModalComponent, decision: Subject<boolean>, router: Router): Observable<boolean | UrlTree> | boolean | UrlTree {
    dialogComponent.open();
    return decision
      .pipe(map((isLeavingPage) => {
        if (isLeavingPage) {
          // leave page
          console.log('leave page true');
          return true;
        } else {
          // stay on current page
          return router.createUrlTree([router.routerState.snapshot.url]);
        }
      }));

  }

决策变量是您在下面看到的主题 navigateAwaySelection$,当它弹出并等待用户响应时,它会从我的自定义对话中接受真或假的发射,当他响应时调用接受方法,该方法调用接下来就通过用户判断真假的主题。

public acceptance(decision: boolean): void { this.navigateAwaySelection$.next(decision); }

因此,这在导航到典型视图时有效。但是有一种极端情况,当我们想确认离开页面时,我们不得不在弹出的对话框上单击两次。这种情况是当我们尝试访问由 canActivate 保护的页面时,如果在桌面上,该保护将重定向到第二个页面,在这种情况下,我们运行 navigateByUrl 进行重定向。

  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean | UrlTree {
 
    if (this.deviceDetectorService.isDesktop()) {
      this.router.navigateByUrl('register/manual',
        {queryParams: route.queryParams}
      );
      return false;
    }
    return true;
  }
}

导航跟踪器显示我们进入注册/手册(预期页面)页面,但实际上屏幕快速闪烁,我们回到了对话和原始页面,再次点击离开页面终于让我们到达目的地。就像第一次导航被取消只是第二次被接受一样,请记住 canDeactivate 保护是在其自己的功能模块文件夹中的指定组件路由中声明的,因为我正在使用延迟加载的模块功能,如果我尝试添加它到全局路由列表,canDeactivate 防护中的“组件”变为空。有关这方面的更多信息,请参阅https://github.com/angular/angular/issues/16868

canDeactivate guard > canActivate guard > canDeactivate guard > canActivate guard > 到达预期页面。

有没有办法解决这个问题,这样我只需要在离开页面上单击一次,就像在其他页面中一样?

角度示踪剂

// 单击链接以导航离开时的跟踪器结果

    Router Event: NavigationStart
browser_adapter.ts:41 NavigationStart(id: 4, url: '/')
browser_adapter.ts:41 NavigationStart {id: 4, url: "/", navigationTrigger: "imperative", restoredState: null}
browser_adapter.ts:47 Router Event: RouteConfigLoadStart
browser_adapter.ts:41 RouteConfigLoadStart(path: register)
browser_adapter.ts:41 RouteConfigLoadStart {route: {…}}
logger.ts:37 Load chunk started  register
browser_adapter.ts:47 Router Event: RouteConfigLoadEnd
browser_adapter.ts:41 RouteConfigLoadEnd(path: register)
browser_adapter.ts:41 RouteConfigLoadEnd {route: {…}}
logger.ts:37 Load chunk ended  register
browser_adapter.ts:47 Router Event: RoutesRecognized
browser_adapter.ts:41 RoutesRecognized(id: 4, url: '/', urlAfterRedirects: '/register', state: Route(url:'', path:'') { Route(url:'', path:'') { Route(url:'register', path:'register') { Route(url:'', path:'') }  }  } )
browser_adapter.ts:41 RoutesRecognized {id: 4, url: "/", urlAfterRedirects: "/register", state: RouterStateSnapshot}
browser_adapter.ts:47 Router Event: GuardsCheckStart
browser_adapter.ts:41 GuardsCheckStart(id: 4, url: '/', urlAfterRedirects: '/register', state: Route(url:'', path:'') { Route(url:'', path:'') { Route(url:'register', path:'register') { Route(url:'', path:'') }  }  } )
browser_adapter.ts:41 GuardsCheckStart {id: 4, url: "/", urlAfterRedirects: "/register", state: RouterStateSnapshot}

//点击离开一次后

 Router Event: ChildActivationStart
browser_adapter.ts:41 ChildActivationStart(path: '')
browser_adapter.ts:41 ChildActivationStart {snapshot: ActivatedRouteSnapshot}
browser_adapter.ts:47 Router Event: ActivationStart
browser_adapter.ts:41 ActivationStart(path: 'register')
browser_adapter.ts:41 ActivationStart {snapshot: ActivatedRouteSnapshot}
browser_adapter.ts:47 Router Event: NavigationCancel
browser_adapter.ts:41 NavigationCancel(id: 4, url: '/')
browser_adapter.ts:41 NavigationCancel {id: 4, url: "/", reason: "Navigation ID 4 is not equal to the current navigation id 5"}
browser_adapter.ts:47 Router Event: NavigationStart
browser_adapter.ts:41 NavigationStart(id: 5, url: '/register/manual')
browser_adapter.ts:41 NavigationStart {id: 5, url: "/register/manual", navigationTrigger: "imperative", restoredState: null}
browser_adapter.ts:47 Router Event: RoutesRecognized
browser_adapter.ts:41 RoutesRecognized(id: 5, url: '/register/manual', urlAfterRedirects: '/register/manual', state: Route(url:'', path:'') { Route(url:'', path:'') { Route(url:'register/manual', path:'register/manual') { Route(url:'', path:'') }  }  } )
browser_adapter.ts:41 RoutesRecognized {id: 5, url: "/register/manual", urlAfterRedirects: "/register/manual", state: RouterStateSnapshot}
browser_adapter.ts:47 Router Event: GuardsCheckStart
browser_adapter.ts:41 GuardsCheckStart(id: 5, url: '/register/manual', urlAfterRedirects: '/register/manual', state: Route(url:'', path:'') { Route(url:'', path:'') { Route(url:'register/manual', path:'register/manual') { Route(url:'', path:'') }  }  } )
browser_adapter.ts:41 GuardsCheckStart {id: 5, url: "/register/manual", urlAfterRedirects: "/register/manual", state: RouterStateSnapshot}

第二次点击离开后

Router Event: ChildActivationStart
browser_adapter.ts:41 ChildActivationStart(path: '')
browser_adapter.ts:41 ChildActivationStart {snapshot: ActivatedRouteSnapshot}
browser_adapter.ts:47 Router Event: ActivationStart
browser_adapter.ts:41 ActivationStart(path: 'register/manual')
browser_adapter.ts:41 ActivationStart {snapshot: ActivatedRouteSnapshot}
browser_adapter.ts:47 Router Event: ChildActivationStart
browser_adapter.ts:41 ChildActivationStart(path: 'register/manual')
browser_adapter.ts:41 ChildActivationStart {snapshot: ActivatedRouteSnapshot}
browser_adapter.ts:47 Router Event: ActivationStart
browser_adapter.ts:41 ActivationStart(path: '')
browser_adapter.ts:41 ActivationStart {snapshot: ActivatedRouteSnapshot}
browser_adapter.ts:47 Router Event: GuardsCheckEnd
browser_adapter.ts:41 GuardsCheckEnd(id: 5, url: '/register/manual', urlAfterRedirects: '/register/manual', state: Route(url:'', path:'') { Route(url:'', path:'') { Route(url:'register/manual', path:'register/manual') { Route(url:'', path:'') }  }  } , shouldActivate: true)
browser_adapter.ts:41 GuardsCheckEnd {id: 5, url: "/register/manual", urlAfterRedirects: "/register/manual", state: RouterStateSnapshot, shouldActivate: true}
browser_adapter.ts:47 Router Event: ResolveStart
browser_adapter.ts:41 ResolveStart(id: 5, url: '/register/manual', urlAfterRedirects: '/register/manual', state: Route(url:'', path:'') { Route(url:'', path:'') { Route(url:'register/manual', path:'register/manual') { Route(url:'', path:'') }  }  } )
browser_adapter.ts:41 ResolveStart {id: 5, url: "/register/manual", urlAfterRedirects: "/register/manual", state: RouterStateSnapshot}
browser_adapter.ts:47 Router Event: ResolveEnd
browser_adapter.ts:41 ResolveEnd(id: 5, url: '/register/manual', urlAfterRedirects: '/register/manual', state: Route(url:'', path:'') { Route(url:'', path:'') { Route(url:'register/manual', path:'register/manual') { Route(url:'', path:'') }  }  } )
browser_adapter.ts:41 ResolveEnd {id: 5, url: "/register/manual", urlAfterRedirects: "/register/manual", state: RouterStateSnapshot}
browser_adapter.ts:47 Router Event: ActivationEnd
browser_adapter.ts:41 ActivationEnd(path: '')
browser_adapter.ts:41 ActivationEnd {snapshot: ActivatedRouteSnapshot}
browser_adapter.ts:47 Router Event: ChildActivationEnd
browser_adapter.ts:41 ChildActivationEnd(path: 'register/manual')
browser_adapter.ts:41 ChildActivationEnd {snapshot: ActivatedRouteSnapshot}
browser_adapter.ts:47 Router Event: ActivationEnd
browser_adapter.ts:41 ActivationEnd(path: 'register/manual')
browser_adapter.ts:41 ActivationEnd {snapshot: ActivatedRouteSnapshot}
browser_adapter.ts:47 Router Event: ChildActivationEnd
browser_adapter.ts:41 ChildActivationEnd(path: '')
browser_adapter.ts:41 ChildActivationEnd {snapshot: ActivatedRouteSnapshot}
browser_adapter.ts:47 Router Event: ActivationEnd
browser_adapter.ts:41 ActivationEnd(path: '')
browser_adapter.ts:41 ActivationEnd {snapshot: ActivatedRouteSnapshot}
browser_adapter.ts:47 Router Event: ChildActivationEnd
browser_adapter.ts:41 ChildActivationEnd(path: '')
browser_adapter.ts:41 ChildActivationEnd {snapshot: ActivatedRouteSnapshot}
browser_adapter.ts:47 Router Event: NavigationEnd
browser_adapter.ts:41 NavigationEnd(id: 5, url: '/register/manual', urlAfterRedirects: '/register/manual')
browser_adapter.ts:41 NavigationEnd {id: 5, url: "/register/manual", urlAfterRedirects: "/register/manual"}

也许这是罪魁祸首:

browser_adapter.ts:47 Router Event: NavigationCancel
browser_adapter.ts:41 NavigationCancel(id: 4, url: '/')
browser_adapter.ts:41 NavigationCancel {id: 4, url: "/", reason: "Navigation ID 4 is not equal to the current navigation id 5"}
4

0 回答 0