Control which class will get instantiated for your Angular dependencies

Using Angular dependency injection, it’s possible to control which class will be injected for a specific provider. Let’s start with a simple scenario.

Given a simple component:

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'dependency-injection';

  constructor(public injectedService: FirstService) {

  }
}

And two services:

@Injectable({
  providedIn: 'root'
})
export class FirstService {

  constructor() { }

  output() {
    return 'first';
  }
}

Where the second one implements the first:

@Injectable({
  providedIn: 'root'
})
export class SecondService implements FirstService {

  constructor() { }

  output() {
    return 'second';
  }
}

We can control which service will be injected using the useClass attribute in the base class declaration:

@Injectable({
  providedIn: 'root',
  useClass: SecondService
})
export class FirstService {

  constructor() { }

  output() {
    return 'first';
  }
}

We need to remove the providedIn attribute in the second service:

@Injectable()
export class SecondService implements FirstService {

  constructor() { }

  output() {
    return 'second';
  }
}

Here it’s important to use the implements keyword rather than the extends keyword as it treats the base class as an interface and does not link to it directly through super which would create a circular reference.

Now our AppComponent will receive a SecondService instance in it’s constructor instead of FirstService.

While this can be used to easily switch which classes are deployed by modifying the attribute in one place, it can also be used to construct more advanced injection scenarios.

For example we could use the useClass attribute in the component’s provider allowing certain components to specify which specific service they require while letting other components receive the default service.

Here is an example of this. First we remove the useClass in FirstService

@Injectable({
  providedIn: 'root'
})
export class FirstService

And then we add a providers attribute in the component decorator:

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
  providers: [
    { provide: FirstService, useClass: SecondService }
  ]
})
export class AppComponent {
  title = 'dependency-injection';

  constructor(public injectedService: FirstService) {

  }
}

This component would receive a SecondService while another component lacking this providers declaration would receive a FirstService.

Other more advanced scenarios are also possible and I will cover one such scenario in a subsequent post.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s