Vraag Service is niet singleton voor angular2 router lui laden met loadChildren


Hier is de plukker. https://plnkr.co/edit/tsNlmRth4mRzz0svGWLK?p=preview

Waarin ik twee modules heb gemaakt met elk twee componenten en één service. Ik wil dat de service singleton (save state) is op het niveau van de module.

Als u op 'Module 1 Page 1' en 'Module 2 Page 2' klikt, worden twee verschillende willekeurige nummers weergegeven. Omdat ik dit nummer aanmaak in de constructor. Dus de service wordt elke keer dat de pagina wordt gewijzigd gemaakt. Maar module2 gedraagt ​​zich perfect.

Notitie: Mode1Module gebruikt loadChildren Mode2Module gebruikt children

Ik heb geconstateerd dat dit type probleem eerder is opgelost angular2 rc5 routerservice singleton

Maar ik denk dat het er nog steeds is. Dus help me alsjeblieft om dit op te lossen.


15
2017-12-05 18:53


oorsprong


antwoorden:


Lazy loaded-modules hebben hun eigen rootbereik. Providers die aan lui geladen modules worden toegevoegd, krijgen een exemplaar in dat rootbereik in plaats van het hoofdbereik van de toepassing. Als u de provider toevoegt aan een module die niet lui is geladen, wordt slechts één instantie in het hoofdbereik van de toepassing gemaakt.

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

Waarom is een dienst die wordt aangeboden in een lui geladen module alleen daarvoor zichtbaar?   module?
  Anders dan aanbieders van de modules die bij de lancering worden geladen, zijn aanbieders van   lui geladen modules zijn module-scoped.

Wanneer de hoekige router een module lui laadt, wordt er een nieuwe gemaakt   uitvoeringscontext. Die context heeft zijn eigen injector die een directe is   kind van de applicatie-injector.

De router voegt de providers van de luie module en de providers van zijn toe   geïmporteerde modules voor deze kinderinjector.

Deze providers zijn geïsoleerd van wijzigingen aan applicatieaanbieders   met hetzelfde opzoektoken. Wanneer de router een component binnen maakt   de luie geladen context, geeft Angular de voorkeur aan gemaakte service-instanties   van deze providers naar de service-instanties van de toepassing root   injector.

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

Waarom is het slecht als SharedModule een service levert aan een lui geladen   module?
  Deze vraag ontstond in het hoofdstuk Hoekmodule als we   besproken hoe belangrijk het is om providers uit de SharedModule te houden.

Stel dat we de UserService in de leveranciers van de module hadden opgenomen (welke   wij niet). Stel dat elke module deze SharedModule importeert (welke   ze doen het allemaal).

Wanneer de app start, laadt Angular gretig de AppModule en de   ContactModule.

Beide exemplaren van de geïmporteerde SharedModule zouden de   UserService. Angular registreert een van hen in de root-app-injector   (zie hierboven). Vervolgens injecteert een onderdeel UserService, Angular vindt het   in de root-injector van de app en levert de app-brede singleton af   UserService. Geen probleem.

Overweeg nu de HeroModule die lui geladen is!

Wanneer de router 'lazy' de HeroModule laadt, wordt een kinderinjector gemaakt   en registreert de UserService-provider met die kinderinjector. De   kinderinjector is niet de wortelinjector.

Wanneer Angular een luie HeroComponent maakt, moet deze een   UserService. Deze keer vindt het een UserService-provider in de lui   module-kindinjector en maakt een nieuw exemplaar van de gebruikersservice.   Dit is een geheel andere UserService-instantie dan de app-breed   singleton-versie die Angular injecteerde in een van de gretig geladen   componenten.

Dat is bijna zeker een vergissing.

Bewijs het voor jezelf. Voer het live voorbeeld uit. Wijzig de SharedModule     zodat het de UserService biedt in plaats van de CoreModule. Dan     schakelen tussen de koppelingen "Contact" en "Heroes" een paar keer. De     gebruikersnaam gaat gek als de Angular een nieuwe UserService maakt     bijvoorbeeld elke keer.

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

Waarom creëert lui laden een kinderinjector?
   Hoekige toevoegingen   @ NgModule.providers naar de root-injector van de toepassing ... tenzij de   module is lui geladen. Dan maakt het een kinderinjector en voegt het toe   module-aanbieders voor de kinderinjector.

Dit betekent dat een module zich anders gedraagt, afhankelijk van de vraag   wordt tijdens het starten van de toepassing geladen of later lazy geladen. Verwaarlozing   dat verschil kan tot nadelige gevolgen leiden.

Waarom voegt Angular geen lui geladen providers toe aan de root-injector van de app   zoals het is voor gretig geladen modules? Waarom de inconsistentie?

Het antwoord is gebaseerd op een fundamenteel kenmerk van de Angular   afhankelijkheid injectiesysteem. Een injector kan providers toevoegen totdat dit het geval is   voor het eerst gebruikt. Zodra een injector services begint te maken en te leveren,   de lijst met providers is bevroren. Geen nieuwe providers toegestaan.

Wanneer een toepassing start, configureert Angular eerst de root   injector met de aanbieders van alle gretig geladen modules eerder   het maken van de eerste component en het injecteren van een van de geleverde componenten   Diensten. Zodra de applicatie begint, is de root-injector van de app gesloten   naar nieuwe providers.

Tijd verstrijkt. Applicatielogica activeert lui laden van een module.   Angular moet de providers van de luie geladen module toevoegen aan een injector   ergens. Het kan ze niet aan de root-injector van de app toevoegen, omdat dat   injector is gesloten voor nieuwe aanbieders. Dus Angular maakt een nieuw kind   injector voor de lui geladen modulecontext.


22
2017-12-05 19:23



Zoals vermeld door @ Günter Zöchbauer, wordt dit uitgelegd in de Hoekdocumenten, waar luie geladen modules hun eigen instanties van diensten krijgen. Als een service bedoeld is als een echte singleton, dan zou u dat ook moeten doen

Geef het rechtstreeks op in AppModule

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

Deze services zijn toepassingsbreed, zelfs voor lui geladen modules. U hoeft het dus niet toe te voegen aan de providers van die lui geladen modules.

Importeer een module die de service biedt in de AppModule

Merk op dat een conventie is om een ​​te gebruiken forRoot, zoals vermeld in [de documenten] [1]

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

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

De forRoot zou alleen in de AppModule import. Dit zorgt ervoor dat alleen de AppModule voegt de provider toe. Elke lazy-loaded module heeft toegang tot dezelfde singleton-service die door de AppModule.


15
2017-12-23 01:56



U kunt dit probleem oplossen door Mod1Page1- en Mod1Page2-componenten als één kind in één Mod1-component te verpakken:

@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-link naar opgeloste vork

Mod1Service wordt elke keer gemaakt wanneer u navigeert naar een onderdeel van een Mod1 Module van een component van een andere module, maar wanneer u navigeert. De moduleondersteuning van Mod1Module zal niet opnieuw worden gemaakt.


0
2018-01-26 08:53