[英]How do I create a singleton service in Angular 2?
我讀過在引導時注入應該讓所有孩子共享同一個實例,但是我的主要和頭組件(主應用程序包括頭組件和路由器出口)每個都獲得了我的服務的單獨實例。
我有一個 FacebookService,我用它來調用 facebook javascript api 和一個使用 FacebookService 的 UserService。 這是我的引導程序:
bootstrap(MainAppComponent, [ROUTER_PROVIDERS, UserService, FacebookService]);
從我的日志記錄來看,引導程序調用完成,然后我看到 FacebookService 然后 UserService 在每個構造函數中的代碼運行之前創建,MainAppComponent、HeaderComponent 和 DefaultComponent:
創建單例服務的推薦方式已更改。 現在建議在服務的@Injectable
裝飾器中指定它應該在“根”中提供。 這對我來說很有意義,而且根本不需要在模塊中列出所有提供的服務。 您只需在需要時導入服務,它們就會在適當的位置自行注冊。 您還可以指定一個模塊,以便僅在導入該模塊時才提供該模塊。
@Injectable({
providedIn: 'root',
})
export class ApiService {
}
使用 NgModule,我認為現在的方法是創建一個包含您的服務類的“CoreModule”,並在模塊的提供者中列出服務。 然后在主應用程序模塊中導入核心模塊,該模塊將為在其構造函數中請求該類的任何子項提供一個實例:
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ApiService } from './api.service';
@NgModule({
imports: [
CommonModule
],
exports: [ // components that we want to make available
],
declarations: [ // components for use in THIS module
],
providers: [ // singleton services
ApiService,
]
})
export class CoreModule { }
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { AppComponent } from './app.component';
import { CoreModule } from './core/core.module';
@NgModule({
declarations: [ AppComponent ],
imports: [
CommonModule,
CoreModule // will provide ApiService
],
providers: [],
bootstrap: [ AppComponent ]
})
export class AppModule { }
如果您在bootstrap()
列出提供者,則無需在組件裝飾器中列出它們:
import { ApiService } from '../core/api-service';
@Component({
selector: 'main-app',
templateUrl: '/views/main-app.html',
// DO NOT LIST PROVIDERS HERE IF THEY ARE IN bootstrap()!
// (unless you want a new instance)
//providers: [ApiService]
})
export class MainAppComponent {
constructor(private api: ApiService) {}
}
事實上,在 'providers' 中列出你的類會創建它的一個新實例,如果任何父組件已經列出它,那么子組件不需要,如果他們這樣做,他們將獲得一個新實例。
傑森完全正確! 這是由依賴注入的工作方式引起的。 它基於分層注入器。
Angular2 應用程序中有幾個注入器:
當 Angular2 嘗試在組件構造函數中注入一些東西時:
因此,如果您想為整個應用程序創建一個單例,則需要在根注入器或應用程序組件注入器級別定義提供程序。
但是 Angular2 會從底部查看注入器樹。 這意味着將使用最低級別的提供程序,並且關聯實例的范圍將是此級別。
有關更多詳細信息,請參閱此問題:
我知道 angular 有像 Thierry 所說的分層注入器。
但是,如果您發現一個用例,您真的不想將它注入到父級,我在這里還有另一個選擇。
我們可以通過創建服務的實例來實現這一點,並在提供時始終返回該實例。
import { provide, Injectable } from '@angular/core';
import { Http } from '@angular/core'; //Dummy example of dependencies
@Injectable()
export class YourService {
private static instance: YourService = null;
// Return the instance of the service
public static getInstance(http: Http): YourService {
if (YourService.instance === null) {
YourService.instance = new YourService(http);
}
return YourService.instance;
}
constructor(private http: Http) {}
}
export const YOUR_SERVICE_PROVIDER = [
provide(YourService, {
deps: [Http],
useFactory: (http: Http): YourService => {
return YourService.getInstance(http);
}
})
];
然后在您的組件上使用您的自定義提供方法。
@Component({
providers: [YOUR_SERVICE_PROVIDER]
})
並且您應該擁有一個不依賴分層注入器的單例服務。
我並不是說這是一種更好的方法,只是以防萬一有人遇到無法使用分層注入器的問題。
語法已更改。 檢查此鏈接
依賴項是注入器范圍內的單例。 在下面的示例中,單個 HeroService 實例在 HeroesComponent 及其 HeroListComponent 子項之間共享。
步驟 1. 使用 @Injectable 裝飾器創建單例類
@Injectable()
export class HeroService {
getHeroes() { return HEROES; }
}
步驟 2. 注入構造函數
export class HeroListComponent {
constructor(heroService: HeroService) {
this.heroes = heroService.getHeroes();
}
步驟 3. 注冊提供商
@NgModule({
imports: [
BrowserModule,
FormsModule,
routing,
HttpModule,
JsonpModule
],
declarations: [
AppComponent,
HeroesComponent,
routedComponents
],
providers: [
HeroService
],
bootstrap: [
AppComponent
]
})
export class AppModule { }
這對我來說似乎很有效
@Injectable()
export class MyStaticService {
static instance: MyStaticService;
constructor() {
return MyStaticService.instance = MyStaticService.instance || this;
}
}
添加@Injectable
裝飾到服務,並登記其作為根模塊提供商將使一個單身。
這是一個使用 Angular 2.3 版的工作示例。 只需像這個構造函數(private _userService:UserService) 那樣以stand 方式調用服務的構造函數。 它將為應用程序創建一個單例。
用戶服務.ts
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Rx';
import { Subject } from 'rxjs/Subject';
import { User } from '../object/user';
@Injectable()
export class UserService {
private userChangedSource;
public observableEvents;
loggedUser:User;
constructor() {
this.userChangedSource = new Subject<any>();
this.observableEvents = this.userChangedSource.asObservable();
}
userLoggedIn(user:User) {
this.loggedUser = user;
this.userChangedSource.next(user);
}
...
}
app.component.ts
import { Component } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { UserService } from '../service/user.service';
import { User } from '../object/user';
@Component({
selector: 'myApp',
templateUrl: './app.component.html'
})
export class AppComponent implements OnInit {
loggedUser:User;
constructor(private _userService:UserService) {
this._userService.observableEvents.subscribe(user => {
this.loggedUser = user;
console.log("event triggered");
});
}
...
}
您可以在提供者中使用useValue
import { MyService } from './my.service';
@NgModule({
...
providers: [ { provide: MyService, useValue: new MyService() } ],
...
})
從 Angular@6 開始,您可以在Injectable
使用providedIn
。
@Injectable({
providedIn: 'root'
})
export class UserService {
}
在 Angular 中有兩種方法可以使服務成為單例:
- 聲明應該在應用程序根中提供服務。
- 將服務包含在 AppModule 或僅由 AppModule 導入的模塊中。
從 Angular 6.0 開始,創建單例服務的首選方法是在服務上指定它應該在應用程序根中提供。 這是通過將 providedIn 設置為服務的 @Injectable 裝飾器上的 root 來完成的:
只需在 app.module.ts 中將您的服務聲明為提供者即可。
它為我完成了工作。
providers: [Topic1Service,Topic2Service,...,TopicNService],
然后使用構造函數私有參數實例化它:
constructor(private topicService: TopicService) { }
或者因為如果您的服務是從 html 使用的,則 -prod 選項將聲明:
Property 'topicService' is private and only accessible within class 'SomeComponent'.
為您的服務添加一個成員,並用構造函數中收到的實例填充它:
export class SomeComponent {
topicService: TopicService;
constructor(private topicService: TopicService) {
this.topicService= topicService;
}
}
singleton service
是一種應用程序中僅存在一個實例的服務。
有(2) 種方法可以為您的應用程序提供單例服務。
使用providedIn
屬性,或
直接在應用的AppModule
中提供模塊
使用提供的輸入
從 Angular 6.0 開始,創建單例服務的首選方法是在服務的@Injectable()
裝飾器@Injectable()
providedIn
為 root。 這告訴 Angular 在應用程序根中提供服務。
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class UserService {
}
NgModule 提供者數組
在使用 Angular 6.0 之前版本構建的應用程序中,服務注冊 NgModule 提供程序數組如下:
@NgModule({
...
providers: [UserService],
...
})
如果此NgModule
是根AppModule
,則 UserService 將是一個單例並在整個應用程序中可用。 盡管您可能會看到它是這樣編碼的, @Injectable()
Angular 6.0 開始,在服務本身上使用@Injectable()
裝飾器的providedIn
屬性是更可取的,因為它使您的服務可以搖樹。
如果你想在應用程序級別制作服務單例,你應該在app.module.ts 中定義它
提供者:[ MyApplicationService ](您也可以在子模塊中定義相同的內容以使其特定於該模塊)
如果您想在組件級別定義單例服務創建服務,請在 app.module.ts 中添加該服務,並在特定組件內添加 providers 數組,如下面的代碼片段所示。
@Component({ selector: 'app-root', templateUrl: './test.component.html', styleUrls: ['./test.component.scss'], providers : [TestMyService] })
Angular 6 提供了在應用程序級別添加服務的新方法。 您可以在 @Injectable() 中設置以下配置,而不是向 AppModule 中的 providers[] 數組添加服務類:
@Injectable({providedIn: 'root'}) 導出類 MyService { ... }
不過,“新語法”確實提供了一個優勢:Angular 可以延遲加載服務(在幕后),並且可以自動刪除冗余代碼。 這可以帶來更好的性能和加載速度——盡管這通常只適用於更大的服務和應用程序。
除了上述出色的答案之外,如果您的單身人士仍然沒有表現得像單身人士,那么可能還缺少其他一些東西。 我在單例上調用公共函數並發現它使用了錯誤的變量時遇到了這個問題。 事實證明,問題在於對於單例中的任何公共函數,不能保證this
綁定到單例。 這可以通過遵循此處的建議來糾正,如下所示:
@Injectable({
providedIn: 'root',
})
export class SubscriptableService {
public serviceRequested: Subject<ServiceArgs>;
public onServiceRequested$: Observable<ServiceArgs>;
constructor() {
this.serviceRequested = new Subject<ServiceArgs>();
this.onServiceRequested$ = this.serviceRequested.asObservable();
// save context so the singleton pattern is respected
this.requestService = this.requestService.bind(this);
}
public requestService(arg: ServiceArgs) {
this.serviceRequested.next(arg);
}
}
或者,您可以簡單地將類成員聲明為public static
而不是public
,然后上下文無關緊要,但是您必須像SubscriptableService.onServiceRequested$
一樣訪問它們,而不是使用依賴注入並通過this.subscriptableService.onServiceRequested$
訪問它們this.subscriptableService.onServiceRequested$
。
我在使用不同實例的父服務及其子服務時遇到了麻煩。 要強制使用一個實例,您可以在應用程序模塊提供程序中引用子項作為父項的別名。 父級將無法訪問子級的屬性,但兩個服務將使用相同的實例。 https://angular.io/guide/dependency-injection-providers#aliased-class-providers
app.module.ts
providers: [
ChildService,
// Alias ParentService w/ reference to ChildService
{ provide: ParentService, useExisting: ChildService}
]
在創建由組件和服務組成的庫時,我遇到了將創建兩個實例的問題。 一個是我的 Angular 項目,一個是我的庫中的組件。 修復:
my-outside.component.ts
@Component({...})
export class MyOutsideComponent {
@Input() serviceInstance: MyOutsideService;
...
}
my-inside.component.ts
constructor(public myService: MyOutsideService) { }
my-inside.component.hmtl
<app-my-outside [serviceInstance]="myService"></app-my-outside>
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.