Как использовать .forRoot () в иерархии функциональных модулей

Может ли кто-нибудь пояснить мне, как мне структурировать иерархию нескольких вложенных функциональных модулей с .forRoot() вызовами?

Например, что, если у меня есть такие модули:

- MainModule
- SharedModule
- FeatureModuleA
    - FeatureModuleA1
    - FeatureModuleA2
- FeatureModuleB

Все функциональные модули имеют .forRoot() статическую функцию.

Как мне определить FeatureModuleA с каким-либо образом передать .forRoot() функции?

@NgModule({ 
  imports: [
    //- I can use .forRoot() calls here but this module not the root module
    //- I don't need to import sub-modules here, FeatureA only a wrapper
    //FeatureModuleA1.forRoot(), //WRONG!
    //FeatureModuleA2.forRoot(), //WRONG!
  ],
  exports: [
    //I cannot use .forRoot() calls here
    FeatureModuleA1, 
    FeatureModuleA2 
  ]
})
class FeatureModuleA {
  static forRoot(): ModuleWithProviders {
    return {
      //At this point I can set any other class than FeatureModuleA for root
      //So lets create a FeatureRootModuleA class: see below!
      ngModule: FeatureModuleA //should be: FeatureRootModuleA 
    };
  }
}

Я могу создать другой класс для корневого использования, а затем установить его в функции forRoot() FeatureModuleA:

@NgModule({
  imports: [
    //Still don't need any sub module within this feature module
  ]
  exports: [
    //Still cannot use .forRoot() calls but still need to export them for root module too:
    FeatureModuleA1, 
    FeatureModuleA2 
  ]
})
class FeatureRootModuleA { }

Но как я могу передать .forRoot() вызовов в этом специальном ModuleClass?

Как я вижу, мне нужно импортировать все подмодули прямо в мой корневой MainModule и вызывать .forRoot() для каждого там:

@NgModule({
  imports: [
    FeatureModuleA1.forRoot(),
    FeatureModuleA2.forRoot(),
    FeatureModuleA.forRoot(),
    SharedModule.forRoot()
  ]
})
class MainModule { }

Я прав? Прежде чем ответить, взгляните на этот файл: https://github.com/angular/material2/blob/master/src/lib/module.ts

Насколько я знаю, это репо поддерживается официальной командой angular. Поэтому они решают проблему, просто импортируя все .forRoot() вызовы в специальный модуль MaterialRootModule. Я действительно не понимаю, как это будет применяться к моему собственному корневому модулю? Что на самом деле означают root и .forRoot? Это связано с пакетом, а не с фактическим веб-проектом?


person ggabor    schedule 23.09.2016    source источник
comment
Я не уверен, почему вы хотите создать и использовать метод forRoot ... возможно, это поможет: angular.io/docs/ts/latest/guide/ngmodule.html   -  person slaesh    schedule 23.09.2016
comment
да. Прочитал официальную документацию. Там нет ничего о том, как структурировать сложные проекты с несколькими вложенными модулями. Я хочу создать и использовать forRoot именно потому, что синглтоны провайдера должны устанавливаться только один раз в рамках всего проекта.   -  person ggabor    schedule 23.09.2016
comment
Хорошо, потому что в вашем примере не было провайдеров. :)   -  person slaesh    schedule 23.09.2016
comment
Это был просто пример, сосредоточенный на реальной проблеме. Кстати, в официальных документах они упоминают о создании модуля только для инкапсуляции других - вообще без необходимости в каком-либо провайдере.   -  person ggabor    schedule 23.09.2016


Ответы (3)


Обычно forRoot используется для добавления приложений / одноэлементных служб.

@NgModule({
  providers: [ /* DONT ADD HERE */ ]
})
class SharedModule {
  static forRoot() {
    return {
      ngModule: SharedModule,
      providers: [ AuthService ]
    }
  }
}

Причина в том, что если вы добавите AuthService к providers в @NgModule, возможно создание более одного, если вы импортируете SharedModule в другие модули.

Я не на 100% уверен в том, будет ли создана служба при импорте SharedModule в быстро загружаемый модуль, но объяснение того, что упомянутые документы, касались лениво загружаемых модулей. Когда вы лениво загружаете модуль, будут созданы все поставщики.

По этой причине мы добавляем (по соглашению) метод forRoot, чтобы указать, что метод должен вызываться только для корневого (приложения) модуля, тогда как для другого модуля он должен просто импортироваться в обычном режиме.

@NgModule({
  imports: [SharedModule]
})
class FeatureModule {}

