Служба не является одиночной для ленивой загрузки маршрутизатора angular2 с помощью loadChildren
Вот плукер. https://plnkr.co/edit/tsNlmRth4mRzz0svGWLK?p=preview
В котором я создал два модуля с двумя компонентами и один сервис каждый. Я хочу, чтобы сервис был одноэлементным (сохранение состояния) на уровне модуля.
Если вы нажмете "Модуль 1 Страница 1" и "Модуль 2 Страница 2", отобразятся два разных случайных числа. Как я генерирую это число в конструкторе. Таким образом, сервис создается каждый раз при смене страницы. Но модуль2 ведет себя отлично.
Примечание:
Mode1Module использует loadChildren
Mode2Module использует children
Я обнаружил, что проблема такого же типа была исправлена ранее в соответствии с этим синглтоном службы маршрутизатора angular2 rc5
Но я думаю, что все еще там. Поэтому, пожалуйста, помогите мне решить это.
3 ответа
Ленивые загруженные модули имеют собственную корневую область. Поставщики, добавленные в лениво загруженные модули, получают экземпляр в этой корневой области вместо корневой области приложения. Если вы добавите провайдера в модуль, который не загружен с отложенной загрузкой, будет создан только один экземпляр в корневой области приложения.
https://angular.io/docs/ts/latest/cookbook/ngmodule-faq.html
Почему сервис, предоставляемый в загруженном модуле, виден только этому модулю?
В отличие от провайдеров модулей, загруженных при запуске, провайдеры лениво загруженных модулей имеют область действия модулей.Когда Angular-маршрутизатор лениво загружает модуль, он создает новый контекст выполнения. Этот контекст имеет свой собственный инжектор, который является прямым потомком инжектора приложения.
Маршрутизатор добавляет к этому дочернему инжектору поставщиков ленивых модулей и поставщиков импортированных модулей.
Эти поставщики защищены от изменений поставщиков приложений с помощью одного и того же токена поиска. Когда маршрутизатор создает компонент в ленивом загруженном контексте, Angular предпочитает экземпляры службы, созданные из этих поставщиков, экземплярам службы корневого инжектора приложения.
https://angular.io/docs/ts/latest/cookbook/ngmodule-faq.html
Почему плохо, если SharedModule предоставляет сервис для лениво загруженного модуля?
Этот вопрос возник в главе "Угловой модуль", когда мы обсуждали важность недопущения поставщиков в SharedModule.Предположим, мы перечислили UserService в поставщиках модуля (чего у нас нет). Предположим, что каждый модуль импортирует этот SharedModule (что они все делают).
Когда приложение запускается, Angular охотно загружает AppModule и ContactModule.
Оба экземпляра импортированного SharedModule будут предоставлять UserService. Angular регистрирует один из них в инжекторе корневого приложения (см. Выше). Затем какой-то компонент внедряет UserService, Angular находит его в корневом инжекторе приложения и предоставляет единый UserService для всего приложения. Нет проблем.
Теперь рассмотрим HeroModule, который загружен лениво!
Когда ленивый маршрутизатор загружает HeroModule, он создает дочерний инжектор и регистрирует поставщика UserService с этим дочерним инжектором. Детский инжектор не является корневым инжектором.
Когда Angular создает ленивый HeroComponent, он должен внедрить UserService. На этот раз он находит поставщика UserService в дочернем инжекторе ленивого модуля и создает новый экземпляр UserService. Это совершенно другой экземпляр UserService, чем одноуровневая версия для всего приложения, которую Angular внедрила в один из загруженных компонентов.
Это почти наверняка ошибка.
Докажите это для себя. Запустите живой пример. Измените SharedModule так, чтобы он предоставлял UserService, а не CoreModule. Затем переключайтесь между ссылками "Контакт" и "Герои" несколько раз. Имя пользователя сходит с ума, так как Angular каждый раз создает новый экземпляр UserService.
https://angular.io/docs/ts/latest/cookbook/ngmodule-faq.html
Почему ленивая загрузка создает детский инжектор?
Angular добавляет @NgModule.providers к корневому инжектору приложения... если модуль не загружен с отложенной загрузкой. Затем он создает дочерний инжектор и добавляет поставщиков модуля к детскому инжектору.Это означает, что модуль ведет себя по-разному в зависимости от того, загружен ли он во время запуска приложения или загружен позже. Пренебрежение этой разницей может привести к неблагоприятным последствиям.
Почему Angular не добавляет провайдеров с отложенной загрузкой в корневой инжектор приложения, как это происходит с загруженными модулями? Почему несоответствие?
Ответ основан на фундаментальной характеристике системы инжекции угловых зависимостей. Инжектор может добавлять провайдеров до тех пор, пока он не будет впервые использован. Когда инжектор начинает создавать и предоставлять услуги, список его поставщиков блокируется. Новые провайдеры не допускаются.
Когда приложение запускается, Angular сначала настраивает корневой инжектор с поставщиками всех загруженных модулей, прежде чем создавать свой первый компонент и внедрять какие-либо из предоставляемых сервисов. После запуска приложения корневой инжектор приложения закрывается для новых поставщиков.
Время проходит. Логика приложения запускает ленивую загрузку модуля. Angular должен добавить провайдеров лениво загруженного модуля к инжектору где-нибудь. Он не может добавить их в корневой инжектор приложения, потому что этот инжектор закрыт для новых поставщиков. Таким образом, Angular создает новый дочерний инжектор для ленивого загруженного контекста модуля.
Как упоминал @Günter Zöchbauer, это объясняется в Angular docs, где лениво загруженные модули получают свои собственные экземпляры сервисов. Если сервис предназначен для того, чтобы быть настоящим синглтоном, то вам следует либо
Предоставить его прямо вAppModule
@NgModule({
providers: [ SingletonService ]
})
class AppModule {}
Эти сервисы распространяются на все приложения, даже на лениво загруженные модули. Так что вам не нужно добавлять его в providers
из этих лениво загруженных модулей.
Импортируйте модуль, который предоставляет услугу, вAppModule
Обратите внимание, что соглашение заключается в использовании forRoot
, как упомянуто в [документах][1]
@NgModule({
})
class CoreModule {
forRoot() {
return {
ngModule: SomeModule,
providers: [SingletonService]
}
}
}
@NgModule({
imports: [ CoreModule.forRoot() ]
})
class AppModule {}
forRoot
следует вызывать только в AppModule
импорт. Это гарантирует, что только AppModule
добавляет провайдера. Любые лениво загруженные модули имеют доступ к одной и той же одиночной службе, предоставляемой AppModule
,
Вы можете решить эту проблему, обернув компоненты Mod1Page1 и Mod1Page2 в один компонент Mod1 как дочерний:
@Component({
template: '<router-outlet></router-outlet>'
})
class Mod1Component {}
const routes: Routes = [
{
path: '',
component:Mod1Component,
children:[
{
path : 'page1',
component : Mod1Page1Component
},
{
path : 'page2',
component : Mod1Page2Component
}
]
}
];
const routedComponents = [Mod1Component,Mod1Page1Component, Mod1Page2Component];
Plnkr ссылка на решенную вилку
Mod1Service будет создаваться каждый раз, когда вы переходите к любому компоненту Mod1Module из компонента любого другого модуля, но при переходе между ними служба компонента Mod1Module не будет воссоздана.