Elec
Elec

Reputation: 1744

How to inject service into class (not component)

I'd like to inject a service into a class that is not a component.

For example:

Myservice

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

MyClass

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

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

I tried and it doesn't work. It seems like service need to be used in only component or service.

Is there a way to use a service in a normal class? or it's a bad practice to use a service in a normal class.

Thank you.

Upvotes: 125

Views: 95472

Answers (7)

Vahid
Vahid

Reputation: 7551

Since Angular 14 you can use the inject function. But it should only be called in the constructor.

import { MyService } from './my.service'
export class MyClass {
  private myService = inject(MyService);

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

elsewhere

import { MyClass } from './myClass'
@component()
export class MyComponent {
  constructor() {
    const myClass = new MyClass();
  }
}

Upvotes: 5

Flavien Volken
Flavien Volken

Reputation: 21259

You can also make use of a factory like the MyClassFactory as follows:

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

@Injectable()
export class MyClassFactory {
  constructor(private myService:MyService) { }

  createMyClass() {
    return new MyClass(this.myService)
  }
}
import { MyService } from './MyService'
export class MyClass {
  constructor(private myService:MyService) { }
  test() {
     this.myservice.dosomething();
  }
}

Upvotes: 0

Günter Zöchbauer
Günter Zöchbauer

Reputation: 657058

Injections only works with classes that are instantiated by Angulars dependency injection (DI).

  1. You need to
  • add @Injectable() to MyClass and
  • provide MyClass like providers: [MyClass] in a component or NgModule.

When you then inject MyClass somewhere, a MyService instance gets passed to MyClass when it is instantiated by DI (before it is injected the first time).

  1. An alternative approach is to configure a custom injector like

With the new static injector

constructor(private injector:Injector) { 
  let childInjector = Injector.create({ providers: [MyClass], parent: this.injector});

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

With the deprecated ReflectiveInjector

constructor(private injector:Injector) { 
  let resolvedProviders = ReflectiveInjector.resolve([MyClass]);
  let childInjector = ReflectiveInjector.fromResolvedProviders(resolvedProviders, this.injector);

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

This way myClass will be a MyClass instance, instantiated by Angulars DI, and myService will be injected to MyClass when instantiated.
See also Getting dependency from Injector manually inside a directive

  1. Yet another way is to create the instance yourself:
constructor(ms:myService)
let myClass = new MyClass(ms);

Upvotes: 76

Julien
Julien

Reputation: 2756

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
}

Upvotes: 25

Sandeep Gupta
Sandeep Gupta

Reputation: 7250

If your service methods are pure functions, a clean way to solve this is to have static members in your service.

your service

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

your class

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

Upvotes: 3

Kilves
Kilves

Reputation: 1101

This is kind of (very) hacky, but I thought I'd share my solution as well. Note that this will only work with Singleton services (injected at app root, not component!), since they live as long as your application, and there's only ever one instance of them.

First, in your service:

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

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

Then in any class:

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

This solution is good if you want to reduce code clutter and aren't using non-singleton services anyway.

Upvotes: 29

Ryan Crews
Ryan Crews

Reputation: 3033

Not a direct answer to the question, but if you're reading this SO for the reason I am this may help...

Let's say you're using ng2-translate and you really want your User.ts class to have it. You're immediate thought is to use DI to put it in, you are doing Angular after all. But that's kind of overthinking it, you can just pass it in your constructor, or make it a public variable you set from the component (where you presumably did DI it in).

e.g.:

import { TranslateService } from "ng2-translate";

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

  // a bunch of awesome User methods
}

then from some user-related component that injected TranslateService

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

Hopefully this helps those out there like me who were about to write a lot of harder to maintain code because we're too clever sometimes =]

(NOTE: the reason you may want to set a variable instead of making it part of your constructor method is you could have cases where you don't need to use the service, so always being required to pass it in would mean introducing extra imports/code that are never really used)

Upvotes: 34

Related Questions