@NgModule({
  imports: [SharedModule.forRoot()]
})
class AppModule {}
person Paul Samsotha    schedule 23.09.2016
comment
Спасибо за ваши усилия, но это все еще не заботится о многоуровневом вложенном модуле. В вашем примере вы импортировали AuthService в SharedModule. Не могли бы вы провести рефакторинг своего примера, импортировав SharedModule (модуль вместо службы!) - и попробуйте где-нибудь использовать AuthModule.forRoot (). (отказ от ответственности: для меня AuthService / AuthModule здесь просто пример - это может быть любой модуль с методами forChild и forRoot.) - person ggabor; 23.09.2016
comment
forRoot следует использовать только для модуля приложения. Не должно быть необходимости ничего пересылать. Когда вы импортируете Just SharedModule без forRoot, forRoot никогда не вызывается. Таким образом мы должны структурировать наши модули. Сервисы для всего приложения в forRoot и все остальное в NgModule. Вложенные или нет, вот как это следует считать - person Paul Samsotha; 23.09.2016
comment
Если вам нужно импортировать функциональный модуль в другие функциональные модули, продолжайте, это не повредит. Просто помните эмпирическое правило о поставщиках приложений в forRoot, и у вас все должно получиться - person Paul Samsotha; 23.09.2016
comment
Также помните, что declaration не наследуются модулями. Таким образом, вам все равно нужно будет импортировать функциональный модуль в другие модули, если ему потребуются его объявления. - person Paul Samsotha; 23.09.2016
comment
Не могли бы вы обновить свой пример, чтобы продемонстрировать эту концепцию. Я думаю, что понимаю все аспекты модулей и forRoot, но до сих пор не знаю, как передавать поставщиков приложений через иерархию. - person ggabor; 23.09.2016
comment
Любой модуль с поставщиками для всего приложения добавьте forRoot, как в моем примере SharedModule, и импортируйте его в AppModule, вызывая forRoot. Разве мой ответ этого не передает? Если нет, я постараюсь обновить его, чтобы было понятнее. В остальном, возможно, я не понимаю, что означает перенос поставщиков приложений через иерархию. Насколько я понимаю из этого утверждения, это касается того, что я объяснил. - person Paul Samsotha; 23.09.2016
comment
Если ваша основная цель - попытаться выяснить, как вы можете вызвать forRoot в функциональном модуле и просто каким-то образом передать его в модуль приложения, я не думаю, что это произойдет. - person Paul Samsotha; 23.09.2016
comment
Да! Я пытаюсь понять это! Вы проверяли ссылку на материальное репо? Они просто импортировали с помощью .forRoot () в функциональный модуль - а также создали специальный MaterialRootModule для обработки случая. Кстати, в официальных документах об этой технике нет никакой информации. Наконец, они решили, что мне не нужно импортировать и вызывать forRoot () для каждого подмодуля. Поэтому я пытаюсь как-то решить ту же проблему в собственном проекте. - person ggabor; 23.09.2016
comment
Это потому, что этот модуль строго предназначен для использования с модулем приложения. Это не какой-либо функциональный модуль. Он просто выглядит как вспомогательный модуль, если вы хотите добавить все модули в свое приложение, не импортируя их вручную. Но если вы попытаетесь добавить этот модуль к кучке разных модулей, вы рискуете получить кучу разных экземпляров провайдеров, которые должны быть только синглтонами. - person Paul Samsotha; 23.09.2016
comment
Отлично. Я думаю, что понял. Остался только один вопрос: поставщики из тех подмодулей, отмеченных комментарием «// Эти модули включают поставщиков». будет доступен для всего проекта? Или что поставщики доступны только в самом MaterialModule? - person ggabor; 23.09.2016
comment
Если вы импортируете корневой модуль в модуль приложения, да, они доступны для всего приложения. Но это только провайдеры. Компонент, директивы и каналы, вам все равно нужно импортировать модуль Non root в любой модуль, в котором он вам нужен. Это странно, но так оно и есть. Вы можете экспортировать из своего ShareModule, так что вам нужно только импортировать свой SharedModule. - person Paul Samsotha; 23.09.2016
comment
Итак, вы говорите, что если я импортирую модуль в свой AppModule, который импортирует другие модули с суффиксом forRoot (), все эти поставщики будут доступны для всего проекта без необходимости повторного импорта всех подпрограмм с forRoot () внутри AppModule. Это правда? - person ggabor; 23.09.2016
comment
да, но вы должны импортировать forRoot только в ситуации, например, с материалом, когда они сделали консолидатор корневых модулей. Если этот подмодуль предназначен для использования с другими модулями, вам не следует импортировать модуль forRoot в этот подмодуль. - person Paul Samsotha; 23.09.2016

Поскольку forRoot предназначен только для предоставления одноэлементных служб, вы можете "повторно предоставить" их явно в SharedModule:

@NgModule({})
class SharedModule {
  static forRoot() {
    return {
      ngModule: SharedModule,
      providers: [
        AuthService,
        FeatureModuleA.forRoot().providers,
        FeatureModuleB.forRoot().providers,
      ],
    }
  }
}

Таким образом, все услуги будут предоставляться самим SharedModule (а не соответствующим подмодулем), но кажется, что это не имеет значения. Может хоть кто поспорит ...

Обратите внимание, что FeatureModuleA также может «повторно предоставлять» одноэлементные службы из своих подмодулей аналогичным образом.

person Den Klimovsky    schedule 05.04.2018
comment
то, чего не хватает в большинстве ответов, - как тогда вы используете AuthService в других модулях - если я не помещаю AuthService в провайдеры, он терпит неудачу. Если я помещаю его в провайдеры, он не обрабатывает его как синглтон. - person Steve; 01.05.2018
comment
Вы должны добавить SharedModule.forRoot() к импорту вашего AppModule. Таким образом, Angular предоставит все эти услуги для корневого инжектора (AppModule), а значит, и для одиночных файлов. - person Den Klimovsky; 02.05.2018

Как упоминалось выше, важно учитывать ленивые загружаемые модули, потому что общая служба может использоваться в ленивом загружаемом модуле, но если эта служба уже использовалась и была создана другим модулем, то будет два экземпляра, следовательно, потребуется шаблон singleton . Отличным примером здесь является AuthService - вы не хотите иметь один экземпляр службы с неаутентифицированным пользователем, в то время как другой аутентифицирован «тот же» пользователь.

В Angular 6 появился новый способ регистрации провайдера как синглтона. Внутри декоратора @Injectable () для службы используйте атрибут providedIn. Установите его значение "root". Тогда вам не нужно будет добавлять его в список поставщиков корневого модуля, или в этом случае вы также можете установить его в свой SharedModule следующим образом:

@Injectable({
  providedIn: SharedModule // or 'root' for singleton
})
export class AuthService {
...
person thenninger    schedule 08.06.2018