Служба не является одиночной для ленивой загрузки маршрутизатора angular2 с помощью loadChildren

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

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

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

Примечание: Mode1Module использует loadChildrenMode2Module использует 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 не будет воссоздана.

Другие вопросы по тегам