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, то есть асинхронно. Как это сделать?


person Alex JM    schedule 25.09.2016    source источник
comment
Нажмите здесь и найдите конфигурацию маршрута с отложенной загрузкой.   -  person kbtz    schedule 25.09.2016
comment
Ленивая загрузка здесь не поможет. Смотрите мой комментарий к ответу @user32.   -  person Alex JM    schedule 25.09.2016
comment
ты разобрался?   -  person Dmitry Efimenko    schedule 23.02.2017


Ответы (6)


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

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

person Bhushan Gadekar    schedule 04.10.2016
comment
Это имеет большой смысл. Хотя для меня довольно удивительно, что для этого случая нужно делать так много вещей, особенно в низкоуровневой области фреймворка. На данный момент я не могу сделать это принятым ответом, так как я хочу открыть для этого проблему команде angular. Но уверен, что ответ вознаграждает щедрость. - person Alex JM; 05.10.2016
comment
@AlexJ Рад слышать, что это может вам помочь. - person Bhushan Gadekar; 05.10.2016
comment
Я не думаю, что это решение стоит пытаться, так как вам нужно отправить компилятор в браузер, чтобы это сработало, и даже тогда взломать. - person Ashok Koyi; 20.06.2017

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

person manish.nith    schedule 25.09.2016
comment
Извините, я думаю, что я не правильно выразился. Я подчеркнул асинхронность, но моя проблема больше связана с условием. Ленивая загрузка не имеет прямого отношения к моей проблеме. Моя проблема заключается в том, как загрузить тот или иной компонент (или тот или иной модуль) в зависимости от условия, которое может быть асинхронным (скажем, условием является роль пользователя, которая исходит из API) при загрузке приложения. Я обновил свой вопрос, сделав его более понятным. Любой способ решить это? - person Alex JM; 25.09.2016

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

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

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

person tom    schedule 30.09.2016
comment
Это звучит интересно и имеет смысл. Я постараюсь вернуться сюда! Пока я оставляю тему открытой, чтобы приветствовать другие ответы. - person Alex JM; 01.10.2016

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

<user-dashboard *ngIf="role==='user'"></user-dashboard>
<admin-dashboard *ngIf="role==='admin'"></admin-dashboard>
person jovani    schedule 14.10.2016

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

person luxury    schedule 25.09.2016
comment
В этом случае URL-адрес не будет таким же. - person manish.nith; 25.09.2016
comment
В яблочко. То, что я хочу (по крайней мере, сначала), - это иметь тот же URL-адрес, скажем, /dashboard, и в зависимости от того, является ли это пользователем или администратором, чтобы показать UserDashboard или AdminDashboard. Возможно, это не лучший подход, но я хочу избежать разных URL-адресов для этих случаев или, что еще хуже, заполнять компоненты всеми видами условий {{*ngIf}}.... - person Alex JM; 26.09.2016

Очень хорошим решением может быть «Использовать защиту авторизации». Вы можете попробовать реализовать интерфейсы 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
  }
}
person srikanth_k    schedule 25.05.2017
comment
Если использовать его с некоторым смыслом, метод, описанный в этом ответе, может работать. Я не понимаю минус. - person retr0; 10.12.2018