Servis sınıfa nasıl enjekte edilir (bileşene değil)


91

Bileşen olmayan bir sınıfa bir hizmet enjekte etmek istiyorum .

Örneğin:

Myservice

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

Sınıfım

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

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

Bu çözüm işe yaramıyor (sanırım çünkü MyClasshenüz somutlaştırılmadı).

Bir sınıftaki bir hizmeti kullanmanın başka bir yolu var mı (bileşen değil)? Veya kod tasarımımı uygunsuz olarak mı düşünüyorsunuz (bileşen olmayan bir sınıfta bir hizmeti kullanmak)

Teşekkür ederim.

Yanıtlar:


57

Enjeksiyonlar yalnızca Angulars bağımlılık enjeksiyonu (DI) tarafından başlatılan sınıflarla çalışır.

  1. Gerek
    • eklemek @Injectable()için MyClassve
    • sağlamak MyClassgibi providers: [MyClass]bir bileşen ya da NgModule içinde.

Daha sonra bir MyClassyere enjekte ettiğinizde , bir MyServiceörnek MyClassDI tarafından somutlaştırıldığında (ilk seferinde enjekte edilmeden önce) iletilir .

  1. Alternatif bir yaklaşım, özel bir enjektör yapılandırmaktır.
constructor(private injector:Injector) { 
  let resolvedProviders = ReflectiveInjector.resolve([MyClass]);
  let childInjector = ReflectiveInjector.fromResolvedProviders(resolvedProviders, this.injector);

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

Bu yol myClass, MyClassAngulars DI tarafından somutlaştırılmış bir örnek myServiceolacak ve MyClasssomutlaştırıldığında enjekte edilecektir .
Ayrıca bkz. Bir direktif içinde Enjektörden manuel olarak bağımlılık alma

  1. Yine başka bir yol da örneği kendiniz yaratmaktır:
constructor(ms:myService)
let myClass = new MyClass(ms);

2
Bağımsız bir sınıfa hizmet enjekte etmenin bir anti-model olduğunu düşünüyorum.
tomexx

11
Elbette, ama bazen yine de mantıklı olabilir ve neyin mümkün olduğunu bilmek her zaman iyidir
Günter Zöchbauer,

Yapıcıda neden yeni bir enjektör oluşturmalı? Neden enjekte edileni kullanmıyorsunuz? Yeni bir tane yaratacaksanız, o zaman ilk etapta enjekte
ettirmek

Yeni olan, ek sağlayıcılara sahip bir çocuk enjektörüdür. Çocuk enjektöre eklenen sağlayıcılar, ana bileşenlerde veya modül düzeyinde sağlanan sağlayıcılara bağlıysa, DI hepsini otomatik olarak çözebilir. Bu nedenle, kesinlikle bunun mantıklı olduğu durumlar vardır.
Günter Zöchbauer

1
@TimothyPenz düzenleme için teşekkürler. Bu örnekte privategerekli değildir, çünkü injectorkurucu dışında kullanılmamaktadır, bu nedenle bir alanda referans tutmaya gerek yoktur.
Günter Zöchbauer

30

Sorunun doğrudan bir cevabı değil, ama bu SO'yu okuyorsanız, benim nedenim bu yardımcı olabilir ...

Diyelim ki ng2-translate kullanıyorsunuz ve User.tssınıfınızda buna sahip olmasını gerçekten istiyorsunuz . Hemen düşündüğünüz, DI'yi kullanmak için onu koymaktır, sonuçta Angular yapıyorsunuz. Ama bu biraz fazla düşünmek, onu kurucunuza aktarabilir ya da bileşenden ayarladığınız bir genel değişken yapabilirsiniz (muhtemelen onu DI yaptınız).

Örneğin:

import { TranslateService } from "ng2-translate";

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

  // a bunch of awesome User methods
}

daha sonra TranslateService enjekte eden kullanıcıyla ilgili bazı bileşenlerden

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

Umarım bu, benim gibi kodu korumak için çok daha zor yazmak üzere olanlara yardımcı olur çünkü bazen çok zekiyiz =]

(NOT: Yapıcı yönteminizin bir parçası yapmak yerine bir değişken ayarlamak istemenizin nedeni, hizmeti kullanmanız gerekmeyen durumlara sahip olmanızdır, bu nedenle her zaman bunu iletmenizin gerekli olması fazladan içe aktarmalar getirmek anlamına gelir. / asla gerçekten kullanılmayan kod)


ve belki translateServiceayarlamayı getunuttuysanız bir NullReference istisnası yerine anlamlı bir hata attığı bir get / set yapın
Simon_Weaver

1
Belki translateService'i sınıf yapıcısında gerekli bir parametre yapmak daha mantıklıdır? Bu desen, DI desen imho ile daha uyumludur.
Icycool

16

Bu biraz ( çok ) hilekar, ancak çözümümü de paylaşacağımı düşündüm. Bunun yalnızca Singleton hizmetlerinde çalışacağını unutmayın (bileşene değil uygulama köküne enjekte edilir!), Çünkü bunlar uygulamanız kadar uzun yaşarlar ve bunların yalnızca bir örneği vardır.

İlk olarak, hizmetinizde:

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

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

Sonra herhangi bir sınıfta:

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

Kod dağınıklığını azaltmak istiyorsanız ve yine de tekil olmayan hizmetleri kullanmıyorsanız, bu çözüm iyidir.


Ama yapıcıda herhangi bir bildirim olmadan MyClass içinde MyService'i nasıl kullanacaksınız?
Akhil V

@AkhilV, MyService'i başka herhangi bir sınıfı içe aktarır gibi içe aktarırsınız, ardından yöntemi açıklandığı gibi doğrudan çağırabilirsiniz.
Kilves

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
OP'nin senaryosu için en iyi uygulamanın ne olduğundan emin değilim, ancak bu cevap öncü olanlardan çok daha basit görünüyor. injector.get()Çağrıyı modüle taşımak işe yarayacak mı (durum bilgisi olmayan hizmetin birkaç sınıfın aynı örneği "paylaşabileceği" varsayılırsa)?
G0BLiN

Bence bu kod, aynı poney hizmeti örneğini paylaşan tüm poney örnekleri tarafından sona erecek. Ne düşünüyorsun?
Julien

Julien - bu benim senaryom için kabul edilebilir bir sonuç (aslında tercih edilen) - bir giriş doğrulayıcı, kullanıcı izinleri işleyicisi veya yerelleştirme hizmeti gibi bir şey düşünün - uygulamada aynı işlevselliğe ihtiyacınız var, ancak her biri için benzersiz bir örneğe gerek yok hizmetin bireysel tüketicisi.
G0BLiN

Jasmine testlerinde bunu çalıştırmaya çalışırken biraz sorun yaşıyorum. Test kurulumu nasıl yapılandırılır? ServiceLocator.injector, "PoneyService" i TestBed'e eklememe rağmen boş dönmeye devam ediyor?
GGizmos

3

Hizmet yöntemleriniz saf işlevlerse, bunu çözmenin temiz bir yolu, hizmetinizde statik üyelere sahip olmaktır.

servisiniz

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

Senin sınıfın

export class MyClass{
  test(){
     MyService.dosomething(); //no need to inject in constructor
  }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.