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

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

페이스 북 자바 스크립트 API와 FacebookService를 사용하는 UserService를 호출하는 데 사용하는 FacebookService가 있습니다. 내 부트 스트랩은 다음과 같습니다.

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

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

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



답변

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

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

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

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

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

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

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

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


답변

업데이트 (Angular 6 +)

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

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


답변

추가 @Injectable서비스에 장식을, 그리고 그것을 싱글 것 루트 모듈의 공급 업체로 등록.


답변

이것은 나를 위해 잘 작동하는 것 같습니다

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

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


답변

다음은 Angular 버전 2.3의 실제 예제입니다. 이 생성자 (private _userService : UserService)와 같은 방식으로 서비스 생성자를 호출하십시오. 그리고 앱의 싱글 톤을 생성합니다.

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