Angular 2:如何在路由中异步地有条件地加载组件?

23
我希望能够异步地将组件附加到路由上,考虑到某些条件。以下示例是一个异步的工作示例,根据用户角色加载一个组件或另一个组件。
import { UserDashboardComponent }  from './user-dashboard.component'
import { AdminDashboardComponent } from './admin-dashboard.component'

const role = 'admin' // Just for the example
const comp = role === 'admin' ? AdminDashboardComponent : UserDashboardComponent

const routes: Routes = [
  { path: '', component: comp },
]

但是,假设我们想要从API中异步检索角色。如何完成此操作?


1
请点击这里,然后搜索“懒加载路由配置”相关内容。 - kbtz
这里懒加载没有帮助。请看我的评论,针对@user32的回答。 - Alex JM
你解决了吗? - Dmitry Efimenko
6个回答

9

您可以创建一个通用的`module.ts`文件,将这两个组件都添加到`declarations`数组中:

import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { UserDashboardComponent }  from './user-dashboard.component'
import { AdminDashboardComponent } from './admin-dashboard.component    

@NgModule({
  imports: [ BrowserModule ],
  declarations: [ UserDashboardComponent,AdminDashboardComponent ]
})
export class GenericModule { }

这样,您将拥有一个包含您想要加载的模块的模块。接下来的步骤是使用编译器异步加载它们: 在您的组件中执行以下操作:
import {GenericModule} from './generic.module';
import { Component, Input,ViewContainerRef, Compiler, NgModule,ModuleWithComponentFactories,OnInit,ViewChild} from '@angular/core';
@Component({
  selector: 'generic',
  template: '<div #target></div>'
})
export class App implements AfterViewInit {
  @ViewChild('target', {read: ViewContainerRef}) target: ViewContainerRef;

  constructor(private compiler: Compiler) {}

  ngAfterViewInit() {
    this.createComponent('<u>Example template...</u>');
  }

  private createComponent(template: string,role:string) {
    @Component({template: template});
    const mod = this.compiler.compileModuleAndAllComponentsSync(GenericModule);
    const factory = mod.componentFactories.find((comp) =>
    //you can add your comparison condition here to load the component
    //for eg. comp.selector===role where role='admin'
    );
    const component = this.target.createComponent(factory);
  }
}

希望这有所帮助。

1
这很有道理。虽然,对我来说,为这种情况做这么多事情,特别是在低级框架范围内,仍然很惊讶。目前我不能接受这个答案,因为我想向Angular团队提出一个问题。但是肯定会奖励这个答案。 - Alex JM
@AlexJ 很高兴听到这可以帮助到你。 - Bhushan Gadekar
1
我认为这不是值得尝试的解决方案,因为您需要将编译器发送到浏览器才能使其工作,即使这样做也很麻烦。 - Ashok Koyi

8

3
抱歉,我想我没有正确表达自己。我强调了“异步”,但是我的问题更多地涉及“条件”。惰性加载与我的问题不是“直接”相关的。我的问题是如何根据可能是异步的条件(比如说,条件是来自 API 的用户角色)来加载一个组件或另一个组件(或一个模块或另一个模块),当应用程序加载时。我已经更新了我的问题,使它更清晰明了。有解决这个问题的办法吗? - Alex JM

4
你可以定义一个更高层次的组件,比如DashboardComponent,它会在路由/dashboards中被包含。
然后,父组件应该根据异步条件在其模板中加载子组件。
这样,你也需要使用*ngIf,但至少不会使子组件模板杂乱无章。

这听起来很有意思,也很有道理。我会尝试并回来这里!现在我把线程保持开放以欢迎其他回答。 - Alex JM

4

由于您将为任何组件使用一个路由,因此解决方案是创建另一个组件用于该路由,然后其模板可以引用两个组件,但类似于以下的ngIf:

<user-dashboard *ngIf="role==='user'"></user-dashboard>
<admin-dashboard *ngIf="role==='admin'"></admin-dashboard>

1

我建议您利用路由器的navigate方法通过编程方式进行导航。因此,您需要在路由文件中列出所有可能的路由。然后,在您的组件中,根据特定情况调用router.navigate()。


1
在这种情况下,URL 将不会相同。 - Manish
没错。我想要的(至少最初)是拥有相同的URL,比如/dashboard,并根据是用户还是管理员来显示UserDashboard或AdminDashboard。也许这不是最好的方法,但我想避免为这些情况使用不同的URL,甚至更糟糕的是在组件中填充各种{{*ngIf}}条件。 - Alex JM

0
非常好的解决方案可以是“使用 auth-guard”。您可以尝试实现接口 CanActivate/CanLoad,并在该类中放置您的条件。
根据条件,路由被激活。
例子:
import { Injectable }       from '@angular/core';
import {
  CanActivate,
  ActivatedRouteSnapshot,
  RouterStateSnapshot
}                           from '@angular/router';
import { Observable } from 'rxjs/Observable';
import { Router } from '@angular/router';

@Injectable()
export class AuthorizationGuard implements CanActivate {
  constructor() {}

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): 
       Observable<boolean> {
    let url: string = state.url;
    return this.canbeLoaded(url);
  }

  canbeLoaded(url: string): Observable<boolean> {
    return Observable.of(true);  //put your condition here
  }
}

如果有一些技巧运用,这个回答中所描述的方法是可行的。我不明白为什么会被踩。 - retr0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接