Mastering Angular: A Complete Guide to Modern Web Development

5.67K 0 0 0 0

Chapter 4: Services and Dependency Injection

Angular applications are designed to be modular, scalable, and maintainable. A critical aspect of achieving this is through Services and Dependency Injection (DI). Services enable code reusability and separation of concerns, while Angular’s DI framework ensures that dependencies are managed efficiently and transparently.


1. Introduction to Angular Services

Services in Angular are classes used to share data, logic, and functionality across components.

Why Use Services?

  • Separation of concerns
  • Reusability of logic
  • Centralized data access
  • Easier testing and maintenance

2. Creating a Service

Use the Angular CLI to generate a service:

bash

 

ng generate service data

This creates two files:

  • data.service.ts
  • data.service.spec.ts (for testing)

Basic Service Example

ts

 

// src/app/data.service.ts

import { Injectable } from '@angular/core';

 

@Injectable({

  providedIn: 'root'

})

export class DataService {

  private data: string[] = [];

 

  constructor() {}

 

  addData(item: string) {

    this.data.push(item);

  }

 

  getData() {

    return this.data;

  }

}

The @Injectable decorator tells Angular that this class can be injected into other components.


3. Injecting a Service into a Component

To use the DataService in a component:

ts

 

// src/app/app.component.ts

import { Component } from '@angular/core';

import { DataService } from './data.service';

 

@Component({

  selector: 'app-root',

  template: `

    <input #item />

    <button (click)="addItem(item.value)">Add</button>

    <ul>

      <li *ngFor="let i of items">{{ i }}</li>

    </ul>

  `

})

export class AppComponent {

  items: string[] = [];

 

  constructor(private dataService: DataService) {}

 

  addItem(value: string) {

    this.dataService.addData(value);

    this.items = this.dataService.getData();

  }

}

Explanation:

  • The service is injected via the constructor.
  • The service methods are used to manipulate shared data.

4. Service Scope: Singleton vs Non-Singleton

Angular services can be provided at different levels:

Level

Where Provided

Scope

root

@Injectable({ providedIn: 'root' })

Global (singleton)

Component Level

providers: [MyService]

New instance per component

NgModule Level

providers: [MyService] in AppModule

Available to that module's components


5. Dependency Injection (DI) Overview

Dependency Injection is a design pattern used to implement IoC (Inversion of Control). Angular uses DI to inject service instances wherever needed.

Key Terms

Term

Definition

Injector

Object that knows how to create a dependency

Provider

Defines how to create a service instance

Dependency Token

Identifier for a dependency


6. Provider Configuration

Using @Injectable()

ts

 

@Injectable({

  providedIn: 'root'

})

export class MyService {}

This is equivalent to:

ts

 

@NgModule({

  providers: [MyService]

})

export class AppModule {}

Providing in Component

ts

 

@Component({

  selector: 'app-child',

  providers: [MyService],

  ...

})

This creates a new instance of MyService in that component only.


7. Using Services for HTTP Requests

Let’s use Angular’s HttpClient in a service.

Setup

Import HttpClientModule in AppModule.

ts

 

import { HttpClientModule } from '@angular/common/http';

 

@NgModule({

  ...

  imports: [HttpClientModule],

  ...

})

export class AppModule {}

HTTP Service Example

ts

 

import { Injectable } from '@angular/core';

import { HttpClient } from '@angular/common/http';

 

@Injectable({

  providedIn: 'root'

})

export class ApiService {

  private apiUrl = 'https://jsonplaceholder.typicode.com/posts';

 

  constructor(private http: HttpClient) {}

 

  getPosts() {

    return this.http.get(this.apiUrl);

  }

}

Component Using ApiService

ts

 

@Component({

  ...

})

export class PostComponent {

  posts: any[] = [];

 

  constructor(private apiService: ApiService) {}

 

  ngOnInit() {

    this.apiService.getPosts().subscribe(data => {

      this.posts = data as any[];

    });

  }

}


