Angular 用单独的模块解决

Angular resolve with separate modules

如何在另一个模块中的子路由上激活 RouteGuard 或 Resolve?

示例场景:

我有一个应用程序,它有许多独立的模块,每个模块都定义了自己的路由。考虑定义以下路由的这些模块:

Module1 -> ['/a', '/b', '/c']
Module2 -> ['/d', '/e', '/f']

现在,我需要确保应用程序中的每个路由都具有以下解析:

resolve: { config: AppConfiguration} 

我们可以使用:

{ path: '',  component: AppComponent, resolve: { config: AppConfiguration}  }

然而这并没有实现 -/ 执行了解析器,但是 /a 没有。

我发现确保路由 /a/b/c 调用解析器的唯一方法是让它们成为根的子节点,如下所示:

AppModule -> [ { path: '', component: 'MainComponent', resolver: {..}, children: [
    ...Module1Routes
    ...Module2Routes
] ]}

但是这样做意味着应用程序不再按照 Angular 文档推荐的方式构建,RouterModule.forChild() 不再在其他模块中使用。

我确定这是一个很常见的用例 - 有更好的方法吗?

由于 AppConfiguration 应该是所有路由共享的单例,所以有两种常见的模式可以做到这一点。

考虑到解析器的一个主要好处是访问从异步解析器解包的数据作为 activatedRoute.snapshot.data.resolverName,一种方法是使用由所有解析器解包的单例服务,例如:

@Injectable()
class Config {
  data: IConfig;
  data$: Observable<IConfig>;

  constructor(private http: Http) {}

  load(forceReload = false) {
    if (!this.data$ || forceReload) {
      this.data$ = this.http.get('...')
      .map(res => res.json())
      .do(data => {
        this.data = data
       })
      .publishReplay(1)
      .refCount();
    }

    return this.data$;
  }
}

load 方法 returns 一个具有缓存服务器响应的可观察对象,在功能上等同于一个承诺。但是由于已知 observables 是路由器本机使用的,因此在这种情况下使用它们是很自然的。

然后所有解析器可以return config.load() 并确保请求只执行一次(除非它被称为config.load(true)):

@Injectable()
class ConfigResolver {
  constructor(private config: Config) {}
  resolve() {
    return this.config.load();
  }
}

...
{ path: ..., component: ... resolve: { config: ConfigResolver } } 

可以编写代码 DRYer 并提供函数或 class 为所有路由添加 config 解析器,但不推荐这样做,因为这种方法与 AoT 不兼容。在这种情况下 WETter 更好。

另一种方法是使用poorly documented APP_INITIALIZER provider, which is explained in detail in 。当路由解析器推迟路由更改时,APP_INITIALIZER 以相反的方式工作并推迟应用程序初始化。

它是多提供者,应该在根注入器中定义:

export function configInitializerFactory(config: Config) {
  return () => config.load();
}

@NgModule({
  ...
  providers: [
    ...
    Config,
    {
      provide: APP_INITIALIZER,
      useFactory: configInitializerFactory,
      deps: [Config],
      multi: true
    }
  ]
})
...

无需涉及解析器,因为数据已在应用程序初始化期间解析。

幸运的是,data$ observable 已经在 Config 中解包为 data,因此解析数据在注入时已经可用 config.data