liveBook · Manning

Dependency injection

As explained in Pro Angular 16

Dependency injection is a technique in which a framework supplies an object's required dependencies rather than the object constructing them directly.

Dependency injection lets Angular supply an object’s dependencies instead of making the object create them or receive them through a parent component’s input. A class declares what it needs in its constructor; Angular examines the parameter types and uses registered providers to supply the objects. Objects managed this way are services. section 18.2.2 section 18.0

Share a discount without passing it through the component tree

Suppose a discount editor and a discount display both need the same discount object. Mark its class with @Injectable(), register it as a provider, and give each component a constructor parameter typed as that service. The components no longer need input properties for the object, and their parent no longer has to create and pass it along. Both components obtain the shared object through Angular while their visible behavior remains the same. section 18.2.2

Listing 18.9. Declaring a dependency in the discountDisplay.component.ts file in the src/app folder
import { Component, Input } from "@angular/core";
import { DiscountService } from "./discount.service";
 
@Component({
    selector: "paDiscountDisplay",
    template: `<div class="bg-info text-white p-2 my-2">
                  The discount is {{discounter.discount }}
               </div>`
})
export class PaDiscountDisplayComponent {
 
    constructor(public discounter: DiscountService) { }

    // @Input({alias: "discounter", required: true})
    // discounter!: DiscountService;
}

The display component declares its service dependency in the constructor; the commented-out input shows the arrangement it replaces. section 18.2.2

To wire up a dependency:

  • Apply @Injectable() to the service class. A class without constructor dependencies can omit it, but retaining it makes the service’s intended role clear. section 18.2.2
  • Register the service in an Angular module’s providers property so Angular knows how to resolve requests for it. section 18.3.1 section 18.5
  • Declare a constructor parameter of the required service type in the component or another service. Angular resolves service dependencies in turn until it reaches a class with none. section 18.2.2 section 18.3.1
Listing 18.22. Denoting a service and dependency in the repository.model.ts file in the src/app folder
import { Product } from "./product.model";
import { SimpleDataSource } from "./datasource.model";
import { Signal, WritableSignal, signal } from "@angular/core";
import { Injectable } from "@angular/core";

@Injectable()
export class Model {
    //private dataSource: SimpleDataSource;
    private products: WritableSignal<Product[]>;
    private locator = (p: Product, id: number | any) => p.id == id;

    constructor(private dataSource: SimpleDataSource) {
        //this.dataSource = new SimpleDataSource();
        this.products = signal(new Array<Product>());
        this.products.mutate(prods => 
            this.dataSource.getData().forEach(p => prods.push(p)));
    }

    // ...statements omitted for brevity...
}

The repository receives its data source through a constructor parameter rather than constructing it directly, demonstrating that services can depend on services. section 18.3.1

A cart provides another reason to inject a service: the application needs one shared object so different components can access the user’s selections. Registering the cart as a provider lets Angular supply it when a component declares a Cart constructor parameter. section 6.2.1

Injection can also loosen component relationships. A form can inject a model and save a product itself instead of emitting an event for its parent to handle; a table can inject the same model instead of receiving it through an input. This makes building blocks easier to isolate for unit tests and to reuse. It is not necessary to remove every parent–child relationship: choose the degree of injection that suits the application rather than refactoring solely for its own sake. section 18.4.2 section 18.4

How Pro Angular 16 covers it

How deep it goes
Works through decorators, providers, constructor parameters, and a service dependency chain; does not detail injector internals.
Examples
Uses a shared discount editor and display, a cart, and a repository that depends on a data source.
What it assumes
Assumes familiarity with Angular components and classes, while introducing services and constructor-based dependency resolution.
What you can do after
Build it from scratch: decorate and register Angular services, inject them through constructors, and replace direct construction or parent-mediated sharing.
How it connects
Connects services and providers to component inputs, event handling, data repositories, and unit testing.

Also explained in

Compare how 21 books explain dependency injection →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Pro Angular 16. The section links point to the parts of the book it draws on.