Як ввести службу в клас (не компонент)


88

Я хочу ввести службу в клас, який не є компонентом .

Наприклад:

Myservice

import {Injectable} from '@angular/core';
@Injectable()
export class myService {
  dosomething() {
    // implementation
  }
}

Мій клас

import { myService } from './myService'
export class MyClass {
  constructor(private myservice:myService) {

  }
  test() {
     this.myservice.dosomething();
  }
}

Це рішення не працює (я думаю, оскільки MyClassще не було інстанційовано).

Чи є інший спосіб використовувати службу в класі (не компонент)? Або ви вважаєте мою розробку коду неприйнятною (використовувати службу в класі, який не є компонентом)?

Дякую.

Відповіді:


56

Ін'єкції працюють лише з класами, які створюються за допомогою ін'єкції залежностей Angulars (DI).

  1. Тобі потрібно
    • додати @Injectable()до MyClassта
    • надати MyClassяк providers: [MyClass]у компоненті або NgModule.

Коли ви MyClassдесь ін'єктуєте , MyServiceекземпляр передається, MyClassколи він створюється за допомогою DI (перед тим, як його вводити вперше).

  1. Альтернативний підхід - це налаштування власної форсунки типу
constructor(private injector:Injector) { 
  let resolvedProviders = ReflectiveInjector.resolve([MyClass]);
  let childInjector = ReflectiveInjector.fromResolvedProviders(resolvedProviders, this.injector);

  let myClass : MyClass = childInjector.get(MyClass);
}

Цей спосіб myClassбуде MyClassекземпляром, інстанційованим Angulars DI, і myServiceбуде введений в MyClassінстанцію.
Див. Також Отримання залежності від Injector вручну всередині директиви

  1. Ще один спосіб - створити екземпляр самостійно:
constructor(ms:myService)
let myClass = new MyClass(ms);

2
Я думаю, що ін’єкційне обслуговування автономного класу є анти-шаблоном.
tomexx

11
Звичайно, але іноді це все одно може мати сенс, і завжди добре знати, що можливо
Гюнтер Цохбауер,

Навіщо створювати новий інжектор у конструкторі? Чому б не використовувати ту, яку ввели? Якщо ви збираєтеся створити новий, тоді немає жодних причин, щоб його вводили спочатку
smac89

Новий - це дитячий інжектор з додатковими постачальниками. Якщо постачальники, додані до дочірньої форсунки, залежать від постачальників, які надаються на батьківських компонентах або на рівні модуля, то DI може вирішити все це автоматично. Тому, безумовно, бувають ситуації, коли це має сенс.
Günter Zöchbauer,

1
@TimothyPenz дякую за редагування. У цьому прикладі privateце не потрібно, оскільки injectorне використовується поза конструктором, тому немає необхідності зберігати посилання в полі.
Günter Zöchbauer

29

Не пряма відповідь на запитання, але якщо ви читаєте цей SO з тієї причини, що я є, це може допомогти ...

Скажімо, ви використовуєте ng2-translate і дуже хочете, User.tsщоб його мав ваш клас. Ви одразу думаєте, що використовувати DI для його введення, ви все-таки робите Angular. Але це начебто надмірно, ви можете просто передати його у свій конструктор або зробити загальнодоступною змінною, яку ви встановили з компонента (де ви, мабуть, зробили це DI).

наприклад:

import { TranslateService } from "ng2-translate";

export class User {
  public translateService: TranslateService; // will set from components.

  // a bunch of awesome User methods
}

потім з якогось пов'язаного з користувачем компонента, який ввів TranslateService

addEmptyUser() {
  let emptyUser = new User("", "");
  emptyUser.translateService = this.translateService;
  this.users.push(emptyUser);
}

Сподіваємось, це допомагає таким людям, як я, котрі збиралися написати набагато складніше для підтримки коду, тому що ми іноді занадто кмітливі =]

(ПРИМІТКА. Причиною того, що вам може знадобитися встановити змінну замість того, щоб робити її частиною вашого методу конструктора, є те, що у вас можуть бути випадки, коли вам не потрібно користуватися службою, тому завжди вимагати її передачі означатиме введення додаткового імпорту / код, який насправді ніколи не використовується)


і, можливо, зробити translateServiceget / set, де getвикине значущу помилку замість винятку NullReference, якщо ви забули встановити її
Simon_Weaver

1
Може, має сенс зробити translateService необхідним параметром у конструкторі класу? Цей шаблон більше відповідає шаблону DI imho.
Icycool

15

Це свого роду ( дуже ) хакі, але я думав поділитися своїм рішенням. Зауважте, що це буде працювати лише зі службами Singleton (вводяться в корені програми, а не компонентом!), Оскільки вони живуть стільки, скільки працює ваша програма, і їх є лише один примірник.

По-перше, до ваших послуг:

@Injectable()
export class MyService {
    static instance: MyService;
    constructor() {
        MyService.instance = this;
    }

    doSomething() {
        console.log("something!");
    }
}

Тоді в будь-якому класі:

export class MyClass {
    constructor() {
        MyService.instance.doSomething();
    }
}

Це рішення добре, якщо ви хочете зменшити безлад коду і все одно не користуєтесь послугами, що не належать до одного.


Але як ви використовуєте MyService в MyClass без будь-якого оголошення в конструкторі?
Ахіл V

@AkhilV ви просто імпортуєте MyService, як і будь-який інший клас, тоді ви можете безпосередньо викликати метод, як описано.
Кілвес,

13

locator.service.ts

import {Injector} from "@angular/core";

export class ServiceLocator {
    static injector: Injector;
}

app.module.ts

@NgModule({ ... })

export class AppModule {
    constructor(private injector: Injector) {
        ServiceLocator.injector = injector;
    }
 }

poney.model.ts

export class Poney {

    id: number;
    name: string;
    color: 'black' | 'white' | 'brown';

    service: PoneyService = ServiceLocator.injector.get(PoneyService); // <--- HERE !!!

    // PoneyService is @injectable and registered in app.module.ts
}

1
Не впевнені, яка найкраща практика для сценарію ОП, але ця відповідь здається набагато простішою, ніж провідні. Чи буде переміщення injector.get()виклику до модуля спрацьовувати (припускаючи службу без громадянства, щоб декілька класів могли «ділитися» одним і тим самим екземпляром)?
G0BLiN

Я думаю, що код закінчиться усіма екземплярами poney, що мають один і той же екземпляр служби poney. Що ти думаєш?
Жульєн

Жульєн - це прийнятний результат (насправді кращий) для мого сценарію - подумайте про щось на зразок валідатора введення, обробника дозволів користувача або служби локалізації - де вам потрібна однакова функціональність у додатку, але немає необхідності в унікальному екземплярі для кожного індивідуальний споживач послуги.
G0BLiN

3

Якщо ваші методи обслуговування - це чисті функції, чистий спосіб вирішити це - мати статичні члени у вашій службі.

Вашу послугу

import {Injectable} from '@angular/core';
@Injectable()
export class myService{
  public static dosomething(){
    //implementation => doesn't use `this`
  }
}

ваш клас

export class MyClass{
  test(){
     MyService.dosomething(); //no need to inject in constructor
  }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.