Angular 2 (Final):resetConfig如何将路由添加到惰性加载的路由中

5

我正在尝试理解如何动态添加从延迟加载模块中的路由。 我有核心应用程序,并具有以下路由:

export const routes = [{
    path: "",
    component: HomeComponent
}, {
    path: "moduleA",
    loadChildren: "app/moduleA/A.module"
}];

在A.module中,我从"local"模块的moduleA.routes.config.ts导入路由。
const routes = [{
    path: "",
    component: ModuleAHomeComponent,
    children: [
        { path: "", component: ModuleAListComponent },
        { path: ":id", component: ModuleADetailComponent }
    ]
}];
export const routing = RouterModule.forChild(routes);

到目前为止一切都很好。现在,在ModuleADetailComponent中,我有一个“附加模块”的列表,我将其称为我的Web应用程序的插件,它们是分别开发的,我希望能够在ModuleADetailComponent页面中显示。 因此,这个组件的模板大致如下:

<div *ngFor="let plugin of plugins">
    <div class="header">{{ plugin.name }}</div>
    <router-outlet name="*plugin.name*">*LOAD MODULE HERE*</router-outlet>
</div>

当然,插件是在导航到ModuleADetailComponent时加载的配置,因此我无法预先知道配置了哪些模块以及它们的数量。 最后,这些插件中的每一个都可以是一个NgModule(在工作台环境中单独开发)。
我需要的是:如何动态修改路由器配置,以将子项添加到moduleA.routes.config.ts中定义的路由中? 例如,如果我有2个插件,我希望使用router.resetConfig来动态地获得与moduleA.routes.config.ts相同的效果。
const routes = [{
    path: "",
    component: ModuleAHomeComponent,
    children: [
        { path: "", component: ModuleAListComponent },
        { 
            path: ":id",
            component: ModuleADetailComponent,
            children: [
                {
                    path: "plugin1",
                    loadChildren: "app/plugins/plugin1.module",
                    outlet: "plugin1"
                },
                {
                    path: "plugin2",
                    loadChildren: "app/plugins/plugin2.module",
                    outlet: "plugin2"
                }                
            ]
        }
    ]
}];
export const routing = RouterModule.forChild(routes);

我希望我能实现它,否则插件将不得不在没有路由的情况下开发...但它们可以成为大的子模块,从中受益。

编辑 27/09/2016:我已经组装了这个 plunkr:http://plnkr.co/edit/6aV5n5K5wdqdiYo49lZv。 目前我能做到的是动态加载组件并将它们添加到应用程序中(请查看“Dynamic Modules”选项卡)。 我还使用了多个命名路由出口,尽管没有找到任何官方文档(请查看“Dynamic Routed”选项卡)。尽管选项卡名称如此,但路由在提前知道。

我的目标是:动态加载配置(DynamicRoutesComponent 中的 dynamicRoutes 数组),并在每个动态路由上创建一个路由器出口,并使用 resetConfig(或其他必要方法)来获得此配置:

{
  path: "dynamic-routed",
  component: DynamicRoutesComponent,
  children: [
    { path: "", component: EmptyComponent },
    { path: "component1", component: Component1, outlet: "component1" },
    { path: "component2", component: Component2, outlet: "component2" }
  ]
}

每个动态路由只对应一个子页面。

提前感谢您!

3个回答

9

