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
。
如何在另一个模块中的子路由上激活 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
。