🅰️ Angular Q92 / 125

What is inject() function in Angular?

AI-Powered Answer ✓ Answered

The `inject()` function in Angular is a low-level, primitive API that provides a way to manually resolve dependencies within an injection context, without relying on traditional constructor injection. Introduced in Angular v15, it offers increased flexibility for dependency injection outside of class constructors, particularly useful in functional programming paradigms.

What is `inject()`?

inject() is a function that allows you to obtain an instance of a dependency directly from the current injector. Unlike constructor injection, which is bound to class instantiation, inject() can be called in a wider range of contexts, such as within functional APIs (e.g., functional guards, resolvers), factory functions, or even within methods of a service or component.

It requires an *injection context* to be present when it's called, meaning it must be invoked synchronously during the creation of a provider, component, directive, pipe, or within a function that is itself called within such a context.

Why Use `inject()`?

  • Flexibility: Enables dependency injection in contexts where constructor injection is not possible or practical, such as plain functions, functional guards/resolvers, or interceptors.
  • Reduced Boilerplate: Can lead to cleaner code for certain patterns, especially with the rise of standalone components and functional APIs.
  • Tree-shakable Providers: When combined with standalone components and functional providers, it can facilitate more efficient tree-shaking.
  • Internal Framework Use: Angular itself heavily uses inject() internally for its own functional APIs, making it a foundational primitive.

How to Use `inject()`

To use inject(), you simply pass the injection token (e.g., a service class, an InjectionToken) as its first argument. It returns the instance of the requested dependency.

typescript
import { inject, Injectable } from '@angular/core';
import { LoggerService } from './logger.service';

@Injectable({
  providedIn: 'root'
})
export class DataService {
  private logger = inject(LoggerService); // Injecting a service

  getData() {
    this.logger.log('Fetching data...');
    // ... fetch data logic
    return ['item1', 'item2'];
  }
}

// Example in a functional guard/resolver context
// import { CanActivateFn } from '@angular/router';
// import { AuthService } from './auth.service';
//
// export const authGuard: CanActivateFn = (route, state) => {
//   const authService = inject(AuthService);
//   return authService.isAuthenticated();
// };

Options for `inject()`

The inject() function also accepts an optional second argument – an options object – to modify its behavior. This object can contain properties like optional, self, host, and skipSelf.

  • optional?: boolean: If true, inject() returns null if the dependency is not found, instead of throwing an error.
  • self?: boolean: If true, the dependency lookup is restricted to the current injector (e.g., current component/directive instance).
  • host?: boolean: If true, the dependency lookup starts at the host element's injector (relevant for directives/components).
  • skipSelf?: boolean: If true, the current injector is skipped, and the lookup starts from the parent injector.
typescript
import { inject } from '@angular/core';
import { ConfigService } from './config.service';

export class MyComponent {
  // Inject ConfigService, return null if not found
  private config = inject(ConfigService, { optional: true });

  // Example: Inject a dependency only from the current element's injector
  // private myDirectiveDependency = inject(MyDirectiveService, { self: true });
}

Important Considerations

  • Synchronous Call & Injection Context: inject() must be called synchronously within an active injection context. It will throw an error if called asynchronously or outside such a context (e.g., in a plain function not part of the DI tree).
  • Not a Full Constructor Replacement: While powerful, inject() complements constructor injection rather than replacing it entirely. Constructor injection remains the idiomatic way for class-based dependencies.
  • Framework Primitive: Treat inject() as a foundational primitive; for most application code, relying on Angular's higher-level DI mechanisms (like constructor injection) is often clearer and more robust unless specific flexibility is required.