Cara menyuntikkan layanan ke dalam kelas (bukan komponen)

91

Saya ingin memasukkan layanan ke dalam kelas yang bukan merupakan komponen .

Sebagai contoh:

Myservice

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

Kelasku

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

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

Solusi ini tidak berhasil (saya pikir karena MyClass belum dibuat instance-nya).

Apakah ada cara lain untuk menggunakan layanan di kelas (bukan komponen)? Atau apakah Anda menganggap desain kode saya tidak sesuai (untuk menggunakan layanan di kelas yang bukan komponen)?

Terima kasih.

Elec
sumber

Jawaban:

57

Injeksi hanya berfungsi dengan kelas yang dibuat oleh injeksi ketergantungan Angulars (DI).

  1. Kamu butuh
    • tambahkan @Injectable()ke MyClassdan
    • menyediakan MyClassseperti providers: [MyClass]di komponen atau NgModule.

Saat Anda kemudian menyuntikkan di MyClasssuatu tempat, sebuah MyServiceinstance akan diteruskan ke MyClasssaat dibuatkan oleh DI (sebelum disuntikkan pertama kali).

  1. Pendekatan alternatif adalah dengan mengkonfigurasi seperti injektor khusus
constructor(private injector:Injector) { 
  let resolvedProviders = ReflectiveInjector.resolve([MyClass]);
  let childInjector = ReflectiveInjector.fromResolvedProviders(resolvedProviders, this.injector);

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

Cara ini myClassakan menjadi sebuah MyClassinstance, dibuat oleh Angulars DI, dan myServiceakan disuntikkan ke MyClasssaat dibuat instance-nya.
Lihat juga Mendapatkan ketergantungan dari Injector secara manual di dalam direktif

  1. Cara lain adalah membuat sendiri instance tersebut:
constructor(ms:myService)
let myClass = new MyClass(ms);
Günter Zöchbauer
sumber
2
Saya pikir menyuntikkan layanan ke kelas mandiri adalah anti-pola.
tomexx
11
Tentu, tapi terkadang itu masih masuk akal dan selalu baik untuk mengetahui apa yang mungkin
Günter Zöchbauer
Mengapa membuat injektor baru di konstruktor? Mengapa tidak menggunakan yang telah disuntikkan? Jika Anda ingin membuat yang baru maka tidak ada alasan untuk menyuntikkannya terlebih dahulu
smac89
Yang baru adalah injektor anak dengan penyedia tambahan. Jika penyedia yang ditambahkan ke injektor anak bergantung pada penyedia yang disediakan di komponen induk atau di tingkat modul, maka DI dapat menyelesaikan semuanya secara otomatis. Oleh karena itu, pasti ada situasi di mana ini masuk akal.
Günter Zöchbauer
1
@TimothyPenz terima kasih atas pengeditannya. Dalam contoh privateini tidak diperlukan, karena injectortidak digunakan di luar konstruktor, oleh karena itu tidak perlu menyimpan referensi di bidang.
Günter Zöchbauer
30

Bukan jawaban langsung untuk pertanyaan tersebut, tetapi jika Anda membaca ini SO karena alasan saya ini dapat membantu ...

Katakanlah Anda menggunakan ng2-translate dan Anda benar-benar ingin User.tskelas Anda memilikinya. Anda langsung terpikir adalah menggunakan DI untuk memasukkannya, lagipula Anda melakukan Angular. Tapi itu agak berlebihan, Anda bisa meneruskannya di konstruktor Anda, atau menjadikannya variabel publik yang Anda setel dari komponen (di mana Anda mungkin melakukannya).

misalnya:

import { TranslateService } from "ng2-translate";

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

  // a bunch of awesome User methods
}

lalu dari beberapa komponen terkait pengguna yang memasukkan TranslateService

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

Mudah-mudahan ini membantu orang-orang di luar sana seperti saya yang akan menulis lebih sulit untuk mempertahankan kode karena terkadang kita terlalu pintar =]

(CATATAN: alasan Anda mungkin ingin menyetel variabel daripada menjadikannya bagian dari metode konstruktor Anda adalah Anda dapat mengalami kasus di mana Anda tidak perlu menggunakan layanan, jadi selalu diminta untuk meneruskannya berarti memasukkan impor tambahan / kode yang tidak pernah benar-benar digunakan)

Ryan Crews
sumber
dan mungkin membuat translateServiceget / set di mana getmelempar kesalahan yang berarti daripada pengecualian NullReference jika Anda lupa menyetelnya
Simon_Weaver
1
Mungkin lebih masuk akal untuk menjadikan translateService sebagai parameter yang diperlukan dalam konstruktor kelas? Pola ini lebih sejalan dengan pola DI imho.
Icycool
16

Ini agak ( sangat ) hacky, tetapi saya pikir saya juga akan membagikan solusi saya. Perhatikan bahwa ini hanya akan bekerja dengan layanan Singleton (disuntikkan di root aplikasi, bukan komponen!), Karena mereka hidup selama aplikasi Anda, dan hanya ada satu contoh darinya.

Pertama, dalam layanan Anda:

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

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

Kemudian di kelas mana pun:

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

Solusi ini bagus jika Anda ingin mengurangi kekacauan kode dan tidak menggunakan layanan non-tunggal.

Kilves
sumber
Tapi bagaimana Anda menggunakan MyService di MyClass tanpa deklarasi di konstruktor?
Akhil V
@AkhilV Anda cukup mengimpor MyService seperti Anda akan mengimpor kelas lain, lalu Anda dapat langsung memanggil metode seperti yang dijelaskan.
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
}
Julien
sumber
1
Tidak yakin apa praktik terbaik untuk skenario OP, tetapi jawaban ini tampaknya jauh lebih sederhana daripada yang terdepan. Akankah memindahkan injector.get()panggilan ke modul berfungsi (dengan asumsi layanan stateless sehingga beberapa kelas dapat "berbagi" instance yang sama)?
G0BLiN
Saya pikir kode itu akan berakhir dengan semua contoh poney yang berbagi contoh layanan poney yang sama. Bagaimana menurut anda?
Julien
Julien - itu adalah hasil yang dapat diterima (sebenarnya yang lebih disukai) untuk skenario saya - pikirkan sesuatu seperti validator input, penangan izin pengguna atau layanan pelokalan - di mana Anda memerlukan fungsi yang sama di seluruh aplikasi tetapi tidak perlu contoh unik untuk masing-masing konsumen individu layanan.
G0BLiN
Mengalami kesulitan mencoba membuat ini berfungsi dalam tes Jasmine. Bagaimana cara mengkonfigurasi pengaturan pengujian? ServiceLocator.injector terus mengembalikan null, meskipun saya telah menyuntikkan "PoneyService" ke TestBed?
GGizmos
3

Jika metode layanan Anda adalah fungsi murni, cara bersih untuk mengatasinya adalah dengan memiliki anggota statis di layanan Anda.

layanan Anda

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

kelasmu

export class MyClass{
  test(){
     MyService.dosomething(); //no need to inject in constructor
  }
}
dasfdsa
sumber