5 lines
19 KiB
JSON
5 lines
19 KiB
JSON
{
|
||
"id": "guide/singleton-services",
|
||
"title": "Singleton services",
|
||
"contents": "\n\n\n<div class=\"github-links\">\n <a href=\"https://github.com/angular/angular/edit/master/aio/content/guide/singleton-services.md?message=docs%3A%20describe%20your%20change...\" aria-label=\"Suggest Edits\" title=\"Suggest Edits\"><i class=\"material-icons\" aria-hidden=\"true\" role=\"img\">mode_edit</i></a>\n</div>\n\n\n<div class=\"content\">\n <h1 id=\"singleton-services\">Singleton services<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"guide/singleton-services#singleton-services\"><i class=\"material-icons\">link</i></a></h1>\n<p>A singleton service is a service for which only one instance exists in an app.</p>\n<p>For a sample app using the app-wide singleton service that this page describes, see the\n<live-example name=\"ngmodules\"></live-example> showcasing all the documented features of NgModules.</p>\n<h2 id=\"providing-a-singleton-service\">Providing a singleton service<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"guide/singleton-services#providing-a-singleton-service\"><i class=\"material-icons\">link</i></a></h2>\n<p>There are two ways to make a service a singleton in Angular:</p>\n<ul>\n<li>Set the <code>providedIn</code> property of the <code>@<a href=\"api/core/Injectable\" class=\"code-anchor\">Injectable</a>()</code> to <code>\"root\"</code>.</li>\n<li>Include the service in the <code>AppModule</code> or in a module that is only imported by the <code>AppModule</code></li>\n</ul>\n<a id=\"providedIn\"></a>\n<h3 id=\"using-providedin\">Using <code>providedIn</code><a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"guide/singleton-services#using-providedin\"><i class=\"material-icons\">link</i></a></h3>\n<p>Beginning with Angular 6.0, the preferred way to create a singleton service is to set <code>providedIn</code> to <code>root</code> on the service's <code>@<a href=\"api/core/Injectable\" class=\"code-anchor\">Injectable</a>()</code> decorator. This tells Angular\nto provide the service in the application root.</p>\n<code-example path=\"providers/src/app/user.service.0.ts\" header=\"src/app/user.service.ts\">\nimport { <a href=\"api/core/Injectable\" class=\"code-anchor\">Injectable</a> } from '@angular/core';\n\n@<a href=\"api/core/Injectable\" class=\"code-anchor\">Injectable</a>({\n providedIn: 'root',\n})\nexport class UserService {\n}\n\n\n</code-example>\n<p>For more detailed information on services, see the <a href=\"tutorial/toh-pt4\">Services</a> chapter of the\n<a href=\"tutorial\">Tour of Heroes tutorial</a>.</p>\n<h3 id=\"ngmodule-providers-array\">NgModule <code>providers</code> array<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"guide/singleton-services#ngmodule-providers-array\"><i class=\"material-icons\">link</i></a></h3>\n<p>In apps built with Angular versions prior to 6.0, services are registered NgModule <code>providers</code> arrays as follows:</p>\n<code-example language=\"ts\">\n@<a href=\"api/core/NgModule\" class=\"code-anchor\">NgModule</a>({\n ...\n providers: [UserService],\n ...\n})\n</code-example>\n<p>If this NgModule were the root <code>AppModule</code>, the <code>UserService</code> would be a singleton and available\nthroughout the app. Though you may see it coded this way, using the <code>providedIn</code> property of the <code>@<a href=\"api/core/Injectable\" class=\"code-anchor\">Injectable</a>()</code> decorator on the service itself is preferable as of Angular 6.0 as it makes your services tree-shakable.</p>\n<a id=\"forRoot\"></a>\n<h2 id=\"the-forroot-pattern\">The <code>forRoot()</code> pattern<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"guide/singleton-services#the-forroot-pattern\"><i class=\"material-icons\">link</i></a></h2>\n<p>Generally, you'll only need <code>providedIn</code> for providing services and <code>forRoot()</code>/<code>forChild()</code> for routing. However, understanding how <code>forRoot()</code> works to make sure a service is a singleton will inform your development at a deeper level.</p>\n<p>If a module defines both providers and declarations (components, directives, pipes),\nthen loading the module in multiple feature modules would duplicate the registration of the service. This could result in multiple service instances and the service would no longer behave as a singleton.</p>\n<p>There are multiple ways to prevent this:</p>\n<ul>\n<li>Use the <a href=\"guide/singleton-services#providedIn\"><code>providedIn</code> syntax</a> instead of registering the service in the module.</li>\n<li>Separate your services into their own module.</li>\n<li>Define <code>forRoot()</code> and <code>forChild()</code> methods in the module.</li>\n</ul>\n<div class=\"alert is-helpful\">\n<p><strong>Note:</strong> There are two example apps where you can see this scenario; the more advanced <live-example nodownload=\"\" name=\"ngmodules\">NgModules live example</live-example>, which contains <code>forRoot()</code> and <code>forChild()</code> in the routing modules and the <code>GreetingModule</code>, and the simpler <live-example name=\"lazy-loading-ngmodules\" nodownload=\"\">Lazy Loading live example</live-example>. For an introductory explanation see the <a href=\"guide/lazy-loading-ngmodules\">Lazy Loading Feature Modules</a> guide.</p>\n</div>\n<p>Use <code>forRoot()</code> to\nseparate providers from a module so you can import that module into the root module\nwith <code>providers</code> and child modules without <code>providers</code>.</p>\n<ol>\n<li>Create a static method <code>forRoot()</code> on the module.</li>\n<li>Place the providers into the <code>forRoot()</code> method.</li>\n</ol>\n<code-example path=\"ngmodules/src/app/greeting/greeting.module.ts\" region=\"for-root\" header=\"src/app/greeting/greeting.module.ts\">\n<a href=\"api/upgrade/static\" class=\"code-anchor\">static</a> forRoot(config: UserServiceConfig): <a href=\"api/core/ModuleWithProviders\" class=\"code-anchor\">ModuleWithProviders</a><GreetingModule> {\n return {\n ngModule: GreetingModule,\n providers: [\n {provide: UserServiceConfig, useValue: config }\n ]\n };\n}\n\n</code-example>\n<a id=\"forRoot-router\"></a>\n<h3 id=\"forroot-and-the-router\"><code>forRoot()</code> and the <code><a href=\"api/router/Router\" class=\"code-anchor\">Router</a></code><a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"guide/singleton-services#forroot-and-the-router\"><i class=\"material-icons\">link</i></a></h3>\n<p><code><a href=\"api/router/RouterModule\" class=\"code-anchor\">RouterModule</a></code> provides the <code><a href=\"api/router/Router\" class=\"code-anchor\">Router</a></code> service, as well as router directives, such as <code><a href=\"api/router/RouterOutlet\" class=\"code-anchor\">RouterOutlet</a></code> and <code><a href=\"api/router/RouterLink\" class=\"code-anchor\">routerLink</a></code>. The root application module imports <code><a href=\"api/router/RouterModule\" class=\"code-anchor\">RouterModule</a></code> so that the application has a <code><a href=\"api/router/Router\" class=\"code-anchor\">Router</a></code> and the root application components can access the router directives. Any feature modules must also import <code><a href=\"api/router/RouterModule\" class=\"code-anchor\">RouterModule</a></code> so that their components can place router directives into their templates.</p>\n<p>If the <code><a href=\"api/router/RouterModule\" class=\"code-anchor\">RouterModule</a></code> didn’t have <code>forRoot()</code> then each feature module would instantiate a new <code><a href=\"api/router/Router\" class=\"code-anchor\">Router</a></code> instance, which would break the application as there can only be one <code><a href=\"api/router/Router\" class=\"code-anchor\">Router</a></code>. By using the <code>forRoot()</code> method, the root application module imports <code>RouterModule.forRoot(...)</code> and gets a <code><a href=\"api/router/Router\" class=\"code-anchor\">Router</a></code>, and all feature modules import <code>RouterModule.forChild(...)</code> which does not instantiate another <code><a href=\"api/router/Router\" class=\"code-anchor\">Router</a></code>.</p>\n<div class=\"alert is-helpful\">\n<p><strong>Note:</strong> If you have a module which has both providers and declarations,\nyou <em>can</em> use this\ntechnique to separate them out and you may see this pattern in legacy apps.\nHowever, since Angular 6.0, the best practice for providing services is with the\n<code>@<a href=\"api/core/Injectable\" class=\"code-anchor\">Injectable</a>()</code> <code>providedIn</code> property.</p>\n</div>\n<h3 id=\"how-forroot-works\">How <code>forRoot()</code> works<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"guide/singleton-services#how-forroot-works\"><i class=\"material-icons\">link</i></a></h3>\n<p><code>forRoot()</code> takes a service configuration object and returns a\n<a href=\"api/core/ModuleWithProviders\">ModuleWithProviders</a>, which is\na simple object with the following properties:</p>\n<ul>\n<li><code>ngModule</code>: in this example, the <code>GreetingModule</code> class</li>\n<li><code>providers</code>: the configured providers</li>\n</ul>\n<p>In the <live-example name=\"ngmodules\">live example</live-example>\nthe root <code>AppModule</code> imports the <code>GreetingModule</code> and adds the\n<code>providers</code> to the <code>AppModule</code> providers. Specifically,\nAngular accumulates all imported providers\nbefore appending the items listed in <code>@<a href=\"api/core/NgModule#providers\" class=\"code-anchor\">NgModule.providers</a></code>.\nThis sequence ensures that whatever you add explicitly to\nthe <code>AppModule</code> providers takes precedence over the providers\nof imported modules.</p>\n<p>The sample app imports <code>GreetingModule</code> and uses its <code>forRoot()</code> method one time, in <code>AppModule</code>. Registering it once like this prevents multiple instances.</p>\n<p>You can also add a <code>forRoot()</code> method in the <code>GreetingModule</code> that configures\nthe greeting <code>UserService</code>.</p>\n<p>In the following example, the optional, injected <code>UserServiceConfig</code>\nextends the greeting <code>UserService</code>. If a <code>UserServiceConfig</code> exists, the <code>UserService</code> sets the user name from that config.</p>\n<code-example path=\"ngmodules/src/app/greeting/user.service.ts\" region=\"ctor\" header=\"src/app/greeting/user.service.ts (constructor)\">\nconstructor(@<a href=\"api/core/Optional\" class=\"code-anchor\">Optional</a>() config?: UserServiceConfig) {\n if (config) { this._userName = config.userName; }\n}\n\n</code-example>\n<p>Here's <code>forRoot()</code> that takes a <code>UserServiceConfig</code> object:</p>\n<code-example path=\"ngmodules/src/app/greeting/greeting.module.ts\" region=\"for-root\" header=\"src/app/greeting/greeting.module.ts (forRoot)\">\n<a href=\"api/upgrade/static\" class=\"code-anchor\">static</a> forRoot(config: UserServiceConfig): <a href=\"api/core/ModuleWithProviders\" class=\"code-anchor\">ModuleWithProviders</a><GreetingModule> {\n return {\n ngModule: GreetingModule,\n providers: [\n {provide: UserServiceConfig, useValue: config }\n ]\n };\n}\n\n</code-example>\n<p>Lastly, call it within the <code>imports</code> list of the <code>AppModule</code>. In the following\nsnippet, other parts of the file are left out. For the complete file, see the <live-example name=\"ngmodules\"></live-example>, or continue to the next section of this document.</p>\n<code-example path=\"ngmodules/src/app/app.module.ts\" region=\"import-for-root\" header=\"src/app/app.module.ts (imports)\">\nimport { GreetingModule } from './greeting/greeting.module';\n@<a href=\"api/core/NgModule\" class=\"code-anchor\">NgModule</a>({\n imports: [\n GreetingModule.forRoot({userName: 'Miss Marple'}),\n ],\n})\n\n</code-example>\n<p>The app displays \"Miss Marple\" as the user instead of the default \"Sherlock Holmes\".</p>\n<p>Remember to import <code>GreetingModule</code> as a Javascript import at the top of the file and don't add it to more than one <code>@<a href=\"api/core/NgModule\" class=\"code-anchor\">NgModule</a></code> <code>imports</code> list.</p>\n<h2 id=\"prevent-reimport-of-the-greetingmodule\">Prevent reimport of the <code>GreetingModule</code><a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"guide/singleton-services#prevent-reimport-of-the-greetingmodule\"><i class=\"material-icons\">link</i></a></h2>\n<p>Only the root <code>AppModule</code> should import the <code>GreetingModule</code>. If a\nlazy-loaded module imports it too, the app can generate\n<a href=\"guide/ngmodule-faq#q-why-bad\">multiple instances</a> of a service.</p>\n<p>To guard against a lazy loaded module re-importing <code>GreetingModule</code>, add the following <code>GreetingModule</code> constructor.</p>\n<code-example path=\"ngmodules/src/app/greeting/greeting.module.ts\" region=\"ctor\" header=\"src/app/greeting/greeting.module.ts\">\nconstructor(@<a href=\"api/core/Optional\" class=\"code-anchor\">Optional</a>() @<a href=\"api/core/SkipSelf\" class=\"code-anchor\">SkipSelf</a>() parentModule?: GreetingModule) {\n if (parentModule) {\n throw new Error(\n 'GreetingModule is already loaded. Import it in the AppModule only');\n }\n}\n\n</code-example>\n<p>The constructor tells Angular to inject the <code>GreetingModule</code> into itself.\nThe injection would be circular if Angular looked for\n<code>GreetingModule</code> in the <em>current</em> injector, but the <code>@<a href=\"api/core/SkipSelf\" class=\"code-anchor\">SkipSelf</a>()</code>\ndecorator means \"look for <code>GreetingModule</code> in an ancestor\ninjector, above me in the injector hierarchy.\"</p>\n<p>By default, the injector throws an error when it can't\nfind a requested provider.\nThe <code>@<a href=\"api/core/Optional\" class=\"code-anchor\">Optional</a>()</code> decorator means not finding the service is OK.\nThe injector returns <code>null</code>, the <code>parentModule</code> parameter is null,\nand the constructor concludes uneventfully.</p>\n<p>It's a different story if you improperly import <code>GreetingModule</code> into a lazy loaded module such as <code>CustomersModule</code>.</p>\n<p>Angular creates a lazy loaded module with its own injector,\na child of the root injector.\n<code>@<a href=\"api/core/SkipSelf\" class=\"code-anchor\">SkipSelf</a>()</code> causes Angular to look for a <code>GreetingModule</code> in the parent injector, which this time is the root injector.\nOf course it finds the instance imported by the root <code>AppModule</code>.\nNow <code>parentModule</code> exists and the constructor throws the error.</p>\n<p>Here are the two files in their entirety for reference:</p>\n<code-tabs>\n <code-pane header=\"app.module.ts\" path=\"ngmodules/src/app/app.module.ts\">\n\nimport { <a href=\"api/platform-browser/BrowserModule\" class=\"code-anchor\">BrowserModule</a> } from '@angular/platform-browser';\nimport { <a href=\"api/core/NgModule\" class=\"code-anchor\">NgModule</a> } from '@angular/core';\n\n/* App Root */\nimport { AppComponent } from './app.component';\n\n/* Feature Modules */\nimport { ContactModule } from './contact/contact.module';\nimport { GreetingModule } from './greeting/greeting.module';\n\n/* Routing Module */\nimport { AppRoutingModule } from './app-routing.module';\n\n@<a href=\"api/core/NgModule\" class=\"code-anchor\">NgModule</a>({\n imports: [\n <a href=\"api/platform-browser/BrowserModule\" class=\"code-anchor\">BrowserModule</a>,\n ContactModule,\n GreetingModule.forRoot({userName: 'Miss Marple'}),\n AppRoutingModule\n ],\n declarations: [\n AppComponent\n ],\n bootstrap: [AppComponent]\n})\nexport class AppModule { }\n\n\n</code-pane>\n <code-pane header=\"greeting.module.ts\" region=\"whole-greeting-module\" path=\"ngmodules/src/app/greeting/greeting.module.ts\">\nimport { <a href=\"api/core/ModuleWithProviders\" class=\"code-anchor\">ModuleWithProviders</a>, <a href=\"api/core/NgModule\" class=\"code-anchor\">NgModule</a>, <a href=\"api/core/Optional\" class=\"code-anchor\">Optional</a>, <a href=\"api/core/SkipSelf\" class=\"code-anchor\">SkipSelf</a> } from '@angular/core';\n\nimport { <a href=\"api/common/CommonModule\" class=\"code-anchor\">CommonModule</a> } from '@angular/common';\n\nimport { GreetingComponent } from './greeting.component';\nimport { UserServiceConfig } from './user.service';\n\n\n@<a href=\"api/core/NgModule\" class=\"code-anchor\">NgModule</a>({\n imports: [ <a href=\"api/common/CommonModule\" class=\"code-anchor\">CommonModule</a> ],\n declarations: [ GreetingComponent ],\n exports: [ GreetingComponent ]\n})\nexport class GreetingModule {\n constructor(@<a href=\"api/core/Optional\" class=\"code-anchor\">Optional</a>() @<a href=\"api/core/SkipSelf\" class=\"code-anchor\">SkipSelf</a>() parentModule?: GreetingModule) {\n if (parentModule) {\n throw new Error(\n 'GreetingModule is already loaded. Import it in the AppModule only');\n }\n }\n\n <a href=\"api/upgrade/static\" class=\"code-anchor\">static</a> forRoot(config: UserServiceConfig): <a href=\"api/core/ModuleWithProviders\" class=\"code-anchor\">ModuleWithProviders</a><GreetingModule> {\n return {\n ngModule: GreetingModule,\n providers: [\n {provide: UserServiceConfig, useValue: config }\n ]\n };\n }\n}\n\n</code-pane>\n</code-tabs>\n<h2 id=\"more-on-ngmodules\">More on NgModules<a title=\"Link to this heading\" class=\"header-link\" aria-hidden=\"true\" href=\"guide/singleton-services#more-on-ngmodules\"><i class=\"material-icons\">link</i></a></h2>\n<p>You may also be interested in:</p>\n<ul>\n<li><a href=\"guide/sharing-ngmodules\">Sharing Modules</a>, which elaborates on the concepts covered on this page.</li>\n<li><a href=\"guide/lazy-loading-ngmodules\">Lazy Loading Modules</a>.</li>\n<li><a href=\"guide/ngmodule-faq\">NgModule FAQ</a>.</li>\n</ul>\n\n \n</div>\n\n<!-- links to this doc:\n - guide/creating-libraries\n - guide/lazy-loading-ngmodules\n - guide/module-types\n - guide/ngmodule-faq\n - guide/providers\n - guide/router-tutorial-toh\n-->\n<!-- links from this doc:\n - api/common/CommonModule\n - api/core/Injectable\n - api/core/ModuleWithProviders\n - api/core/NgModule\n - api/core/NgModule#providers\n - api/core/Optional\n - api/core/SkipSelf\n - api/platform-browser/BrowserModule\n - api/router/Router\n - api/router/RouterLink\n - api/router/RouterModule\n - api/router/RouterOutlet\n - api/upgrade/static\n - guide/lazy-loading-ngmodules\n - guide/ngmodule-faq\n - guide/ngmodule-faq#q-why-bad\n - guide/sharing-ngmodules\n - guide/singleton-services#forroot-and-the-router\n - guide/singleton-services#how-forroot-works\n - guide/singleton-services#more-on-ngmodules\n - guide/singleton-services#ngmodule-providers-array\n - guide/singleton-services#prevent-reimport-of-the-greetingmodule\n - guide/singleton-services#providedIn\n - guide/singleton-services#providing-a-singleton-service\n - guide/singleton-services#singleton-services\n - guide/singleton-services#the-forroot-pattern\n - guide/singleton-services#using-providedin\n - tutorial\n - tutorial/toh-pt4\n - https://github.com/angular/angular/edit/master/aio/content/guide/singleton-services.md?message=docs%3A%20describe%20your%20change...\n-->"
|
||
} |