【问题标题】:Angular 2 different components with same routeAngular 2个具有相同路线的不同组件
【发布时间】:2017-06-15 04:28:36
【问题描述】:

我有一个应用程序,它需要将经过身份验证的用户组件和来宾用户组件分开。但我需要,这两个组件都将通过“/”路由加载。我写了

{
    path: 'desktop',
    loadChildren: 'app/member/member.module#MemberModule',
    canActivate: [LoggedInGuard],
},
{
    path: '',
    loadChildren: 'app/guest/guest.module#GuestModule',
    canActivate: [GuestGuard],
},

而且它有效。但是如何使两个组件都通过相同的 url 加载呢? 我曾尝试为成员的模块路由写path: '',但没有执行第二个路由规则。 以下是守卫代码:

LoggedInGuard:

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
    if(this.sessionService.isLoggedIn()) {
        return true;
    } else {
        return false;
    }
}

GuestGuard:

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
    if(!this.sessionService.isLoggedIn()) {
        return true;
    } else {
        return false;
    }
}

这里是一个笨蛋:http://embed.plnkr.co/VaiibEVGE79QU8toWSg6/

我应该如何正确地做到这一点?谢谢

【问题讨论】:

  • 错误是什么?
  • 对不起。没有错误。但只执行了第一个路由器规则
  • 如果没有错误并且满足第一个路由器规则,那么您的 LoggedInGuard 返回 true?
  • 我试图从 LoggedInGuard 返回 false,从 GuestGuard 返回 true。但是第二条规则仍然没有执行
  • 我已经创建了一个 plunker 草图:embed.plnkr.co/VaiibEVGE79QU8toWSg6

标签: angular angular2-routing


【解决方案1】:

所以我终于能够做到这一点。 问题是 Angular 使用优先匹配策略,所以我们需要以保护类型的方式匹配路由,以确保匹配正确的路由和正确的模块。

首先我们需要为我们的路由添加自定义matchers,它只会在我们想要的条件下匹配它们(例如用户类型)。

{
 path: 'samePath',
 matcher: firstMatcher,
 loadChildren: '../first/first.module#FirstModule'
},
{
 path: 'samePath',
 matcher: secondMatcher,
 loadChildren: '../second/second.module#SecondModule'
}

匹配器代码是这样的: 在这里,我从 AppModule 注入了 AuthService 服务,并用它检查了用户类型。所以可以根据用户类型匹配路由。

import { applicationInjector } from '../../main';

export function firstMatcher (url: UrlSegment[]) {
  const auth =  applicationInjector.get(AuthService);
  return auth.isUserType('admin') ? ({consumed: [url[0]]}) : null;
}

现在我们只需要在主模块中创建applicationInjector,这样我们就可以在匹配器函数中注入服务;

export let applicationInjector: Injector;

platformBrowserDynamic().bootstrapModule(AppModule).then((componentRef) => {
  applicationInjector = componentRef.injector;
})

【讨论】:

  • 我想补充一点,使用匹配器时不能指定path,因为它会产生编译器错误;无论如何这都是多余的,因为所有通过path 接收的路径信息都是通过匹配器的url 参数传递的。这个答案拯救了我的一天!
  • 您将在 main.ts 中获得循环依赖注入服务。
  • Anton Stepanenkov,你能解释一下Angular 8中的这个例子吗?您的示例不起作用,applicationInjector 返回 undefined...
【解决方案2】:

您可以使用一个模块来处理应该加载的模块,方法是使用 Angular 的 useFactory 提供程序提供 RouterModule 的 ROUTES。

代码可能是这样的。

// HandlerModule

@NgModule({
  declarations: [],
  imports: [
    CommonModule,
    RouterModule
  ],
  providers: [
    {
      provide: ROUTES,
      useFactory: configHandlerRoutes,
      deps: [SessionService],
      multi: true
    }
  ]
})


export class HandlerModule {}

export function configHandlerRoutes(sessionService: SessionService) {
  let routes: Routes = [];
  if (sessionService.isLoggedIn()) {
    routes = [
      {
        path: '', loadChildren: () => import('app/member/member.module').then(mod => mod.MemberModule)
      }
    ];
  } else {
    routes = [
      {
        path: '', loadChildren: () => import(app/guest/guest.module).then(mod => mod.GuestModule)
      }
    ];
  }
  return routes;
}

