Книга: Angular 2 Cookbook
Назад: Injecting a simple service into a component
Дальше: Service injection aliasing with useClass and useExisting

Controlling service instance creation and injection with NgModule

In a stark departure from Angular 1.x, Angular 2 features a hierarchical injection scheme. This has a substantial number of implications, and one of the more prominent one is the ability to control when, and how many, services are created.

Note

The code, links, and a live example of this are available at .

Getting ready

Suppose you begin with the following simple application:

[app/root.component.ts]      import {Component} from '@angular/core';      @Component({     selector: 'root',     template: `       <h1>root component!</h1>       <article></article>       <article></article>     `   })   export class RootComponent {}   [app/article.component.ts]      import {Component} from '@angular/core';      @Component({     selector: 'article',     template: `       <p>Article component!</p>     `   })   export class ArticleComponent {}   [app/article.service.ts]      import {Injectable} from '@angular/core';      @Injectable()   export class ArticleService {     constructor() {        console.log('ArticleService constructor!');     }   }   [app/app.module.ts]      import {NgModule} from '@angular/core';   import {BrowserModule} from '@angular/platform-browser';   import {RootComponent} from './root.component';   import {ArticleComponent} from './article.component;       @NgModule({     imports: [       BrowserModule,     ],     declarations: [       RootComponent,       ArticleComponent     ],     bootstrap: [       RootComponent     ]   })   export class AppModule {}   

Your objective is to inject a single instance of ArticleService into the two child components. In this recipe, console.log inside the ArticleService constructor allows you to see when one is instantiated.

How to do it...

Begin by importing the service into AppModule, then providing it with the following:

[app/app.module.ts]      import {NgModule} from '@angular/core';   import {BrowserModule} from '@angular/platform-browser';   import {RootComponent} from './root.component';   import {ArticleComponent} from './article.component;    import {ArticleService} from './article.service;         @NgModule({     imports: [       BrowserModule,     ],     declarations: [       RootComponent,       ArticleComponent     ],     providers: [       ArticleService     ]     bootstrap: [       RootComponent     ]   })   export class AppModule {}   

Since ArticleService is provided in the same module where ArticleComponent is declared, you are now able to inject ArticleService into the child ArticleComponent instances:

[app/article/article.component.ts]      import {Component} from '@angular/core';   import {ArticleService} from './article.service';      @Component({     selector: 'article',     template: `       <p>Article component!</p>     `   })   export class ArticleComponent {     constructor(private articleService_:ArticleService) {}   }   

With this, you will find that the same service instance is injected into both the child components as the ArticleService constructor, namely console.log, is only executed once.

Splitting up the root module

As the application grows, it will make less and less sense to cram everything into the same top-level module. Instead, it would be ideal for you to break apart modules into chunks that make sense. In the case of this recipe, it would be preferable to provide ArticleService to the application pieces that are actually going to inject it.

Define a new ArticleModule and move the relevant module imports into that file instead:

[app/article.module.ts]      import {NgModule} from '@angular/core';   import {ArticleComponent} from './article.component';   import {ArticleService} from './article.service';      @NgModule({     declarations: [       ArticleComponent     ],     providers: [       ArticleService       ],     bootstrap: [       ArticleComponent     ]   })   export class ArticleModule {}   

Then, import this entire module into AppModule instead:

[app/app.module.ts]      import {NgModule} from '@angular/core';   import {BrowserModule} from '@angular/platform-browser';   import {RootComponent} from './root.component';   import {ArticleModule} from './article.module';      @NgModule({     imports: [       BrowserModule,       ArticleModule     ],     declarations: [       RootComponent     ],     bootstrap: [       RootComponent     ]   })   export class AppModule {}   

If you stop here, you'll find that there are no errors, but AppModule isn't able to render ArticleComponent. This is because Angular modules, like other module systems, need to explicitly define what is being exported to other modules:

[app/article.module.ts]      import {NgModule} from '@angular/core';   import {ArticleComponent} from './article.component';   import {ArticleService} from './article.service';      @NgModule({     declarations: [       ArticleComponent     ],     providers: [       ArticleService       ],     bootstrap: [       ArticleComponent     ],     exports: [       ArticleComponent       ]   })   export class ArticleModule {}   

With this, you will still see that ArticleService is instantiated once.

How it works...

Angular 2's dependency injection takes advantage of its hierarchy structure when providing and injecting services. From where a service is injected, Angular will instantiate a service wherever it is provided. Inside a module definition, this will only ever happen once.

In this case, you provided ArticleService to both AppModule and ArticleModule. Even though the service is injected twice (once for each ArticleComponent), Angular uses the providers declaration to decide when to create the service.

There's more...

At this point, a curious developer should have lots of questions about how exactly this injection schema behaves. There are numerous different configuration flavors that can be useful to the developer, and these configurations only require a minor code adjustment from the preceding result.

Injecting different service instances into different components

As you might anticipate from the preceding explanation, you can reconfigure this application to inject a different ArticleService instance into each child, two in total. This can be done by migrating the providers declaration out of the module definition and into the ArticleComponent definition:

[app/article.module.ts]      import {NgModule} from '@angular/core';   import {ArticleComponent} from './article.component';      @NgModule({     declarations: [       ArticleComponent     ],     bootstrap: [       ArticleComponent     ],     exports: [       ArticleComponent       ]   })   export class ArticleModule {}   [app/article.component.ts]      import {Component} from '@angular/core';   import {ArticleService} from './article.service';      @Component({     selector: 'article',     template: `       <p>Article component!</p>     `,     providers: [       ArticleService     ]   })   export class ArticleComponent {     constructor(private articleService_:ArticleService) {}   }   

You can verify that two instances are being created by observing the two console.log statements called from the ArticleService constructor.

Service instantiation

The location of the providers also means that service instance instantiation is bound to the lifetime of the component. For this application, this means that whenever a component is created, if a service is provided inside that component definition, a new service instance will be created.

For example, if you were to toggle the existence of a child component with ArticleService provided inside it, it will create a new ArticleService every time ArticleComponent is constructed:

[app/root.component.ts]      import {Component} from '@angular/core';      @Component({     selector: 'root',     template: `       <h1>root component!</h1>       <button (click)="toggle=!toggle">Toggle</button>       <article></article>       <article *ngIf="toggle"></article>     `   })   export class RootComponent {}   

You can verify that new instances are being created each time ngIf evaluates to true by observing additional console.log statements called from the ArticleService constructor.

See also

  • Injecting a simple service into a component walks you through the basics of Angular 2's dependency injection schema
  • Service injection aliasing with useClass and useExisting demonstrates how to intercept dependency injection provider requests
Назад: Injecting a simple service into a component
Дальше: Service injection aliasing with useClass and useExisting

thank you
Flame
cant read the code since it is all on a single line. Also this comments section is russian
Rakuneque
DATA COLLECTION AND ANALYSIS Two reviewers extracted data and assessed methodological quality independently lasix torsemide conversion Many others were in that space already