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

Angular 角度2:如何有条件地异步加载路由中的组件?,angular,typescript,angular2-router,Angular,Typescript,Angular2 Router,我希望在给定条件的情况下,将组件异步连接到路由 以下示例有效(但异步),根据用户角色加载一个或另一个组件: import { UserDashboardComponent } from './user-dashboard.component' import { AdminDashboardComponent } from './admin-dashboard.component' const role = 'admin' // Just for the example const comp

我希望在给定条件的情况下,将组件异步连接到路由

以下示例有效(但异步),根据用户角色加载一个或另一个组件:

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中检索角色,因此是异步的。实现这一点的方法是什么?

Angular 2支持模块级的延迟加载。功能模块是异步加载的,而不是组件。 您可以创建该组件功能模块。

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

您只需定义一个更高级别的组件,例如仪表板组件,该组件包含在管线/仪表板中

然后,父组件将根据异步条件在其模板中加载子组件


这样,您还需要使用*ngIf,但至少不会使子组件模板混乱。

您可以创建一个generic.module.ts,它将在声明数组中包含两个组件:

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);
  }
}
从“/generic.module”导入{GenericModule};
从“@angular/core”导入{Component,Input,ViewContainerRef,Compiler,NgModule,moduleWithComponentFactorys,OnInit,ViewChild};
@组成部分({
选择器:“通用”,
模板:“”
})
导出类应用程序实现AfterViewInit{
@ViewChild('target',{read:ViewContainerRef})target:ViewContainerRef;
构造函数(专用编译器:编译器){}
ngAfterViewInit(){
这个.createComponent('Example template…');
}
私有createComponent(模板:字符串,角色:字符串){
@组件({template:template});
const mod=this.compiler.compileModule和AllComponentsSync(GenericModule);
常量工厂=mod.componentFactories.find((comp)=>
//您可以在此处添加比较条件以加载组件
//例如,公司选择器===角色,其中角色==管理员'
);
const component=this.target.createComponent(工厂);
}
}

希望这有帮助。

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

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

非常好的解决方案可能是“使用身份验证保护”。您可以尝试实现接口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
  }
}
从'@angular/core'导入{Injectable};
进口{
激活,
激活的路由快照,
路由器状态快照
}来自“@angular/router”;
从“rxjs/Observable”导入{Observable};
从'@angular/Router'导入{Router};
@可注射()
导出类AuthorizationGuard实现CanActivate{
构造函数(){}
canActivate(路由:ActivatedRouteSnapshot,状态:RouterStateSnashot):
可观察{
让url:string=state.url;
返回此.canbeLoaded(url);
}
canbeLoaded(url:string):可观察{
return Observable.of(true);//将您的条件放在这里
}
}

并在此处搜索“延迟加载路由配置”延迟加载没有帮助。请看我对@user32 answer的评论。你明白了吗?对不起,我想我没有正确地表达自己。我强调了“异步”,但我的问题更多的是关于“条件”。延迟加载与我的问题没有“直接”关系。我的问题是,在应用程序加载时,如何根据可能是异步的条件(比如,条件是来自API的用户角色)加载一个或另一个组件(或一个或另一个模块)。我更新了我的问题,使之更清楚。有没有办法解决这个问题?如果是那样的话,url就不一样了。我想要的(至少一开始)是有相同的url,比如说/dashboard,并且取决于显示UserDashboard或AdminDashboard的是用户还是管理员。也许这不是最好的方法,但我想避免在这种情况下使用不同的url,或者更糟糕的是,用各种{{*ngIf}}条件填充组件……这听起来很有趣,也很有意义。我会试着回到这里!现在,为了欢迎其他的回答,我把话题留着,这很有道理。不过,对于这种情况,我们不得不做很多事情,特别是在底层框架范围内,这让我非常惊讶。现在我不能让这成为公认的答案,因为我想就此向angular团队提出一个问题。但答案肯定会奖励赏金。@AlexJ很高兴听到这对你有帮助。我认为这是一个不值得尝试的解决方案,因为你需要将编译器发送到浏览器才能工作&即使是hacky。如果使用得当,此答案中描述的方法也可以工作。我不明白投反对票的原因。