8. Hierarchical Injectors and Tree-shakable Providers

Angular DI follows a hierarchical structure, where child injectors inherit from parent injectors.

ts

 

@Injectable({

  providedIn: 'root' // tree-shakable

})

Angular will remove services from the bundle if they’re not used—this is known as tree shaking.


9. Multi-Provider Tokens

Sometimes you want multiple services under one token. You can use multi: true.

ts

 

export const LOGGER = new InjectionToken<string[]>('Logger');

 

@NgModule({

  providers: [

    { provide: LOGGER, useValue: 'FileLogger', multi: true },

    { provide: LOGGER, useValue: 'ConsoleLogger', multi: true }

  ]

})

Inject like this:

ts

 

constructor(@Inject(LOGGER) private loggers: string[]) {}


10. Using Factory Providers

You can dynamically configure services using factories.

ts

 

@Injectable()

export class ConfigService {

  constructor(public config: string) {}

}

 

export function configFactory() {

  return new ConfigService('ProductionConfig');

}

 

@NgModule({

  providers: [

    { provide: ConfigService, useFactory: configFactory }

  ]

})


11. Optional and Self Injection

Optional Injection

If a service is optional, use @Optional():

ts

 

constructor(@Optional() private logger?: LoggerService) {}

Self Injection

Use @Self() to restrict DI to current injector:

ts

 

constructor(@Self() private logger: LoggerService) {}


12. Using ProvidedIn ‘any’ vs ‘root’ vs Module

providedIn Value

Behavior

root

Singleton across app

any

Unique instance for each lazy module

Specific Module

Limited to that module


13. Testing Services

Service Test Example

ts

 

describe('DataService', () => {

  let service: DataService;

 

  beforeEach(() => {

    TestBed.configureTestingModule({});

    service = TestBed.inject(DataService);

  });

 

  it('should add and return data', () => {

    service.addData('test');

    expect(service.getData()).toContain('test');

  });

});


14. Common Service Patterns

Pattern

Usage

Singleton

Shared state across app

Stateless

Pure logic service (e.g., math service)

Http Service

External API communication

Shared Service

Cross-component communication (RxJS)


15. Best Practices

  • Always use providedIn: 'root' for shared services.
  • Avoid providing services in components unless needed.
  • Use interfaces for better typing.
  • Keep services stateless unless explicitly managing state.
  • Isolate HTTP logic inside services, not components.
  • Use RxJS Subjects for reactive data flows.

Chapter Summary

  • Services encapsulate business logic and shared data.
  • Dependency Injection enables flexibility and testability.
  • Services can be scoped globally, per module, or per component.
  • Use HttpClient inside services for API access.
  • Providers can be configured using useClass, useFactory, useValue, and multi.



Back

FAQs


1. Q: Is Angular suitable for beginners?

A: Yes, but having a basic understanding of TypeScript and JavaScript ES6 will help a lot.

2. Q: What language is Angular written in?

A: Angular uses TypeScript, which is a superset of JavaScript.

3. Q: What's the difference between AngularJS and Angular?

A: AngularJS (1.x) is the older version using JavaScript, while Angular (2+) uses TypeScript and a component-based architecture.

4. Q: Can I use Angular for mobile apps?

A: Yes, with frameworks like Ionic or NativeScript.

5. Q: How does Angular compare to React?

A: Angular is a full framework with built-in tools, while React is a library focused on UI.

6. Q: Is Angular good for large-scale applications?

A: Yes, Angular excels in large, structured, maintainable apps.

7. Q: What is Angular CLI?

A: It’s a command-line tool that helps scaffold, develop, and manage Angular applications efficiently.

8. Q: Do I need Node.js to use Angular?

A: Yes, Node.js is required to install Angular CLI and manage dependencies.

9. Q: What is a component in Angular?

A: A reusable unit of UI that consists of a template, logic, and styling.

10. Q: Can Angular be used with backend frameworks like Django or Laravel?

A: Yes, Angular can be paired with any backend via APIs.