【问题标题】:Components doesn't render after guard, angular 9防护后组件不渲染,角度 9
【发布时间】:2020-03-30 10:24:49
【问题描述】:

我正在尝试使用AuthGuard 中间件保护经过身份验证的路由。当我将此中间件实现为路由时。激活守卫后,这些路线不起作用。

应用路由模块

const routes: Routes = [
  {
    path: 'posts',
    canLoad: [AuthGuardMiddleware],
    loadChildren: () => import('./modules/posts/posts.module').then(m => m.PostsModule)
  },
  {
    path: 'users',
    canLoad: [AuthGuardMiddleware],
    loadChildren: () => import('./modules/users/users.module').then(m => m.UsersModule)
  }
];

@NgModule({
  imports: [RouterModule.forRoot(routes, {
    preloadingStrategy: PreloadAllModules
  })],
  exports: [RouterModule]
})

和 AuthGuardMiddleware

@Injectable({
  providedIn: 'root'
})
export class AuthGuardMiddleware implements CanActivate, CanLoad {
  constructor(private authService: AuthService, private router: Router) {
  }

  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): Observable<boolean | UrlTree> {
    return this.authService.user.pipe(map(user => {
      if (user) {
        return true;
      }
      return this.router.createUrlTree(['/login']);
    }));
  }

  canLoad(route: Route, segments: UrlSegment[]): Observable<boolean> | Promise<boolean> | boolean {
    return this.authService.user.pipe(map(user => {
      if (user) {
        return true;
      }
      this.router.navigate(['/']);
      return false;
    }));
  }
}

我该如何解决? 注意:AuthService::user 类型为 BehaviorSubject 初始化为 null

更新

我意识到当我清理我的应用程序组件并删除条件路由器出口时,一切都开始正常工作。我仍然不知道究竟是什么导致了这个错误。

应用组件

export class AppComponent implements OnInit {
  currentUrl: string;
  isAuthRoute = false;

  constructor(private router: Router) {
  }

  ngOnInit() {
    this.router.events.subscribe(event => {
      if (event instanceof NavigationEnd) {
        this.currentUrl = event.url;
        this.isAuthRoute = ['/login', '/register']
          .indexOf(this.currentUrl) !== -1;
      }
    });
  }
}

应用组件视图

<div *ngIf="!isAuthRoute">
  <div class="row page-wrapper">
    <div class="col-md-3 col-md-pull-9">
      <app-sidebar></app-sidebar>
    </div>
    <div class="col-md-9 col-md-push-3 content-area">
      <router-outlet></router-outlet>
    </div>
  </div>
</div>

<div style="height: 80vh" class="d-flex justify-content-center align-items-center" *ngIf="isAuthRoute">
  <router-outlet ></router-outlet>
</div>

【问题讨论】:

  • 根据您的解决方案,也许您应该将显示/隐藏路由器插座的逻辑传递给延迟加载模块(守卫将决定是否可以加载/激活它)。 router-outlet 从一开始就需要在那里吗?我听说过 angular-dart 有这样的问题。但是......在得出这个结论之前,请尝试添加一个额外的模块,该模块可以作为您应用程序所有其余部分的外壳,并且只会为授权用户加载/激活。它相当于您的*ngIf 逻辑。
  • 其实这里的 ngIf 逻辑只是为了显示一页登录页面并隐藏侧边栏。所有经过身份验证的组件都封装为一个单独的模块。
  • 是的,我明白了……我建议您将该逻辑转移到一个新的延迟加载 shell 模块的保护中。这将解决问题如果它确实与动态创建的router-outlet有关。
  • 看看this angular-dart issue 显然与你所拥有的相似。
  • @julianobrasil 它也可以,谢谢你的所有建议,但我会接受 PierreDuc 的回答

标签: angular


【解决方案1】:

你的 AuthService.user 返回一个 Observable 流,它永远不会完成。据我所知,canLoad 和canActivate 需要一个完整的Observable。您可以使用take(1) 运算符来实现:

  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): Observable<boolean | UrlTree> {
    return this.authService.user.pipe(
       map(user => {
        if (user) {
          return true;
        }
        return this.router.createUrlTree(['/login']);
      }),
      take(1)
    );
  }

  canLoad(route: Route, segments: UrlSegment[]): Observable<boolean> | Promise<boolean> | boolean {
    return this.authService.user.pipe(
      map(user => {
        if (user) {
          return true;
        }
        this.router.navigate(['/']);
        return false;
      }),
      take(1)
    );
  }

我猜这可能发生的另一个原因是因为您使用的是PreloadAllModules 策略。这将立即加载所有模块,但我猜这是在用户尚未登录时完成的,因此守卫将返回 false 并且模块不会加载,也不会重试。

所以你需要重新考虑是否要以这种方式使用canLoad 保护,如果你正在使用PreloadAllModules。也许canActivate 守卫就足够了。

您还可以实现自定义预加载策略,以某种方式等待用户登录以加载它


有了你关于条件路由器出口的附加信息,这是有道理的,因为首先组件将被放置在一个router-outlet中,但在路由完成后,另一个router-outlet变得可见,但什么都没有放在那里:)。您可以通过使用 ngTemplateOutlet 指令来更新您的模板来规避此问题:

<ng-template #outlet>
  <router-outlet ></router-outlet>
</ng-template>

<div *ngIf="!isAuthRoute">
  <div class="row page-wrapper">
    <div class="col-md-3 col-md-pull-9">
      <app-sidebar></app-sidebar>
    </div>
    <div class="col-md-9 col-md-push-3 content-area">
      <ng-component [ngTemplateOutlet]="outlet"></ng-component>
    </div>
  </div>
</div>

<div style="height: 80vh" class="d-flex justify-content-center align-items-center" *ngIf="isAuthRoute">
  <ng-component [ngTemplateOutlet]="outlet"></ng-component>
</div>

这样可以确保在AppComponent 中只放置一个router-outlet,这是应该这样做的方式

【讨论】:

  • 但我使用的是 BehaviorSubject,据我所知,它在调用时运行
  • @TeomanTıngır 你试过我的解决方案了吗? :) 除非您调用 .complete(),否则 BehaviorSubject 也是非完整的 observable
  • 是的,我做到了,没有任何改变,我也抛弃了用户,它提供了用户模型,守卫正常工作,实际上路由器重定向到 url。问题是组件根本不渲染。
  • 当用户登录时,它会成功重定向任何组件,但之后如果我点击任何链接,浏览器会切换到链接但组件不加载
  • 我也试过 canLoad 但没用。但不知何故,我弄清楚了为什么组件不渲染。但还是不知道是什么原因造成的。我会在一分钟内更新我的问题
【解决方案2】:

我看不到您在 AppRoutingModule 中使用 AuthGuard 的位置。

尝试将其应用于任何路线,例如:

{
    path: 'posts',
    canLoad: [AuthGuard],
    loadChildren: () => import('./modules/posts/posts.module').then(m => m.PostsModule),
},

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-08-30
    • 2021-03-05
    • 1970-01-01
    • 2021-09-13
    • 2020-08-26
    • 2020-01-31
    • 1970-01-01
    相关资源
    最近更新 更多