【问题标题】:Angular resolve with separate modules使用单独的模块进行角度解析
【发布时间】:2017-05-07 22:21:01
【问题描述】:

如何在另一个模块中的子路由上激活 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() 不再用于其他模块。

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

【问题讨论】:

  • config 是解析器的原因吗?是否应该在每次路由更改时重新实例化?
  • 我有另一种有意义的情况 - 必须更改变量名,因为它是超级机密和东西 :)
  • 另外,如果你有它在根目录中,目标是不要在每次路由更改时重新实例化它,而是在加载根目录时加载一次,(即;当我按 F5 或重新启动时应用程序)
  • 所以从 /a 到 /b 到 /c 会调用一次解析器,但我仍然可以转到 /c,它会调用解析器。将解析器单独附加到每个路由会调用它 3 次。
  • 是的,children 就是这样做的方法。但是,如果您希望它们成为功能模块并使用forChild,那么整个想法就会破裂 - 您将事物与根模块分离,但您尝试通过通用解析器再次耦合它们。没有多大意义。

标签: angular routing angular-routing ng-modules


【解决方案1】:

由于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 方法返回一个带有缓存服务器响应的 observable,它在功能上等同于一个 Promise。但既然已知可观察对象是由路由器原生使用的,那么在这种情况下使用它们是很自然的。

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

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

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

可以使代码 DRYer 并提供一个函数或一个类来为所有路由添加 config 解析器,但不建议这样做,因为这种方法与 AoT 不兼容。在这种情况下,WETter 更好。

另一种方式是使用poorly documented APP_INITIALIZER provider,在this question中有详细说明。当路由解析器推迟路由更改时,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 获得。

【讨论】:

  • @David 是的,APP_INITIALIZER 很适合。我猜您是根据 A1 经验判断的,其中路由解析器是延迟初始化过程的唯一方法。幸运的是,这已在 A2 中修复。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-12
  • 2014-04-20
  • 2016-07-29
  • 2018-08-05
  • 2019-01-03
  • 1970-01-01
相关资源
最近更新 更多