Angular 2: Как условно загрузить компонент в маршруте асинхронно?

Я хочу привязать компонент к асинхронному маршруту, с условием.

Следующий пример, который работает (но является асинхронным), загружает один или несколько компонентов в зависимости от роли пользователя:

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

Angular 2 поддерживает ленивую загрузку на уровне модуля. Функциональные модули загружаются асинхронно, а не компонент. Вы можете создать этот компонентный модуль. https://angular.io/docs/ts/latest/guide/ngmodule.html

Ответ 2

Вы можете создать 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);
  }
}

Надеюсь, что это поможет.

Ответ 3

Вы можете просто определить компонент более высокого уровня f.e. DashboardComponent, который включен в маршрут/панели мониторинга

Затем родительский компонент должен загружать дочерние элементы в свой шаблон на основе асинхронного условия.

Таким образом вам также понадобится использовать * ngIf, но, по крайней мере, не загромождать шаблоны дочерних компонентов.

Ответ 4

Поскольку вы будете использовать один маршрут для одного из компонентов, одно решение создает другой компонент для этого маршрута, тогда его шаблон может ссылаться на оба компонента, но с ngIf, как показано ниже:

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

Ответ 5

Я советую вам использовать навигацию по программному обеспечению с помощью метода навигации маршрутизатора. Таким образом, вы перечисляете все возможные маршруты в файле маршрутизатора. Затем в своем компоненте вы вызываете router.navigate() на основе конкретного случая.

Ответ 7

Очень хорошим решением может быть "Use 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
  }
}