首先,loadChildren 属性方法必须提供以下格式(https://angular.io/docs/ts/latest/guide/router.html#!#asynchronous-routing):

{
  path: 'admin',
  loadChildren: 'app/admin/admin.module#AdminModule',
},

正如您在上面看到的那样,模块路径模块名称使用#字符连接起来。
在您的情况下,您需要更改您的app.routes,大致如下(不确定您的模块/文件名,而且plunkr示例并不反映本问题中给出的名称):
export const routes = [{
    path: "",
    component: HomeComponent
}, {
    path: "moduleA",
    loadChildren: "app/moduleA#ModuleA"
}];

这只是一个简介,现在让我们继续解决您的问题。据我所读,您想动态添加子路由。
您可以使用以下代码在功能模块中动态提供子路由。
import {ROUTES} from '@angular/router/src/router_config_loader';
import {ANALYZE_FOR_ENTRY_COMPONENTS} from '@angular/core';

[...]
let routesToAdd = ... // provide your routes here

@NgModule({
  [...]
  imports: [RouterModule.forChild([])] // Empty on purpose
  [...]
  providers: [
    {
      provide: ROUTES,
      useValue: routesToAdd,
      multi: true
    },
    {
      provide: ANALYZE_FOR_ENTRY_COMPONENTS,
      useValue: routesToAdd, // provide them here too
      multi: true
    }
  ],
  [...]
})

你需要为ANALYZE_FOR_ENTRY_COMPONENTS令牌(以及ROUTES令牌)提供相同的路由,否则你将会得到No component factory found for YOUR_COMPONENT. Did you add it to @NgModule.entryComponents?错误提示。

然而,在AoT编译期间,这段代码可能会产生错误。在这种情况下,你需要为ROUTES令牌提供一个factory而不是value。然而,你将无法为ANALYZE_FOR_ENTRY_COMPONENTS令牌提供一个factory - 它只接受useValue(请参见https://github.com/angular/angular/blob/03e855ae8f9e9b86251320c7551a96092bd8b0c4/modules/%40angular/compiler/src/metadata_resolver.ts#L926)。

在这种情况下,请在你的特性模块中使用以下代码:

import {ROUTES} from '@angular/router/src/router_config_loader';

[...]
let routesToAdd = ... // provide your routes here

@NgModule({
  [...]
  imports: [RouterModule.forChild([])] // Empty on purpose
  [...]
  providers: [
    {
      provide: ROUTES,
      useFactory: (getRoutesFromSomewhere),
      //deps: [SomeService, SOME_TOKEN] // deps is optional, in the case you need no params - delete it; otherwise pass 'em
      multi: true
    }
  ],
  entryComponents: [CompA, CompB, CompC, etc...]
  [...]
})
注意: 使用Observables/Promises提供路由不是一个可靠的解决方案,因此Angular路由器期望Route[]Routes,但HTTP请求只能返回Observable/Promise(Angular应用程序已初始化,但仍然使用Observables/Promises进行路由检索过程)(请参见https://github.com/ngx-i18n-router/config-loader#readme)。
请使用返回Route[]Routes的函数。
同时,您可以检查@ngx-i18n-router/core模块文件(Angular的国际化实用程序,受益于动态路由)以及其示例应用程序的实现。

1
非常感谢您的详细回答,当我回到那个项目时,我一定会尝试并让您知道是否有效,不幸的是现在我已经开始着手其他事情了! - Etchelon
我无法让代码运行。出现 NavigationError {id: 4, url: "/products", error: ZoneAwareError} 的错误。 这是特征模块的代码: Object @NgModule({ imports: [ SharedModule, RouterModule.forChild([]) ], declarations: [ ProductListComponent, ProductDetailComponent, ProductEditComponent, ], providers: [ ProductService, ProductResolver, { provide: ROUTES, useValue: _routes, multi: true }, { provide: ANALYZE_FOR_ENTRY_COMPONENTS, useValue: _routes, multi: true } ] }) - bomaboom

1

你能像这样做吗 this?基本上,在组件的构造函数中,你可以访问 activatedRoute.routeConfig.children,通过它,你应该能够向该数组添加更多的子元素。

如果你正在尝试从另一个数据源获取数据,则应该能够在 Observablesubscriber 部分设置这个。我想的是这样的:

@Component({
  selector: 'app-module-a-detail',
  templateUrl: './module-a-detail.component.html'
})
export class ModuleADetailComponent {
  constructor(
    private activatedRoute: ActivatedRoute) {

    const childRoutes: Routes = this.activatedRoute.routeConfig.children;

    this.pluginService.getPlugins({
      .subscribe(map(plugin => {
        // build up my additional childRoute here
        childRoutes.push(newChildRoute);
      }))
    );
  }
}

0

实际上,对我来说问题是如何构建newChildRoute

以前我的代码大致是这样的:

         "path": pathFromFile,
         "outlet": outletFromFile,
         "data": dataFromFile,
         "component": HostComponent, (my custom host component for named outlet)
         "loadChildren": moduleURLFromFile + "#" + moduleNameFromFile

创建新对象 {} 来更改我的实际 this.router.config

这样调用 this.router.resetConfig(this.router.config); 应该可以工作....

但是将 "loadChildren": moduleURLFromFile + "#" + moduleNameFromFile

替换为

"loadChildren": () => import(moduleURLFromFile ).then(m => m.moduleNameFromFile)

似乎不起作用。

希望这样更清楚明白。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接