typescript - Angular 2'de tekil bir servis nasıl oluştururum?
Başlatma sırasında enjeksiyon yaparken tüm çocukların aynı örneği paylaşması gerektiğini okudum, ama ana ve başlık bileşenlerim (ana uygulama başlık bileşenini ve router-outlet'i içeriyor) servislerimin her biri ayrı bir örneğini alıyor.
Facebook javascript API'sine çağrı yapmak için kullandığım bir FacebookService ve FacebookService kullanan bir UserService var. İşte benim bootstrap'im:
bootstrap(MainAppComponent, [ROUTER_PROVIDERS, UserService, FacebookService]);
Kayıtlarıma göre bootstrap çağrısı bitiyor gibi görünüyor, ardından her bir yapıcıdaki kod çalışmadan önce FacebookService ve sonra UserService'in oluşturulduğunu görüyorum, MainAppComponent, HeaderComponent ve DefaultComponent:
Cevap #1
Güncelleme (Angular 6 +)
Singleton bir servis oluşturmanın önerilen yolu değişti. Artık servisin @Injectable dekoratöründe 'root' içinde sağlanması gerektiğini belirtmek öneriliyor. Bu bana çok mantıklı geliyor ve modüllerinizde sağlanan tüm servisleri listelemenize artık gerek yok. Servisleri sadece ihtiyaç duyduğunuzda import ediyorsunuz ve kendilerini doğru yerde kaydediyorlar. Ayrıca bir modül belirtebilirsiniz, böylece servis yalnızca modül import edilirse sağlanır.
@Injectable({
providedIn: 'root',
})
export class ApiService {
}
- https://angular.io/guide/singleton-services
- https://angular.io/guide/providers
Güncelleme (Angular 2)
NgModule ile, bunu yapmanın yolu bence şimdi, servis sınıfınızı içeren bir 'CoreModule' oluşturmak ve servisi modülün providers kısmına listelemektir. Sonra ana uygulama modülünüzde core modülü ithal edersiniz, bu da constructor'larında bu sınıfı isteyen herhangi bir çocuğa tek bir örnek sağlar:
CoreModule.ts
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 { }
AppModule.ts
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 { }
Orijinal Cevap
Eğer bir sağlayıcıyı bootstrap() içinde listelerseniz, bileşen dekoratörünüzde onları listelemenize gerek yoktur:
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) {}
}
Aslında sınıfınızı 'providers' içinde listelemek, onun yeni bir örneğini oluşturur; eğer herhangi bir üst bileşen zaten listelemişse, alt bileşenlerin listelemesine gerek yoktur ve eğer listelerlerse yeni bir örnek alacaklardır.
Cevap #2
Jason tamamen haklı! Bu, bağımlılık enjeksiyonunun çalışma şekli nedeniyle oluyor. Hiyerarşik enjeksiyonlara dayanıyor.
Bir Angular2 uygulamasında birkaç enjektör vardır:
- Uygulamanızı başlatırken yapılandırdığınız kök biri
- Her bileşen için bir enjektör. Bir bileşeni başka bir bileşenin içinde kullanırsanız. Bileşen enjektörü, ana bileşen birin çocuğudur. Uygulama bileşeni (uygulamanızı boostrap yaparken belirttiğiniz) kök enjektörünü ana bileşen olarak kullanır.
Angular2 bileşen yapıcısına bir şey enjekte etmeye çalıştığında:
- Bileşenle ilişkili olan enjektöre bakar. Eğer eşleşen bir tane varsa, ilgili örneği almak için onu kullanır. Bu örnek tembel bir şekilde oluşturulur ve bu enjektör için tekil bir örnektir.
- Eğer bu seviyede bir sağlayıcı yoksa, üst enjekte edicisine bakacaktır (ve bu şekilde devam eder).
Yani, tüm uygulama için bir singleton'a sahip olmak istiyorsanız, sağlayıcının ya kök enjekte edici seviyesinde ya da uygulama bileşeni enjekte edicisinde tanımlanması gerekir.
Ama Angular2, enjeksiyon ağacına aşağıdan bakacaktır. Bu, en alt seviyedeki sağlayıcının kullanılacağı ve ilişkilendirilen örneğin kapsamının bu seviye olacağı anlamına gelir.
Daha fazla ayrıntı için bu soruya bakın:
- Angular 2 (Beta) modunda bir servisi diğerine enjekte etmenin en iyi yolu nedir?
Cevap #3
Thierry'nin dediği gibi Angular'ın hiyerarşik enjektörleri olduğunu biliyorum.
Ama eğer gerçekten onu ebeveyne enjekte etmek istemediğiniz bir kullanım durumu bulursanız burada başka bir seçeneğim var.
Bunu, servisin bir örneğini oluşturarak başarabiliriz ve provide üzerinde her zaman onu döndürebiliriz.
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);
}
})
];
Ve sonra bileşeninizde özel sağlama yönteminizi kullanırsınız.
@Component({
providers: [YOUR_SERVICE_PROVIDER]
})
Ve hiyerarşik enjektörlere bağlı olmadan tek bir servisiniz olmalı.
Bunun daha iyi bir yol olduğunu söylemiyorum, sadece hiyerarşik enjeksiyonların mümkün olmadığı bir durumda biriyle ilgili bir sorun olursa diye.
Cevap #4
Sözdizimi değiştirildi. Bu bağlantıyı kontrol edin
Bağımlılıklar, bir enjekte edici kapsamı içinde tekil örneklerdir. Aşağıdaki örnekte, tek bir HeroService örneği HeroesComponent ve onun HeroListComponent çocukları arasında paylaşılmaktadır.
Adım 1. @Injectable dekoratörü ile tekil sınıf oluşturun
@Injectable()
export class HeroService {
getHeroes() { return HEROES; }
}
Adım 2. Yapıcıya enjekte et
export class HeroListComponent {
constructor(heroService: HeroService) {
this.heroes = heroService.getHeroes();
}
Adım 3. Sağlayıcıyı kaydet
@NgModule({
imports: [
BrowserModule,
FormsModule,
routing,
HttpModule,
JsonpModule
],
declarations: [
AppComponent,
HeroesComponent,
routedComponents
],
providers: [
HeroService
],
bootstrap: [
AppComponent
]
})
export class AppModule { }
