Как создать одноэлементный сервис в Angular 2?



Я читал, что инъекции при начальной загрузке должны иметь все дочерние элементы с одним и тем же экземпляром, но мои основные и заголовочные компоненты (основное приложение включает компонент заголовка и выход маршрутизатора) получают отдельный экземпляр моих служб.



У меня FacebookService, который я использую, чтобы сделать звонки в facebook API-интерфейса JavaScript и приложения UserService, который использует FacebookService. Вот мой бутстрап:



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


из моего журнала это выглядит как вызов bootstrap заканчивается, затем я вижу, что FacebookService затем UserService создается до запуска кода в каждом из конструкторов, MainAppComponent, HeaderComponent и DefaultComponent:



enter image description here

722   11  

11 ответов:

Джейсон совершенно правы! Это вызвано тем, как работает инъекция зависимости. Он основан на иерархических инжекторах.

в приложении Angular2 есть несколько инжекторов:

  • корневой, который вы настраиваете при загрузке приложения
  • инжектор на компонент. Если вы используете компонент внутри другого. Инжектор компонента является дочерним элементом родительского компонента. Компонент приложения (тот, который вы указываете, когда boostrapping ваше приложение) имеет корневой инжектор в качестве родительского).

когда Angular2 пытается ввести что-то в конструктор компонента:

  • Он смотрит в инжектор, связанный с компонентом. Если есть соответствующий, он будет использовать его, чтобы получить соответствующий экземпляр. Этот экземпляр лениво создан и является синглтоном для этого инжектора.
  • если на этом уровне нет провайдера, он будет смотреть на родительский инжектор (и так на.)

поэтому, если вы хотите иметь синглтон для всего приложения, вам нужно определить поставщика либо на уровне корневого инжектора, либо инжектора компонента приложения.

но Angular2 будет смотреть на дерево инжектора снизу. Это означает, что поставщик на самом низком уровне будет использоваться, и объем, связанный экземпляр будет этот уровень.

смотрите этот вопрос подробнее подробности:

Обновление (Угловой 6)

рекомендуемый способ создания синглтон услуги изменилось. Теперь рекомендуется указать в поле @Injectable декоратор на сервисе, который должен быть предоставлен в "корне". Для меня это имеет большой смысл, и больше нет необходимости перечислять все предоставляемые услуги в ваших модулях. Вы просто импортируете услуги, когда они вам нужны, и они регистрируются в нужном месте. Вы также можете указать модуль поэтому он будет предоставлен только при импорте модуля.

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

Обновление (Угловой 2)

С NgModule, способ сделать это сейчас я думаю, чтобы создать "CoreModule" с вашим классом обслуживания в нем, и список услуг в поставщиках модуля. Затем вы импортируете основной модуль в свой основной модуль приложения, который предоставит один экземпляр для любых детей, запрашивающих этот класс в своих конструкторах:

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

фактически перечисление вашего класса в "поставщиках" создает новый экземпляр этого, если какой-либо родительский компонент уже перечисляет его, то дети не нужно, и если они это сделают, они получат новый экземпляр.

Я знаю, что у angular есть иерархические инжекторы, как сказал Тьерри.

но у меня есть еще один вариант здесь, Если вы найдете вариант использования, когда вы действительно не хотите вводить его у родителя.

мы можем добиться этого, создав экземпляр службы, и на provide всегда возвращать это.

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 decorator

@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 декоратор к службе,и регистрация его в качестве поставщика в корневом модуле сделает его одноэлементным.

вот рабочий пример с угловой версии 2.3. Просто вызовите конструктор службы стенд, как этот конструктор (private _userService: UserService) . И это создаст синглтон для приложения.

пользователей.услуга.ТС

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

    ...
}

приложение.деталь.ТС

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

это, кажется, работает хорошо для меня

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

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

можно использовать useValue в поставщиков

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

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

от углового@6, Вы можете иметь providedIn на Injectable.

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

}

Регистрация документы здесь

есть два способа сделать службу синглтоном в Angular:

  1. объявите, что служба должна быть предоставлена в корне приложения.
  2. включить службу в модуль или модуль, который только импортируемый модуль.

начиная с угловой 6.0, предпочтительным способом создания одноэлементных служб является указание на службе, что она должна быть предоставлена в корне приложения. Это делается путем установки providedIn в root на @Injectable decorator сервиса:

просто объявите свой сервис в качестве поставщика в приложении.модуль.ТС только.

Он сделал эту работу за меня.

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;
  }
}
  1. Если вы хотите сделать сервис синглтон на уровне приложения вы должны определить его в приложение.модуль.ТС

    поставщики: [ MyApplicationService ] (вы можете определить то же самое в дочернем модуле, а также сделать его специфичным для этого модуля)

    • Не добавляйте эту службу в провайдер, который создает экземпляр для этого компонента, который нарушает концепцию синглтона, просто введите его конструктор.
  2. Если вы хотите определить синглтон сервис на уровне компонентов создайте службу, добавьте эту службу в приложение.модуль.ts и добавить в массив поставщиков внутри конкретного компонента, как показано ниже snipet.

Comments

    Ничего не найдено.