development

Angular 2에서 싱글 톤 서비스를 어떻게 만듭니 까?

big-blog 2020. 7. 1. 07:33
반응형

Angular 2에서 싱글 톤 서비스를 어떻게 만듭니 까?


부트 스트랩을 할 때 주입하면 모든 자식이 동일한 인스턴스를 공유해야하지만 내 기본 및 헤더 구성 요소 (주 응용 프로그램에는 헤더 구성 요소 및 라우터 콘센트가 포함됨)가 각각 별도의 서비스 인스턴스를 얻습니다.

Facebook Javascript API 및 FacebookService를 사용하는 UserService를 호출하는 데 사용하는 FacebookService가 있습니다. 내 부트 스트랩은 다음과 같습니다.

bootstrap(MainAppComponent, [ROUTER_PROVIDERS, UserService, FacebookService]);

로깅에서 부트 스트랩 호출이 완료되는 것처럼 보이며 각 생성자의 코드가 실행되기 전에 FacebookService와 UserService가 생성되는 것을 볼 수 있습니다. MainAppComponent, HeaderComponent 및 DefaultComponent :

여기에 이미지 설명을 입력하십시오


Jason은 완전히 옳습니다! 의존성 주입이 작동하는 방식으로 인해 발생합니다. 계층 적 인젝터를 기반으로합니다.

Angular2 애플리케이션에는 여러 인젝터가 있습니다.

  • 응용 프로그램을 부트 스트랩 할 때 구성하는 루트
  • 구성 요소 당 인젝터. 다른 구성 요소 안에 구성 요소를 사용하는 경우 구성 요소 인젝터는 상위 구성 요소의 하위입니다. 응용 프로그램 구성 요소 (응용 프로그램을 증폭시킬 때 지정하는 구성 요소)에는 루트 인젝터가 부모 인젝터가 있습니다.

Angular2가 컴포넌트 생성자에 무언가를 주입하려고 할 때 :

  • 구성 요소와 연관된 인젝터를 찾습니다. 일치하는 것이 있으면이를 사용하여 해당 인스턴스를 가져옵니다. 이 인스턴스는 느리게 생성되며이 인젝터의 싱글 톤입니다.
  • 이 수준에 공급자가 없으면 부모 인젝터 등을 살펴 봅니다.

따라서 전체 응용 프로그램에 대해 싱글 톤을 사용하려면 루트 인젝터 또는 응용 프로그램 구성 요소 인젝터 수준에서 공급자를 정의해야합니다.

그러나 Angular2는 인젝터 트리를 아래에서 볼 것입니다. 즉, 가장 낮은 수준의 공급자가 사용되며 연결된 인스턴스의 범위가이 수준이됩니다.

자세한 내용은이 질문을 참조하십시오.


업데이트 (각도 6)

싱글 톤 서비스 를 만드는 권장 방법 이 변경되었습니다. 이제 @Injectable서비스 데코레이터에서 'root'로 제공 되도록 지정하는 것이 좋습니다 . 이것은 나에게 의미가 있으며 더 이상 모듈에 제공된 서비스를 모두 나열 할 필요가 없습니다. 필요할 때 서비스를 가져 오면 적절한 장소에 등록됩니다. 모듈을 가져와야 만 제공되도록 모듈지정할 수도 있습니다.

@Injectable({
  providedIn: 'root',
})
export class ApiService {
}

업데이트 (각도 2)

NgModule을 사용하여 지금 할 수있는 방법은 서비스 클래스로 'CoreModule'을 만들고 서비스를 모듈 공급자에 나열하는 것입니다. 그런 다음 기본 앱 모듈에서 핵심 모듈을 가져와 생성자에서 해당 클래스를 요청하는 모든 자식에게 하나의 인스턴스를 제공합니다.

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 { }

원래 답변

에 공급자 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) {}
}

실제로 '제공자'에 클래스를 나열하면 새 인스턴스가 만들어지고 부모 구성 요소에 이미 나열되어 있으면 자식이 필요하지 않으며 새 인스턴스가 있으면 새 인스턴스가 생성됩니다.


나는 각도에 티에리와 같은 계층 적 인젝터가 있다는 것을 알고있다.

그러나 부모에게 실제로 주입하고 싶지 않은 유스 케이스를 찾으면 여기에 다른 옵션이 있습니다.

우리는 서비스 인스턴스를 생성하여이를 달성 할 수 있으며, 항상이를 반환해야합니다.

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 { }

Adding @Injectable decorator to the Service, AND registering it as a provider in the Root Module will make it a singleton.


this seems to be working well for me

@Injectable()
export class MyStaticService {
  static instance: MyStaticService;

  constructor() {
    return MyStaticService.instance = MyStaticService.instance || this;
  }
}

Here is a working example with Angular version 2.3. Just call the constructor of the service the stand way like this constructor(private _userService:UserService) . And it will create a singleton for the app.

user.service.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");
        });
    }
    ...
}

You can use useValue in providers

import { MyService } from './my.service';

@NgModule({
...
  providers: [ { provide: MyService, useValue: new MyService() } ],
...
})

From Angular@6, you can have providedIn in an Injectable.

@Injectable({
  providedIn: 'root'
})
export class UserService {

}

Check the docs here

There are two ways to make a service a singleton in Angular:

  1. Declare that the service should be provided in the application root.
  2. Include the service in the AppModule or in a module that is only imported by the AppModule.

Beginning with Angular 6.0, the preferred way to create a singleton services is to specify on the service that it should be provided in the application root. This is done by setting providedIn to root on the service's @Injectable decorator:


Just declare your service as provider in app.module.ts only.

It did the job for me.

providers: [Topic1Service,Topic2Service,...,TopicNService],

then either instanciate it using a constructor private parameter :

constructor(private topicService: TopicService) { }

or since if your service is used from html, the -prod option will claim:

Property 'topicService' is private and only accessible within class 'SomeComponent'.

add a member for your service and fill it with the instance recieved in the constructor:

export class SomeComponent {
  topicService: TopicService;
  constructor(private topicService: TopicService) { 
    this.topicService= topicService;
  }
}

  1. If you want to make service singleton at application level you should define it in app.module.ts

    providers: [ MyApplicationService ] (you can define the same in child module as well to make it that module specific)

    • Do not add this service in provider which creates an instance for that component which breaks the singleton concept, just inject through constructor.
  2. If you want to define singleton service at component level create service, add that service in app.module.ts and add in providers array inside specific component as shown in below snipet.

    @Component({ selector: 'app-root', templateUrl: './test.component.html', styleUrls: ['./test.component.scss'], providers : [TestMyService] })

  3. Angular 6 provide new way to add service at application level. Instead of adding a service class to the providers[] array in AppModule , you can set the following config in @Injectable() :

    @Injectable({providedIn: 'root'}) export class MyService { ... }

The "new syntax" does offer one advantage though: Services can be loaded lazily by Angular (behind the scenes) and redundant code can be removed automatically. This can lead to a better performance and loading speed - though this really only kicks in for bigger services and apps in general.


In addition to the above excellent answers, there may be something else that is missing if things in your singleton still aren't behaving as a singleton. I ran into the issue when calling a public function on the singleton and finding that it was using the wrong variables. It turns out that the problem was the this isn't guaranteed to be bound to the singleton for any public functions in the singleton. This can be corrected by following the advice here, like so:

@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$.

참고 URL : https://stackoverflow.com/questions/36198785/how-do-i-create-a-singleton-service-in-angular-2

반응형