Skip to content

Instantly share code, notes, and snippets.

@bennadel
Created September 5, 2017 12:20
Using Abstract Classes As Dependency-Injection Tokens For Swappable Behaviors In Angular 4.2.3
// Import the core angular services.
import { Component } from "@angular/core";
import { Inject } from "@angular/core";
import { InjectionToken } from "@angular/core";
// ----------------------------------------------------------------------------------- //
// ----------------------------------------------------------------------------------- //
// This injection token can be used to configure the Providers as well as the @Inject()
// meta-data that will tell the dependency-injection container how to locate the desired
// class instance.
var GreeterToken = new InjectionToken<GreeterInterface>( "Greeter behavior" );
interface GreeterInterface {
greeting() : string;
}
class NiceGreeter implements GreeterInterface {
public greeting() : string {
return( "Hello, what a pleasure to meet you." );
}
}
class MeanGreeter implements GreeterInterface {
public greeting() : string {
return( "Hello, you are a doofus!" );
}
}
// ----------------------------------------------------------------------------------- //
// ----------------------------------------------------------------------------------- //
@Component({
selector: "my-app",
providers: [
// For this application, let's provide the MeanGreeter instance when the
// GreeterToken needs to be injected into the App component.
{
provide: GreeterToken,
useClass: MeanGreeter // <--- Defining the swappable implementation.
}
],
styleUrls: [ "./app.component.css" ],
template:
`
<p>
<strong>Greeting:</strong> {{ greeting }}
</p>
`
})
export class AppComponent {
public greeting: string;
// I initialize the app component.
constructor( @Inject( GreeterToken ) greeter: GreeterInterface ) {
this.greeting = greeter.greeting();
}
}
// Import the core angular services.
import { Component } from "@angular/core";
// ----------------------------------------------------------------------------------- //
// ----------------------------------------------------------------------------------- //
interface GreeterInterface {
greeting() : string;
}
class NiceGreeter implements GreeterInterface {
public greeting() : string {
return( "Hello, what a pleasure to meet you." );
}
}
// CAUTION: Here, I am saying that the "MeanGreeter" IMPLEMENTS the "NiceGreeter."
class MeanGreeter implements NiceGreeter {
public greeting() : string {
return( "Hello, you are a doofus!" );
}
}
// ----------------------------------------------------------------------------------- //
// ----------------------------------------------------------------------------------- //
@Component({
selector: "my-app",
providers: [
// For this application, let's provide the MeanGreeter instance when the
// NiceGreeter needs to be injected into the App component.
{
provide: NiceGreeter,
useClass: MeanGreeter // <--- Defining the swappable implementation.
}
],
styleUrls: [ "./app.component.css" ],
template:
`
<p>
<strong>Greeting:</strong> {{ greeting }}
</p>
`
})
export class AppComponent {
public greeting: string;
// I initialize the app component.
constructor( greeter: NiceGreeter ) {
this.greeting = greeter.greeting();
}
}
// Import the core angular services.
import { Component } from "@angular/core";
// ----------------------------------------------------------------------------------- //
// ----------------------------------------------------------------------------------- //
// Because an Abstract class has a runtime representation, we can use it as a
// dependency-injection (DI) token in Angular's DI container. And, since each concrete
// class has to implement or extend this abstract base class, it means that the base
// class can act as the "interface" to the behavior as well.
abstract class Greeter {
abstract greeting() : string;
}
// NOTE: We could have also used "extends Greeter" if Greeter provided base
// functionality that needed to be shared with its concrete classes.
class NiceGreeter implements Greeter {
public greeting() : string {
return( "Hello, what a pleasure to meet you." );
}
}
// NOTE: We could have also used "extends Greeter" if Greeter provided base
// functionality that needed to be shared with its concrete classes.
class MeanGreeter implements Greeter {
public greeting() : string {
return( "Hello, you are a doofus!" );
}
}
// ----------------------------------------------------------------------------------- //
// ----------------------------------------------------------------------------------- //
@Component({
selector: "my-app",
providers: [
// For this application, let's provide the MeanGreeter instance when the
// Greeter needs to be injected into the App component.
{
provide: Greeter,
useClass: MeanGreeter // <--- Defining the swappable implementation.
}
],
styleUrls: [ "./app.component.css" ],
template:
`
<p>
<strong>Greeting:</strong> {{ greeting }}
</p>
`
})
export class AppComponent {
public greeting: string;
// I initialize the app component.
constructor( greeter: Greeter ) {
this.greeting = greeter.greeting();
}
}
class NiceGreeter implements GreeterInterface {
private value: string = "Hello, what a pleasure to meet you.";
public greeting() : string {
return( this.value );
}
}
// CAUTION: Here, I am saying that the "MeanGreeter" IMPLEMENTS the "NiceGreeter."
class MeanGreeter implements NiceGreeter {
public greeting() : string {
return( "Hello, you are a doofus!" );
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment