0% found this document useful (0 votes)
35 views35 pages

08 Dependency Injectionand Providers

The document discusses dependency injection in Angular. It defines dependency injection as decoupling code from its dependencies. Angular uses dependency injection to provide services to components through an injector. A component declares its dependencies through the constructor, and the injector resolves them. Providers configure how the injector creates instances of dependencies. Alternative providers allow overriding dependencies with different classes, values, or factories.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
35 views35 pages

08 Dependency Injectionand Providers

The document discusses dependency injection in Angular. It defines dependency injection as decoupling code from its dependencies. Angular uses dependency injection to provide services to components through an injector. A component declares its dependencies through the constructor, and the injector resolves them. Providers configure how the injector creates instances of dependencies. Alternative providers allow overriding dependencies with different classes, values, or factories.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
You are on page 1/ 35

Microsoft and Apple Training

Dependency Injection and Providers


Agenda
 Terminology
 Dependency Injection Basics
 Hierarchical Injection
 Providers
 Dependency Injection Tokens
Dependency Injection
 Dependency injection is the act of decoupling a piece of code of its
dependencies.
– It is a design principle
– Improves maintainability
– Improves testability
– Improves overall design

 You can overdo it sometimes, make sure you don’t overdo it 


Dependency Injection
 Dependent object
– Depends on another object
 Dependencies are components the dependent object depends upon
 The injector injects those dependencies into the dependent object
 Eg. Account object depends on a Logger object
Dependency Injection
 Decouple the creation of the dependency from the dependent object
– By passing in the dependency as a parameter

class Account {
//tight coupling
logger: Logger = new Logger();
}

class Account {
//loose coupling
constructor(public logger: Logger) {}
}
Dependency Injection Framework
 The Injector
– Gets a list of registered classes
– Manages the responsibility of dependency creation
– Supplies the dependency to the dependent object

let logger = injector.get('Logger');

 This makes sure that Logger is created and injected into the Account
constructor
Dependency Injection in Angular
 Angular's Dependency Injection mechanism
– A Component simply has to ask for a service
– An Injector is responsible for delivering the service and managing its life cycle
– The Injector relies on Providers
– Providers can create or return a service
Three Steps
 The service

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


Create the
@Injectable() Service
export class AuthenticationService { ... }

 The client

import {AuthenticationService} from './authentication.service';

@Component({
providers: [AuthenticationService] Register Service
})
export class Login {
constructor(private _authenticationService: AuthenticationService){}
}

Use Service
Service Registration (Angular 6+)
 Registration can be handled inside the @Injectable decorator

@Injectable({
providedIn: 'root'
})

 Provider is no longer needed


 providedIn:
– ‘root’: singleton injection for your application
– ‘{modulename}’: injected into a specific module
Creating a Service
 Create a new class and export it
– Like with creating a component
– No special requirements

export class GameService {


getGames() { return GAMES; }
}

 Best practice: add @Injectable() before service definition


– Note the parentheses "()" with Injectable!

@Injectable()
export class GameService {
getGames() { return GAMES; }
}
Register the Service
 Register the service with the injector
– In a module  application-wide
– In a component  locally

// In a module // In a component
@NgModule({ @Component({
providers: [GameService] selector: 'my-games',
}) template: `
export class AppModule { } <h2>Games</h2>
<game-list></game-list>
`,
providers: [GameService]
})
Using the Service
 Create a constructor in the component in-need
– Make sure that it has a correct type annotation!

@Component({
...
})
export class GameListComponent {
games : Game[];
constructor( private _gameService: GameService) {
this.games = _gameService.getGames();
}
}
Optional Service
 By default every service needs to be registered

EXCEPTION: No provider for LoggerService! (GameListComponent ->


GameService -> LoggerService)

 Optional dependencies can be used


– Need the Optional() parameter decorator in dependent object
– Passes a null reference when there's no provider

constructor(@Optional() private _logger: LoggerService) { }


Dependency Injection
 Where can I use DI?
– Services
– Directives
– Pipes

 DI is pervasive throughout Angular

 Angular requires a decorator because it needs constructor metadata in


order to inject the Service
– Metadata is only generated for classes with a decorator
– That's why a service with dependencies requires @Injectable()
Hierarchical Injection
 Each service is created as a singleton
– In the scope of the injector

 A hierarchy of components leads to a hierarchy of injectors (kinda)


– For performance: Injectors get shared if possible

C DI

C C DI DI

C C C DI DI DI
Hierarchical Injection
 When registered in a component, the singleton can be injected into the
component and all its children

C1 Register here

C2 C3

Use here
C4 C4 C4

 C3 and all instances of C4 now use the same singleton


Example
 There is a different injector with every providers array
// GameListComponent
@Component({
providers: [ RestoreService, LoggerService ]
})
// GameDetailComponent
@Component({
providers: [ RestoreService ]
})

 The GameList component shares a LoggerService with all GameDetail


components
 The GameList component and each GameDetail component has its own
RestoreService
Application-Wide Injection
 Services can be registered in an NgModule
– Singleton available to the entire application
– Can still be overridden locally in a Component

