Cách đưa dịch vụ vào lớp (không phải thành phần)


89

Tôi muốn đưa một dịch vụ vào một lớp không phải là một thành phần .

Ví dụ:

Myservice

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

Lớp học của tôi

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

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

Giải pháp này không hoạt động (tôi nghĩ vì MyClasschưa được khởi tạo).

Có cách nào khác để sử dụng một dịch vụ trong một lớp (không phải thành phần) không? Hay bạn sẽ coi thiết kế mã của tôi là không phù hợp (để sử dụng một dịch vụ trong một lớp không phải là một thành phần)?

Cảm ơn bạn.

Câu trả lời:


56

Tiêm chỉ hoạt động với các lớp được khởi tạo bằng tiêm phụ thuộc Angulars (DI).

  1. Bạn cần phải
    • thêm @Injectable()vào MyClass
    • cung cấp MyClassnhư providers: [MyClass]trong một thành phần hoặc NgModule.

Sau MyClassđó, khi bạn tiêm vào một nơi nào đó, một MyServicecá thể sẽ được chuyển đến MyClasskhi nó được DI khởi tạo (trước khi nó được tiêm lần đầu tiên).

  1. Một cách tiếp cận khác là định cấu hình một bộ phun tùy chỉnh như
constructor(private injector:Injector) { 
  let resolvedProviders = ReflectiveInjector.resolve([MyClass]);
  let childInjector = ReflectiveInjector.fromResolvedProviders(resolvedProviders, this.injector);

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

Cách này myClasssẽ là một MyClassthể hiện, được khởi tạo bởi Angulars DI, và myServicesẽ được đưa vào MyClasskhi được khởi tạo.
Xem thêm Nhận phụ thuộc từ Injector theo cách thủ công bên trong một chỉ thị

  1. Tuy nhiên, một cách khác là tự tạo phiên bản:
constructor(ms:myService)
let myClass = new MyClass(ms);

2
Tôi nghĩ rằng việc tiêm dịch vụ vào một lớp độc lập là một cách chống đối.
tomexx

11
Chắc chắn rồi, nhưng đôi khi nó vẫn có ý nghĩa và luôn tốt khi biết điều gì có thể xảy ra
Günter Zöchbauer

Tại sao phải tạo một kim phun mới trong hàm tạo? Tại sao không sử dụng cái đã được tiêm? Nếu bạn đang đi để tạo ra một cái mới thì không có lý do gì để có một tiêm ở nơi đầu tiên
smac89

Loại mới là một máy tiêm trẻ em với các nhà cung cấp bổ sung. Nếu các nhà cung cấp được thêm vào bộ tiêm con phụ thuộc vào các nhà cung cấp được cung cấp ở các thành phần chính hoặc ở cấp mô-đun, thì DI có thể giải quyết tất cả tự động. Do đó chắc chắn có những tình huống mà điều này có ý nghĩa.
Günter Zöchbauer

1
@TimothyPenz cảm ơn bạn đã chỉnh sửa. Trong ví dụ này, điều này privatelà không cần thiết, bởi vì injectornó không được sử dụng bên ngoài hàm tạo, do đó không cần phải giữ một tham chiếu trong một trường.
Günter Zöchbauer

29

Không phải là câu trả lời trực tiếp cho câu hỏi, nhưng nếu bạn đang đọc SO này vì lý do tôi là tôi thì điều này có thể giúp ích ...

Giả sử bạn đang sử dụng ng2-translate và bạn thực sự muốn User.tslớp học của mình có nó. Ngay lập tức bạn nghĩ là sử dụng DI để đưa nó vào, sau cùng thì bạn đang làm Angular. Nhưng đó là kiểu suy nghĩ quá kỹ, bạn chỉ có thể chuyển nó vào hàm tạo của mình hoặc biến nó thành một biến công khai mà bạn đặt từ thành phần (nơi bạn có lẽ đã DI nó vào).

ví dụ:

import { TranslateService } from "ng2-translate";

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

  // a bunch of awesome User methods
}