那么在您的 AppRoutingModule 中,路径 '' 的模块将成为 HandlerModule:

// AppRoutingModule

 {
    path: '',
    loadChildren: () => import('app/handler/handler.module').then(mod => mod.HandlerModule)
}

在 SessionService 之后,当提供方法 isLoggedIn 的值发生变化时,您必须更新 Router.config,因为应用程序只会加载第一次加载的页面(模块)。这是因为 HandlerModule 中 useFactory 提供程序使用的函数“configHandlerRoutes”仅在我们第一次导航到“”路径时执行,之后 Angular 路由器已经知道他必须加载哪个模块。

在 SessionService 中你必须做的总结:

  export class SessionService {
  private loggedIn: boolean;
  constructor(private router: Router) {
    this.loggedIn = false;
  }

  public isLoggedIn(): boolean {
    return this.loggedIn;
  }

  public setLoggedIn(value: boolean): void {
    const previous = this.loggedIn;
    this.loggedIn = value;
    if (previous === this.loggedIn) {
      return;
    }
    const i = this.router.config.findIndex(x => x.path === '');
    this.router.config.splice(i, 1);
    this.router.config.push(
      {path: '', loadChildren: () => import('app/handler/handler.module').then(mod => mod.HandlerModule)}
    );
  }
}

就是这样。

如果您想要其他参考,请参阅他们使用相同方法的文章: https://medium.com/@german.quinteros/angular-use-the-same-route-path-for-different-modules-or-components-11db75cac455

【讨论】:

  • 非常灵活的解决方案!!
  • 但不知道什么时候启动应用程序它不起作用我必须在那个模块中做一些改变然后它才开始运行
  • 在生成 AOT 构建时遇到此错误“无法读取未定义的属性 'loadChildren' 中的错误”
  • 是的,这是 Angular 8 版本的问题。它在 Angular 9+ 版本中运行良好,检查错误是否已在 repo 中报告并解决:github.com/gquinteros93/same-path-for-different-modules/issues/…
  • 这是我的方案的最佳解决方案(在具有功能标志的页面的新旧版本之间切换)。有一件事让我陷入了困境:HandlerModule (path: '') 中的路由 path 未绑定到 AppRoutingModule 中的路径。对于我的场景,AppRoutingModule 中的路径不是空字符串。但在 HandlerModule 中,它们应该保留为空字符串。
【解决方案3】:

一种方法是根据用户是否登录来路由到适当的区域。

(即无论您打开空白路由还是访客路由,它都会被正确重定向,并且返回按钮不起作用)

路线:

{
    path: '',
    loadChildren: 'app/member/member.module#MemberModule',
    canActivate: [LoggedInGuard],
},
{
    path: 'guest',
    loadChildren: 'app/guest/guest.module#GuestModule',
    canActivate: [GuestGuard],
}

LoggedInGuard:

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
    if(this.sessionService.isLoggedIn()) {
        return true;
    } else {
        // route to 'guest' if not logged in
        this.router.navigate(['/guest'], { replaceUrl: true });
        return false;
    }
}

GuestGuard (登录后自动路由到 MemberComponent)

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
    if(this.sessionService.isLoggedIn()) {
        // route to member area if already logged in
        this.router.navigate(['/'], { replaceUrl: true });
        return false;
    } else {
        return true;
    }
}

【讨论】:

  • OP 希望它们都具有相同的路由:“但我需要,两个组件都将通过 '/' 路由加载。”
  • @echonax 您不能使用相同的路由路径加载不同的模块(即使使用不同的警卫)。唯一的选择是做类似上面的事情,无论打开哪个路径,用户都会被正确重定向。
【解决方案4】:

下面的代码对我有用!!!

路线 = [ { 小路: '', 组件:localStorage.getItem('isVisited')?MainComponent:(()=>{ localStorage.setItem('isVisited','true');return AboutComponent})() } ]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-12-26
    • 1970-01-01
    • 2016-08-30
    • 2017-07-25
    • 2018-05-14
    • 1970-01-01
    • 1970-01-01
    • 2018-10-16
    相关资源
    最近更新 更多