Cómo inyectar el servicio en la clase (no en el componente)

88

Quiero inyectar un servicio en una clase que no es un componente .

Por ejemplo:

Myservice

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

Mi clase

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

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

Esta solución no funciona (creo que porque MyClassaún no se ha creado una instancia).

¿Hay otra forma de utilizar un servicio en una clase (no un componente)? ¿O consideraría que el diseño de mi código es inapropiado (para usar un servicio en una clase que no es un componente)?

Gracias.

Elec
fuente

Respuestas:

56

Las inyecciones solo funcionan con clases instanciadas por inyección de dependencia (DI) de Angulars.

  1. Necesitas
    • añadir @Injectable()a MyClassy
    • proporcionar MyClasscomo providers: [MyClass]en un componente o NgModule.

Cuando luego inyecta en MyClassalgún lugar, una MyServiceinstancia se pasa a MyClasscuando DI crea una instancia (antes de que se inyecte la primera vez).

  1. Un enfoque alternativo es configurar un inyector personalizado como
constructor(private injector:Injector) { 
  let resolvedProviders = ReflectiveInjector.resolve([MyClass]);
  let childInjector = ReflectiveInjector.fromResolvedProviders(resolvedProviders, this.injector);

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

De esta forma myClassserá una MyClassinstancia, instanciada por Angulars DI, y myServicese inyectará MyClasscuando se instancia.
Consulte también Obtener dependencia de Injector manualmente dentro de una directiva

  1. Otra forma más es crear la instancia usted mismo:
constructor(ms:myService)
let myClass = new MyClass(ms);
Günter Zöchbauer
fuente
2
Creo que inyectar servicio a una clase independiente es un anti-patrón.
tomexx
11
Claro, pero a veces todavía puede tener sentido y siempre es bueno saber qué es posible
Günter Zöchbauer
¿Por qué crear un nuevo inyector en el constructor? ¿Por qué no usar el que se inyectó? Si va a crear uno nuevo, entonces no hay razón para inyectarse uno en primer lugar
smac89
El nuevo es un inyector infantil con proveedores adicionales. Si los proveedores agregados al inyector secundario dependen de proveedores que se proporcionan en los componentes principales o en el nivel de módulo, entonces DI puede resolverlo todo automáticamente. Por lo tanto, definitivamente hay situaciones en las que esto tiene sentido.
Günter Zöchbauer
1
@TimothyPenz gracias por la edición. En este ejemplo, privateno es necesario, porque injectorno se usa fuera del constructor, por lo tanto, no es necesario mantener una referencia en un campo.
Günter Zöchbauer
29

No es una respuesta directa a la pregunta, pero si está leyendo esto SO por la razón por la que soy, esto puede ayudar ...

Digamos que estás usando ng2-translate y realmente quieres que tu User.tsclase lo tenga. Su pensamiento inmediato es usar DI para ponerlo, después de todo, está haciendo Angular. Pero eso es como pensarlo demasiado, puede simplemente pasarlo en su constructor, o convertirlo en una variable pública que estableció desde el componente (donde presumiblemente lo hizo DI).

p.ej:

import { TranslateService } from "ng2-translate";

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

  // a bunch of awesome User methods
}

luego de algún componente relacionado con el usuario que inyectó TranslateService

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

Espero que esto ayude a aquellos que, como yo, estaban a punto de escribir un código mucho más difícil de mantener porque a veces somos demasiado inteligentes =]

(NOTA: la razón por la que es posible que desee establecer una variable en lugar de convertirla en parte de su método de constructor es que podría haber casos en los que no necesite usar el servicio, por lo que siempre se le solicitará que la pase significaría introducir importaciones adicionales / código que nunca se usa realmente)

Ryan Crews
fuente
y tal vez haga translateServiceun get / set donde getarroje un error significativo en lugar de una excepción NullReference si olvidó configurarlo
Simon_Weaver
1
¿Quizás tenga más sentido hacer que translateService sea un parámetro obligatorio en el constructor de la clase? Este patrón está más en línea con el patrón DI en mi humilde opinión.
Icycool
15

Esto es un poco ( muy ) hacky, pero pensé en compartir mi solución también. Tenga en cuenta que esto solo funcionará con los servicios de Singleton (inyectados en la raíz de la aplicación, no en el componente), ya que viven tanto como su aplicación, y solo hay una instancia de ellos.

Primero, a su servicio:

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

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

Luego en cualquier clase:

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

Esta solución es buena si desea reducir el desorden de código y no está utilizando servicios que no sean singleton de todos modos.

Kilves
fuente
Pero, ¿cómo se usa MyService en MyClass sin ninguna declaración en el constructor?
Akhil V
@AkhilV, solo importa MyService como importaría cualquier otra clase, luego puede llamar directamente al método como se describe.
Kilves hace
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
fuente
1
No estoy seguro de cuál es la mejor práctica para el escenario de OP, pero esta respuesta parece mucho más simple que las principales. ¿Funcionará mover la injector.get()llamada al módulo (asumiendo un servicio sin estado para que varias clases puedan "compartir" la misma instancia)?
G0BLiN
Creo que el código terminaría con todas las instancias poney compartiendo la misma instancia de servicio poney. ¿Qué piensas?
Julien
Julien, ese es un resultado aceptable (en realidad, el preferido) para mi escenario, piense en algo como un validador de entrada, un controlador de permisos de usuario o un servicio de localización, donde necesita la misma funcionalidad en toda la aplicación, pero no hay necesidad de una instancia única para cada uno. consumidor individual del servicio.
G0BLiN
3

Si sus métodos de servicio son funciones puras, una forma limpia de resolver esto es tener miembros estáticos en su servicio.

tu servicio

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

Tu clase

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