@NgModule({
imports: [BrowserModule],
declarations: [AppComponent, GameListComponent, GameDetailComponent],
bootstrap: [AppComponent],
providers: [LoggerService]
})
export class AppModule { }
Where to Register?
 Stateless Services can easily be shared
– One instance for all!
– Remember that you can
register when bootstrapping

 Stateful could get messy when shared


– Components can override each other's state
Providers
 Providers inform the Injector how to create a runtime version of the
dependency

 The injector can be configured with alternative providers


– The Service class itself
– A Substitute class
– A Factory function
– ...
Provide Function
 This
providers: [LoggerService]

 Is short-hand expression for

providers: [{provide: LoggerService, useClass: LoggerService }]


Provide Function
 The provide function needs two arguments
– A token serving as the key for registering the provider
– Provider definition object

providers: [{provide: LoggerService, useClass: LoggerService }]

Here the token is


the class itself

 The provider definition object


– Is like a recipe for creating the dependency
– Can have alternatives
Alternative Class Providers
 Asking a different class to provide the service
providers: [{provide: LoggerService,
useClass: BetterLoggerService }]

 Somebody asking for a LoggerService will now get an instance of


BetterLoggerService

 BetterLoggerService can have dependencies of its own


– Make sure to register those dependencies
Value Providers
 Sometimes it's easier to provide object
– Instead of asking injector to create it from a class

// An object in the shape of the logger service


let silentLogger = {
logs: ['Silent logger. Provided via "useValue"'],
log: () => { }
}

// providers in @Component metadata


providers: [{provide: LoggerService, useValue: silentLogger }]
Aliased Class Providers
 Suppose following scenario
– Old Component depends on OldLogger
– OldLogger has same interface as NewLogger
– Old Component can't be updated

 Old component needs to use the instance of NewLogger, when talking to


OldLogger (alias)
Aliased Class Providers
 Let's try with useClass

providers: [NewLogger,
// Not aliased! Creates two instances of
`NewLogger`
{ provide: OldLogger, useClass: NewLogger
})],
 Hmm, doesn't seem to work as requested... Solution?

providers: [NewLogger,
// Alias OldLogger w/ reference to
NewLogger
{ provide: OldLogger, useExisting:
NewLogger })],
Factory Provider
 What if the right providers needs to be decided at runtime?
– And even needs to switch at runtime
 Example
– GameService hides the alpha games from normal users, but people can switch roles in
one browser session

 Solution
– Use a factory provider
Factory Provider
TS
let gameServiceFactory = (logger: LoggerService, userService: UserService) => {
return new SecureGameService(logger, userService.user.isAuthorized);
};

@Component({
providers: [{
provide: GameService,
useFactory: gameServiceFactory,
deps: [LoggerService, UserService]
//deps are here as provider tokens to be able to inject
//the correct services
}]
})
Providers
 For the sake of encapsulation, you can separate the Provider declaration from
the component
export let GameServiceProvider: FactoryProvider = {
provide: GameService,
useFactory: gameServiceFactory,
deps: [LoggerService, UserService]
};

 And import in the component

@Component({
providers: [GameServiceProvider, UserService, ...]
})
Providing Multiple Dependencies
 Using multi extends a value rather than overriding it
– Receive array instead of one item

var injector = ReflectiveInjector.resolveAndCreate([


{provide: TOKEN, useValue: 'dep1', multi: true },
{provide: TOKEN, useValue: 'dep2', multi: true }
]);

let deps = injector.get(TOKEN);


// deps == ['dep1', 'dep2']

 Example
– Return a collection of validator functions
Dependency Injection Tokens
 Registering a provider needs a token
– Injector maintains internal token-provider map with token as key

 Most of the time Class Dependency Tokens are used

 Alternatives
– String tokens
– Injectiontokens
Interfaces as a Token?
 Injecting configuration objects with a value provider
– A token is needed, but what to use?

export interface Config {


apiEndpoint: string,
title: string
}

export const CONFIG: Config = {


apiEndpoint: 'api.heroes.com',
title: 'Dependency Injection'
};

 Let's use the interface!

providers: [Config, ...


// Doesn't work because Interfaces don't exist in JavaScript!
String Tokens
 Instead of an interface token
– Use a string to represent the provider

// Use string as provider token


Providers: [{provide: 'app.config', useValue: CONFIG }]

 Using this dependency?


– Extra metadata is needed in the form of @Inject(<token>)

// @Inject(token) to inject the dependency


constructor(@Inject('app.config') private _config: Config){ }
Injection Token
 Some people really hate strings
– And they're right... Just imagine Pavarotti in one...

 Instead use an object to contain these tokens

//1. Create token


export let APP_CONFIG = new InjectionToken('app.config');
//2. Register token
providers: [{provide: APP_CONFIG, useValue: CONFIG }]
//3. Use token
constructor(@Inject(APP_CONFIG) private _config: Config){ }

 Angular uses InjectionToken objects to register all non-class dependencies


Summary
 Terminology
 Dependency Injection Basics
– Create, Register and Inject Service
 Hierarchical Injection
– NgModel for application-wide
– Component for itself and children
 Providers
– Class, provide, new Provider
– useClass, useExisting, useValue, useFactory, multi
 Dependency Injection Tokens
– Class, String, InjectionToken

You might also like