Сервис не является singleton для angular2 маршрутизатор ленивая загрузка с loadChildren

Вот плюкер. https://plnkr.co/edit/tsNlmRth4mRzz0svGWLK?p=preview

В котором я создал два модуля с двумя компонентами и по одной службе. Я хочу, чтобы служба должна быть одноточечной (сохранить состояние) на уровне модуля.

Если вы нажмете "Модуль 1 Page 1" и "Модуль 2", он отобразит два разных случайных числа. Поскольку я создаю это число в конструкторе. Таким образом, услуга создается при каждом изменении страницы. Но модуль2 отлично себя ведет.

Примечание: Mode1Module использует loadChildren Mode2Module использует children

Я обнаружил, что проблема такого же типа исправлена ​​ранее в соответствии с этим angular2 сервисом маршрутизатора rc5 oneton

Но я думаю, что все еще есть. Поэтому, пожалуйста, помогите мне решить эту проблему.

Ответ 1

У ленивых загружаемых модулей есть своя собственная корневая область. Провайдеры, добавленные к ленивым загруженным модулям, получают экземпляр в этой корневой области вместо корневой области приложения. Если вы добавите поставщика в модуль, который не будет ленивым, будет создан только один экземпляр в корневой области приложения.

https://angular.io/docs/ts/latest/cookbook/ngmodule-faq.html#!#q-lazy-loaded-module-provider-visibility

Почему услуга, предоставляемая в ленивом загруженном модуле, видима только для этого модуль?
В отличие от поставщиков модулей, загруженных при запуске, поставщики ленивые модули загружаются с модулями.

Когда маршрутизатор Angular ленивый загружает модуль, он создает новый контекст выполнения. Этот контекст имеет свой собственный инжектор, который является прямым дочерний элемент инжектора приложения.

Маршрутизатор добавляет поставщиков ленивых модулей и поставщиков своих импортированные модули для этого дочернего инжектора.

Эти поставщики изолированы от изменений в поставщиках приложений с тем же токеном поиска. Когда маршрутизатор создает компонент внутри ленивый загруженный контекст, Angular предпочитает созданные экземпляры службы от этих поставщиков до экземпляров службы корневого приложения инжектор.

https://angular.io/docs/ts/latest/cookbook/ngmodule-faq.html#!#q-why-bad

Почему это плохо, если SharedModule предоставляет услугу ленивым загруженным модуль?
Этот вопрос возник в главе Angular Module, когда мы обсудили важность удержания провайдеров из SharedModule.

Предположим, что мы указали UserService у поставщиков модулей (которые мы не). Предположим, что каждый модуль импортирует этот SharedModule (который они все делают).

При запуске приложения Angular загружает AppModule и ContactModule.

Оба экземпляра импортированного SharedModule предоставили UserService. Angular регистрирует один из них в корневом приложении инжектор (см. выше). Затем некоторый компонент вставляет UserService, Angular находит его в инжекторе корневого приложения, и предоставляет широкоэкранный синглтон UserService. Нет проблем.

Теперь рассмотрим HeroModule, который ленивый загружен!

Когда маршрутизатор ленивый загружает HeroModule, он создает дочерний инжектор и регистрирует провайдер UserService с этим дочерним инжектором. инжектор ребенка не является инжектором корня.

Когда Angular создает ленивый HeroComponent, он должен UserService. На этот раз он находит провайдера UserService в ленивом модульного дочернего инжектора и создает новый экземпляр UserService. Это совершенно другой экземпляр UserService, чем приложение singleton версии, которая Angular вводится в один из загруженных компоненты.

Это почти наверняка ошибка.

Докажите это себе. Запустите живой пример. Изменить SharedModule   так что он предоставляет UserService, а не CoreModule. затем   несколько раз переключаться между ссылками "Контакт" и "Герои".   имя пользователя идет bonkers, поскольку Angular создает новый UserService   каждый раз.

https://angular.io/docs/ts/latest/cookbook/ngmodule-faq.html#!#q-why-child-injector

Почему ленивая загрузка создает дочерний инжектор?
 Angular добавляет @NgModule.providers к корневому инжектору приложения... если только модуль ленивый. Затем он создает дочерний инжектор и добавляет провайдеров модулей для дочернего инжектора.

Это означает, что модуль ведет себя по-разному в зависимости от того, загружается во время запуска приложения или позже выполняется загрузка. Пренебрегая эта разница может привести к неблагоприятным последствиям.

Почему Angular не добавляет ленивых загружаемых поставщиков в инжектор корневого приложения как это делается для загруженных модулей? Почему непоследовательность?

Ответ обоснован в фундаментальной характеристике Angularсистема впрыска зависимостей. Инжектор может добавлять поставщиков, пока сначала используется. Как только инжектор начинает создавать и доставлять услуги, его список поставщиков заморожен. Никакие новые поставщики не разрешены.

Когда запускаются приложения, Angular сначала настраивает корень инжектор с поставщиками всех загруженных модулей до создавая свой первый компонент и вводя любой из предоставленных Сервисы. После запуска приложения инжектор корневого приложения закрывается новым провайдерам.

Время проходит. Логика приложения запускает ленивую загрузку модуля. Angular должен добавить поставщиков ленивого загруженного модуля к инжектору где-то. Он не может добавить их в инжектор корневого приложения, поскольку инжектор закрыт для новых поставщиков. Итак, Angular создает новый дочерний элемент инжектор для контекста ленивого загруженного модуля.

Ответ 2

Как упоминалось в @Günter Zöchbauer, это объясняется документами Angular, где ленивые модули получают свои собственные экземпляры сервисов. Если служба предназначена для истинного синглтона, то вы должны либо

Предоставить его прямо в AppModule

@NgModule({
  providers: [ SingletonService ]
})
class AppModule {}

Эти службы являются общими, даже для ленивых модулей. Поэтому вам не нужно добавлять его в providers этих ленивых модулей.

Импортировать модуль, предоставляющий услугу, в AppModule

Обратите внимание, что соглашение заключается в использовании forRoot, как указано в [docs] [1]

@NgModule({
})
class CoreModule {
  forRoot() {
    return {
      ngModule: SomeModule,
      providers: [SingletonService]
    }
  }
}

@NgModule({
  imports: [ CoreModule.forRoot() ]
})
class AppModule {}

forRoot следует вызывать только в импорте AppModule. Это гарантирует, что только AppModule добавляет провайдера. Любые загруженные модули имеют доступ к одной и той же однопользовательской службе, предоставляемой AppModule.

Ответ 3

Вы можете решить эту проблему, обернув компоненты 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 не будет воссоздана.