Угловое разрешение с отдельными модулями
Как я могу активировать 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()
больше не используется в других модулях.
Я уверен, что это довольно распространенный вариант использования - есть ли лучший способ?
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
Метод возвращает наблюдаемый с кэшированным ответом сервера, который функционально равен обещанию. Но так как известно, что наблюдаемые объекты изначально используются маршрутизатором, естественно использовать их в этом контексте.
Тогда все решатели могут вернуться 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 лучше.
Другой способ - использовать плохо документированные APP_INITIALIZER
провайдер, который подробно объясняется в этом вопросе. Пока преобразователи маршрутов откладывают изменение маршрута, APP_INITIALIZER
работает в обратном направлении и откладывает инициализацию приложения.
Это мульти-провайдер и должен быть определен в корневой инжектор:
export function configInitializerFactory(config: Config) {
return () => config.load();
}
@NgModule({
...
providers: [
...
Config,
{
provide: APP_INITIALIZER,
useFactory: configInitializerFactory,
deps: [Config],
multi: true
}
]
})
...
Не нужно задействовать преобразователи, потому что данные уже были разрешены во время инициализации приложения.
К счастью, data$
наблюдаемый уже разворачивается в data
в Config
Таким образом, разрешенные данные уже доступны для инъекций как config.data
,