sau đó từ một số thành phần liên quan đến người dùng đã đưa TranslateService

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

Hy vọng rằng điều này sẽ giúp những người ngoài kia như tôi, những người sắp viết code khó bảo trì hơn vì đôi khi chúng ta quá thông minh =]

(LƯU Ý: lý do bạn có thể muốn đặt một biến thay vì biến nó thành một phần của phương thức khởi tạo của bạn là bạn có thể có những trường hợp bạn không cần sử dụng dịch vụ, vì vậy luôn được yêu cầu chuyển nó vào có nghĩa là bạn phải nhập thêm / mã không bao giờ thực sự được sử dụng)


và có thể làm cho translateServicemột get / set nơi getném một lỗi có ý nghĩa thay vì một ngoại lệ NullReference nếu bạn quên để thiết lập nó
Simon_Weaver

1
Có lẽ sẽ có ý nghĩa hơn khi làm cho translateService trở thành một tham số bắt buộc trong phương thức khởi tạo lớp? Mẫu này phù hợp hơn với imho mẫu DI.
Icycool

15

Đây là loại ( rất ) hacky, nhưng tôi nghĩ rằng tôi cũng sẽ chia sẻ giải pháp của mình. Lưu ý rằng điều này sẽ chỉ hoạt động với các dịch vụ Singleton (được đưa vào tại gốc ứng dụng, không phải thành phần!), Vì chúng tồn tại lâu như ứng dụng của bạn và chỉ có một phiên bản duy nhất của chúng.

Đầu tiên, trong dịch vụ của bạn:

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

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

Sau đó, trong bất kỳ lớp nào:

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

Giải pháp này là tốt nếu bạn muốn giảm sự lộn xộn của mã và không sử dụng các dịch vụ không phải singleton.


Nhưng làm cách nào để bạn sử dụng MyService trong MyClass mà không cần khai báo trong hàm tạo?
Akhil V

@AkhilV bạn chỉ cần nhập MyService như khi nhập bất kỳ lớp nào khác, sau đó bạn có thể gọi trực tiếp phương thức như được mô tả.

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
Không chắc cách thực hành tốt nhất cho kịch bản của OP là gì, nhưng câu trả lời này có vẻ đơn giản hơn nhiều so với câu trả lời hàng đầu. Việc di chuyển lệnh injector.get()gọi đến mô-đun có hoạt động không (giả sử dịch vụ không trạng thái để một số lớp có thể "chia sẻ" cùng một thể hiện)?
G0BLiN

Tôi nghĩ rằng mã đó sẽ kết thúc bởi tất cả các phiên bản poney chia sẻ cùng một phiên bản dịch vụ poney. Bạn nghĩ sao?
Julien

Julien - đó là kết quả có thể chấp nhận được (thực sự là kết quả được ưu tiên) đối với tình huống của tôi - hãy nghĩ về một thứ gì đó như trình xác thực đầu vào, trình xử lý quyền người dùng hoặc dịch vụ bản địa hóa - nơi bạn cần cùng một chức năng trên ứng dụng nhưng không cần một phiên bản duy nhất cho mỗi người tiêu dùng cá nhân của dịch vụ.
G0BLiN

Gặp khó khăn khi cố gắng làm cho điều này hoạt động trong các bài kiểm tra của Jasmine. Làm thế nào để định cấu hình thiết lập thử nghiệm? ServiceLocator.injector tiếp tục trả về null, mặc dù tôi đã đưa "PoneyService" vào TestBed?
user1023110

3

Nếu các phương thức dịch vụ của bạn là các chức năng thuần túy, thì một cách rõ ràng để giải quyết vấn đề này là có các thành viên tĩnh trong dịch vụ của bạn.

dịch vụ của bạn

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

lớp của bạn

export class MyClass{
  test(){
     MyService.dosomething(); //no need to inject in constructor
  }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.