Dependency Injection

Basic Dependency Injection, singleton pattern and reuseable

Dependency Injection (DI) in Angular

What is Dependency Injection?

Dependency Injection (DI) is a design pattern where classes receive their dependencies from an external source rather than creating them internally. In Angular, the DI system provides and manages dependencies across your application.

Basic Service Example

Here’s a simple service that manages a list of Fresher names:

// fresher.service.ts
import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'  // Makes this a singleton service
})
export class FresherService {
  private freshers: string[] = [
    'John Doe',
    'Jane Smith',
    'Mike Johnson'
  ];

  getFreshers(): string[] {
    return this.freshers;
  }

  addFresher(name: string) {
    this.freshers.push(name);
  }
}

Using the Service in Components

// fresher-list.component.ts
import { Component, OnInit } from '@angular/core';
import { FresherService } from './fresher.service';

@Component({
  selector: 'app-fresher-list',
  template: `
    <h2>Fresher List</h2>
    <ul>
      <li *ngFor="let fresher of freshers">
        {{ fresher }}
      </li>
    </ul>
    <button (click)="addNewFresher()">Add Fresher</button>
  `
})
export class FresherListComponent implements OnInit {
  freshers: string[] = [];

  constructor(private fresherService: FresherService) {}

  ngOnInit() {
    this.freshers = this.fresherService.getFreshers();
  }

  addNewFresher() {
    this.fresherService.addFresher('New Fresher');
    this.freshers = this.fresherService.getFreshers();
  }
}

Service with HTTP Example

Here’s a more realistic example using HttpClient:

// fresher.interface.ts
interface Fresher {
  id: number;
  name: string;
  email: string;
}

// fresher.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class FresherService {
  private apiUrl = 'api/freshers';

  constructor(private http: HttpClient) {}

  getFreshers(): Observable<Fresher[]> {
    return this.http.get<Fresher[]>(this.apiUrl);
  }

  addFresher(fresher: Fresher): Observable<Fresher> {
    return this.http.post<Fresher>(this.apiUrl, fresher);
  }
}

DI Providers

There are different ways to provide services in Angular:

  1. Root-level Provider (Singleton):
@Injectable({
  providedIn: 'root'
})
  1. Module-level Provider:
@NgModule({
  providers: [FresherService]
})
  1. Component-level Provider:
@Component({
  providers: [FresherService]  // New instance for this component
})

Best Practices

  1. Use providedIn: ‘root’ for singleton services
@Injectable({
  providedIn: 'root'
})
  1. Implement OnDestroy for cleanup:
export class MyService implements OnDestroy {
  ngOnDestroy() {
    // Cleanup code
  }
}
  1. Use Interfaces for better type safety:
interface ServiceConfig {
  apiUrl: string;
  timeout: number;
}

@Injectable({providedIn: 'root'})
class MyService {
  constructor(@Inject('CONFIG') private config: ServiceConfig) {}
}
  1. Lazy Loading with feature modules:
@Injectable({
  providedIn: 'any'  // New instance per lazy-loaded module
})

Common Use Cases

  1. Data Services: Managing application data
@Injectable({providedIn: 'root'})
export class DataService {
  private data: any[] = [];
  
  getData() { return this.data; }
  addData(item: any) { this.data.push(item); }
}
  1. State Management: Managing application state
@Injectable({providedIn: 'root'})
export class StateService {
  private state = new BehaviorSubject<any>(initialState);
  state$ = this.state.asObservable();
  
  updateState(newState: any) {
    this.state.next(newState);
  }
}
  1. Utility Services: Shared functionality
@Injectable({providedIn: 'root'})
export class UtilityService {
  formatDate(date: Date): string {
    return date.toLocaleDateString();
  }
  
  generateId(): string {
    return Math.random().toString(36).substr(2, 9);
  }
}

Testing with DI

describe('FresherComponent', () => {
  let component: FresherComponent;
  let service: FresherService;

  beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [FresherComponent],
      providers: [FresherService]
    });

    component = TestBed.createComponent(FresherComponent).componentInstance;
    service = TestBed.inject(FresherService);
  });

  it('should load freshers', () => {
    const mockFreshers = ['John', 'Jane'];
    spyOn(service, 'getFreshers').and.returnValue(mockFreshers);
    
    component.ngOnInit();
    expect(component.freshers).toEqual(mockFreshers);
  });
});

Last modified March 27, 2025: Edit members.yaml (21070ed)