Angular 5 在启动时根据 API 数据构建路线

Ale*_*iev 2 angular angular-router

我需要根据从 API(站点地图)接收的数据在 Angular 应用程序启动时创建路由。\n1. APP_INITIALIZER 调用 SettingsService.loadSettings 从 API 获取数据\n2。数据到达 ROUTES 并构建所需的路线。

\n\n

应用程序模块.ts

\n\n
import { BrowserModule } from \'@angular/platform-browser\';\nimport { NgModule, APP_INITIALIZER } from \'@angular/core\';\nimport { HttpClientModule } from \'@angular/common/http\';\nimport { RouterModule, ROUTES } from \'@angular/router\';\n\nimport { AppComponent } from \'./app.component\';\nimport { MuseumComponent } from \'./museum/museum.component\';\nimport { SettingsService } from \'./services/settings.service\';\n\nexport function initSettings(settings: SettingsService) {\n  return () => settings.loadSettings();\n}\n\nexport function buildRoutes(settings: SettingsService) {\n  console.log(\'SettingsService.currentSettings\', settings.currentSettings);\n  const routes = [];\n  settings.currentSettings.site_map.forEach(element => {\n   routes.push({\n     path: `/${element.url}`,\n     component: MuseumComponent\n   });\n  });\n  routes.push({\n   path: \'**\',\n   component: AppComponent\n  });\n  console.log(routes);\n  return routes;\n}\n\n@NgModule({\n  declarations: [\n    AppComponent,\n    MuseumComponent\n  ],\n  imports: [\n    BrowserModule,\n    HttpClientModule,\n    RouterModule.forRoot([])\n  ],\n  providers: [\n    SettingsService,\n    {\n      \'provide\': APP_INITIALIZER,\n      \'useFactory\': initSettings,\n      \'deps\': [SettingsService],\n      \'multi\': true\n    },\n    {\n      \'provide\': ROUTES,\n      \'multi\': true,\n      \'useFactory\': buildRoutes,\n      \'deps\': [SettingsService]},\n  ],\n  bootstrap: [AppComponent]\n})\nexport class AppModule { }\n
Run Code Online (Sandbox Code Playgroud)\n\n

设置.service.ts

\n\n
import { Injectable } from \'@angular/core\';\nimport { HttpClient, HttpHeaders } from \'@angular/common/http\';\n\nimport { Config } from \'./../config\';\n\n@Injectable()\nexport class SettingsService {\n\n  private apiUrl = Config.apiUrl;\n\n  public currentSettings;\n\n  constructor(\n    private http: HttpClient\n  ) { }\n\n  loadSettings(): Promise<any> {\n    return new Promise ((resolve, reject) => {\n      const url = this.apiUrl + `/settings`;\n      this.http.get(url)\n      .subscribe(\n        response => {\n          console.log(\'API answer: \', response);\n          this.currentSettings = response;\n          resolve(true);\n        },\n        err => {\n          console.log(\'Server error: \' + JSON.stringify(err));\n          reject(false);\n        }\n      );\n    });\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

我在控制台上收到错误:

\n\n
SettingsService.currentSettings undefined\nmain.ts:12 TypeError: Cannot read property \'site_map\' of undefined\n
Run Code Online (Sandbox Code Playgroud)\n\n

如果我这样做:

\n\n

应用程序模块.ts

\n\n
export function buildRoutes(settings: SettingsService) {\n  console.log(\'SettingsService.currentSettings\', settings.currentSettings);\n  const routes = [];\n  /*settings.currentSettings.site_map.forEach(element => {\n   routes.push({\n     path: `/${element.url}`,\n     component: MuseumComponent\n   });\n  });*/\n  routes.push({\n   path: \'**\',\n   component: AppComponent\n  });\n  console.log(routes);\n  return routes;\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

我收到:

\n\n
SettingsService.currentSettings undefined\napp.module.ts:27 [{\xe2\x80\xa6}]\n0: {path: "**", component: \xc6\x92} length: 1 __proto__: Array(0)\nsettings.service.ts:23 API answer:  {languages: Array(2), entity_types: \nArray(5), site_map: Array(2)}\ncore.js:3688 Angular is running in the development mode. Call \nenableProdMode() to enable the production mode.\n
Run Code Online (Sandbox Code Playgroud)\n\n

看起来,ROUTES 发生在 APP_INITIALIZER 之前。为什么会这样?

\n

Ale*_*iev 5

我使用了错误的方法,解决方案是在这里建立的Angular。使用 APP_INITIALIZER 时路由器 DI 不工作

正确的代码:

应用程序模块.ts

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

import { AppComponent } from './app.component';
import { MuseumComponent } from './museum/museum.component';
import { SettingsService } from './services/settings.service';
import { ApiService } from './services/api.service';

export function initSettings(settings: SettingsService) {
 return () => settings.loadSettings();
}

@NgModule({
 declarations: [
  AppComponent,
  MuseumComponent
 ],
 imports: [
  BrowserModule,
  HttpClientModule,
  RouterModule.forRoot([])
 ],
 entryComponents: [
  MuseumComponent
 ],
 providers: [
  SettingsService,
  ApiService,
  {
   'provide': APP_INITIALIZER,
   'useFactory': initSettings,
   'deps': [SettingsService],
   'multi': true,
  }
],
bootstrap: [AppComponent]
})
export class AppModule { }
Run Code Online (Sandbox Code Playgroud)

设置.service.ts

import { Injectable, Injector } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Router } from '@angular/router';

import { ApiService } from './api.service';

import { MuseumComponent } from '../museum/museum.component';

@Injectable()
export class SettingsService {

 currentSettings: any;

 constructor(
  private injector: Injector,
  private api: ApiService
 ) {   }

loadSettings(): Promise<any> {
 return new Promise((resolve, reject) => {
  setTimeout(() => {
      const router = this.injector.get(Router);
      console.log(router);
      this.api.getSettings()
      .subscribe(
        response => {
          this.currentSettings = response;
          this.currentSettings.site_map.forEach(element => {
            router.config.push({ path: `${element.url}`, component: MuseumComponent });
          });
          resolve(true);
        },
        err => {
          console.log(err);
          reject(false);
        }
      );
   });
  });
 }
}
Run Code Online (Sandbox Code Playgroud)

api.service.ts

import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';

import { Config } from './../config';

@Injectable()
export class ApiService {

private apiUrl = Config.apiUrl;

constructor(
 private http: HttpClient
) { }

 public getSettings(): Observable<any> {
  const url = this.apiUrl + `/settings`;
  return this.http.get(url);
 }
}
Run Code Online (Sandbox Code Playgroud)