基于租户(域名主机名)的角度应用程序在启动时的延迟加载路由

时间:2019-12-13 07:42:25

标签: angular angular8

我需要在启动时基于主机名在Angular应用程序中使用单独的路由。为此,我创建了一个单独的服务TenantService以获取租户。接下来,我创建了另一个名为AppRoutingService的服务,该服务具有根据TenantService的响应创建路由的功能。该功能需要在应用程序启动时执行,并相应地编辑应用程序路由。

以下是我收到的错误,接下来是代码段:

Getting cyclic dep error
compiler.js:10477 Uncaught Error: Provider parse errors:
Cannot instantiate cyclic dependency! ApplicationRef ("[ERROR ->]"): in NgModule AppModule in ./AppModule@-1:-1
    at NgModuleProviderAnalyzer.parse (compiler.js:10477)
    at NgModuleCompiler.compile (compiler.js:20607)
    at JitCompiler._compileModule (compiler.js:25606)
    at compiler.js:25567
    at Object.then (compiler.js:2166)
    at JitCompiler._compileModuleAndComponents (compiler.js:25565)
    at JitCompiler.compileModuleAsync (compiler.js:25527)
    at CompilerImpl.compileModuleAsync (platform-browser-dynamic.js:216)
    at compileNgModuleFactory__PRE_R3__ (core.js:34647)
    at PlatformRef.bootstrapModule (core.js:34956)

代码如下:

AppModule

import { BrowserModule } from '@angular/platform-browser';
import { NgModule, APP_INITIALIZER } from '@angular/core';
import { RouterModule } from "@angular/router";

import { AppComponent } from './app.component';
import { TenantService } from './tenant/tenant.service';
import {AppRoutingService} from './app-routing.service';

export function initSettings(appRoutingService: AppRoutingService) {
  return () => appRoutingService.initializeRoutes();
}

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    RouterModule.forRoot([])
  ],
  providers: [TenantService,
    AppRoutingService,
  {
    provide : APP_INITIALIZER,
    useFactory : initSettings,
    deps : [AppRoutingService],
    multi : true
  }],
  bootstrap: [AppComponent]
})
export class AppModule { }

AppRoutingService

import { Injectable } from '@angular/core';
import {TenantService, Tenant } from './tenant/tenant.service';
import { Routes, Router } from '@angular/router';

import { from } from 'rxjs';
@Injectable({
  providedIn: 'root'
})
export class AppRoutingService {

  constructor(private router: Router, private tenantService: TenantService) { }

  initializeRoutes(): Promise<any> {
    return new Promise((resolve, reject) => {
      let appRoutes: Routes = [];
      if (this.tenantService.getTenant() === Tenant.CLIENT1) {
        console.log('Configuring CLIENT1 Login Components.');
        appRoutes = [
          { path: 'hotel-list', loadChildren: () => import('./hotel/hotel.module').then(m => m.HotelModule) },
          { path: '', redirectTo: 'hotel-list', pathMatch: 'full'}
        ];
        appRoutes.forEach(e => this.router.config.unshift(e));
        resolve(true);
      } else if (this.tenantService.getTenant() === Tenant.CLIENT2) {
        console.log('Configuring CLIENT2 Login Components.');
        appRoutes = [
          { path: 'natgeo-list', loadChildren: () => import('./natgeo/natgeo.module').then(m => m.NatgeoModule) },
          { path: '', redirectTo: 'natgeo-list', pathMatch: 'full'}
        ];
        appRoutes.forEach(e => this.router.config.unshift(e));
        resolve(true);
      } else {
        reject(false);
      }
    });
  }
}

TenantService

import { Injectable } from '@angular/core';
import { HttpHeaders } from '@angular/common/http';

@Injectable({
  providedIn: 'root'
})
export class TenantService {
  constructor() {}

  getTenantForHostname(hostname: string): Tenant {
    return this.getTenantForHost(hostname.split('.')[0]);    
  }

  getTenantForString(s: string) {
    for (const e in Tenant) {
      if (e.toLowerCase() === s.toLowerCase()) {
        return Tenant[e] as Tenant;
      }
    }
    return null;
  }

  getTenantForHost(host: string): Tenant {
    return this.getTenantForString(host);
  }

  getTenant(): Tenant {
    return this.getTenantForHostname(location.hostname);
  }

  addTenantToHeaders(headers: HttpHeaders): HttpHeaders {
    return headers.append('X-Tenant-ID', this.getTenant());
  }
}

export enum Tenant {
  CLIENT1 = 'hotel',
  CLIENT2 = 'natgeo'
}

感谢您为纠正错误提供的任何帮助。另外,也欢迎您使用一种替代方法来满足我的要求。

1 个答案:

答案 0 :(得分:2)

只需使用Injector来获取Router并将其重置。

Stackblitz:https://stackblitz.com/github/hsuanxyz/ng-dynamic-routes-config

Github存储库https://github.com/hsuanxyz/ng-dynamic-routes-config

tenant.service.ts

import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class TenantService {

  getTenant() {
    return true;
  }
}

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { APP_INITIALIZER, Injector, NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { TenantService } from './tenant.service';
import { Router } from '@angular/router';

export function initSettings(injector: Injector, tenantService: TenantService) {
  return () => new Promise(resolve => {
    let routes = [];
    if (tenantService.getTenant()) {
      routes = [
        { path: 'hotel-list', loadChildren: () => import('./hotel/hotel.module').then(m => m.HotelModule) },
        { path: '', redirectTo: 'hotel-list', pathMatch: 'full' }
      ];
    } else {
      routes = [
        { path: 'natgeo-list', loadChildren: () => import('./natgeo/natgeo.module').then(m => m.NatgeoModule) },
        { path: '', redirectTo: 'natgeo-list', pathMatch: 'full' }
      ];
    }
    const router: Router = injector.get(Router);
    router.resetConfig(routes);
    resolve();
  });
}

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule
  ],
  providers: [
    TenantService,
    {
      provide : APP_INITIALIZER,
      useFactory : initSettings,
      deps : [Injector, TenantService],
      multi : true
    }
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }