inject() Function (v14)

📚 What is inject() Function?

inject() is a functional way to access dependency injection, introduced in Angular 14:

  • Functional approach: No constructor needed
  • Works everywhere: Components, services, guards, pipes, directives
  • Field initializers: Can be used in class field declarations
  • Factory functions: Enables functional patterns and composition
  • Must be in injection context: Constructor, field initializer, or factory function

🎯 Interview Questions

  • Q1: What is the advantage of inject() over constructor injection?
  • A: Can be used outside constructors (field initializers, factory functions), supports functional patterns, cleaner syntax for many dependencies.
  • Q2: Where can you use inject()?
  • A: In injection context: constructor, field initializer, factory function called from injection context, or runInInjectionContext().
  • Q3: What is runInInjectionContext()?
  • A: Function that allows running code in an injection context outside of normal DI flow, useful for dynamic injection.
  • Q4: How do you inject optional dependencies with inject()?
  • A: Use inject(Service, { optional: true }) to return undefined if not found instead of throwing error.

🔧 Live Demo - inject() Usage

Data from DataService (using inject):

  • Item 1
  • Item 2
  • Item 3

Injected Config (InjectionToken):

Base URL: https://api.example.com

Timeout: 5000ms

🔄 Constructor vs inject() Comparison

Constructor Injection (Old)

constructor(
  private dataService: DataService,
  private logger: LoggerService,
  @Optional() private config: Config
) {}

inject() Function (New) ✅

private dataService = inject(DataService);
private logger = inject(LoggerService);
private config = inject(Config, { optional: true });

💻 inject() Function Examples


import { inject, InjectionToken } from '@angular/core';

// ===== Basic inject() usage =====
@Component({...})
export class MyComponent {
  // Field initializer - cleaner syntax!
  private readonly http = inject(HttpClient);
  private readonly router = inject(Router);
  private readonly service = inject(MyService);
  
  // No constructor needed for simple cases!
}

// ===== inject() in Services =====
@Injectable({ providedIn: 'root' })
export class DataService {
  private readonly http = inject(HttpClient);
  private readonly logger = inject(LoggerService);
  
  fetchData() {
    this.logger.log('Fetching...');
    return this.http.get('/api/data');
  }
}

// ===== Optional and Self/SkipSelf =====
private optional = inject(Service, { optional: true }); // undefined if not found
private self = inject(Service, { self: true }); // Only this injector
private skipSelf = inject(Service, { skipSelf: true }); // Parent injector only

// ===== InjectionToken with inject() =====
const API_URL = new InjectionToken<string>('API_URL');

// In component
private apiUrl = inject(API_URL);

// ===== Factory function pattern =====
function createDataLoader() {
  const http = inject(HttpClient);
  const baseUrl = inject(API_URL);
  
  return {
    load: (id: string) => http.get(`${baseUrl}/items/${id}`)
  };
}

@Component({...})
export class MyComponent {
  private loader = createDataLoader(); // Works!
}

// ===== Functional Guards with inject() =====
export const authGuard = () => {
  const auth = inject(AuthService);
  const router = inject(Router);
  
  if (auth.isAuthenticated()) {
    return true;
  }
  return router.parseUrl('/login');
};