Functional Dependency Injection with inject() in Angular

Posted on 2025-05-25 by Burak Hamdi TUFAN
Web
Functional Dependency Injection with inject() in Angular
Hello everyone, in this article we are going to talk about functional dependency injection with inject() which is promoting loosely coupled function with Angular 14+ Framework.

Lets get started.

Dependency Injection is one of the heavy stone of the Angular Framework. Traditional Angular relies on constructor based dependency injection. With the introduction of functional dependency injection, we can inject the dependency at any place. And it offers to reduce the class dependency injections without constructor injection. It offers more flexible and powerful way to access dependencies.

What is inject()?

The inject() function is part of Angular’s dependency injection system. It has been introduced with Angular 14. We can get the dependency from outside of the class constructor. This is especially useful in functional constructs, such as Injection outside of class context.

When to Use inject()

Recomended places to use inject():
  • You're writing standalone components or functional services.
  • You need to access dependencies in factory functions.
  • You want to improve testability and reduce boilerplate.
  • Avoid using inject() inside constructors or class methods
Now lets make some examples.
Below you can see the traditional Constructor-Based Injection

@Injectable({ providedIn: 'root' })
export class UserService {
  constructor(private http: HttpClient) {}
}
We are injecting the service by the constructor.
Injecting with using inject() function

@Injectable({ providedIn: 'root' })
export class UserService {
  private http = inject(HttpClient);

  getUsers() : Observable<any> {
    return this.http.get('/api/users');
  }
}

NOTE:
injecttion must be done before the instance created. So we can call the inject() function in a constructor, a constructor parameter and a field initializer. We can also call it from a provider's factory. But we can not call it from a method since the instance has been already created.

Using inject() in Factory Providers

Factory providers are one of the most powerful use cases for inject(). We can directly access the dependency with the inject function without the constructor requirement.

Below you can see an example for Factory Provider.

export function tokenFactory(): string {
  const config = inject(AppConfigService);
  return config.apiToken;
}

@NgModule({
  providers: [
    {
      provide: 'API_TOKEN',
      useFactory: tokenFactory,
    }
  ]
})
export class AppModule {}
Here, we’re creating a string token 'API_TOKEN' using a factory function that accesses AppConfigService using inject().

Functional Injection in Standalone Components

Angular’s standalone components, are working so efficient with inject function. Here is the example of using inject with Angular Standalone component.

@Component({
  selector: 'app-user',
  template: `<p>{{ user?.name }}</p>`,
  imports: [CommonModule],
})
export class ProfileComponent {
  private readonly userService = inject(UserService);
  readonly user = this.userService.getCurrentUser();
}

As you can see above, Constructor is not required and it reduces the boilerplate.

Unit Test Example Using inject()

While implementation of unit test we can define the dependency injection like below.

describe('UserService', () => {
  let companyServiceSpy: jasmine.SpyObj<CompanyService>;
  companyServiceSpy = jasmine.createSpyObj('CompanyService', ['details']);

  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [UserService, HttpClient, { provide: CompanyService, useValue: companyServiceSpy }],
    });
  });

  it('should fetch users', () => {
    const userService = inject(UserService);
    expect(userService).toBeTruthy();
  });
});
Above we have injected the service in the test class and we are ready to use it. We can also create a test service spy and we can inject our spy to track the service methods.

Keep in Mind:

  • inject() must be called outside of lifecycle hooks or constructors. It should be defined at the initialisation level or factory providers.
  • It only works in Angular-initialized contexts, such as components, services, and factory providers.
  • Avoid overusing inject() in large files—it can make dependencies harder to trace.

Conclusion

The inject() function offers a flexible, clean, and efficient alternative to traditional constructor-based dependency injection. It's especially beneficial in the era of standalone components and functional providers. With the inject, we can introduce cleaner and more easy-to-maintain code for our Angular project.

That is all

Happy Dependency Injecting

Burak Hamdi TUFAN


Tags
Share this Post
Send with Whatsapp