Web Programming TutorialsLearn How to Prevent Name Collisions in Angular 2 Providers

Learn How to Prevent Name Collisions in Angular 2 Providers

Prevent Name Collisions

The concept of dependency injection in Angular 2 makes it a very unique front end technology. Here, the dependencies can be made available to the injector either by using string or type tokens. However, when we use the string tokens, there may be a possibility of running into naming collisions i.e. the use of same token name by the different providers. In this article, we are going to understand the creation of the “opaque tokens” in order to prevent such name collisions.

Let’s understand the differences between the string and type tokens. To understand the differences, we must refer an earlier article of this series ‘Angular 2 Provides using Map Literals’ which has the working demonstration of Angular 2 providers.

The provider-registered factories are capable of injecting singleton instances for Angular dependency injection. Such instances are injected at the runtime. Therefore, the Providers are setup in order to configure dependency injection and associate a factory with a token in an application. Such a provider token could be either a string or a type. For example, in the Map Literals example, we had used an EmployeeService class to inject an instance of it when we ask for a dependency of that type in the application, we would use EmployeeService as a provider token as shown below (in bold).

import { Component, Input, OnInit } from '@angular/core';
import { ActivatedRoute, Params } from '@angular/router';
import { Employee } from './employee';
import { EmployeeService } from './employee.service';

@Component({
  selector: 'my-employee-detail',
  templateUrl: 'resource/employee-detail.component.html',
  styleUrls: ['assets/employee-detail.component.css'],
  providers: [  
  	{ provide: EmployeeService, 
        useClass: EmployeeService 
      }
  ] // creates a provider for EmployeeService
})
export class EmployeeDetailComponent implements OnInit {

  @Input() employee: Employee;
  
  constructor(
  	private employeeService: EmployeeService,
  	private route: ActivatedRoute) {
  	
	}
  ngOnInit(): void {
  	this.route.params.forEach((params: Params) => {
    	let id = +params['id'];
    	this.employeeService.getEmployee(id)
      	.then(employee => this.employee = employee);
  		});
	}
  goBack(): void {
  	window.history.back();
  }
}

In the above example, the token matches the dependency as instance-type and ‘useClass’ is the provider strategy. Alternatively, the shorthand version can also be used as shown below. This should be noted that the Angular 2 has many such shorthand versions for dependency Injection, annotations, etc.

@Component({
  selector: 'my-employee-detail',
  templateUrl: 'resource/employee-detail.component.html',
  styleUrls: ['assets/employee-detail.component.css'],
  providers: [EmployeeService] 
// creates a provider for EmployeeService
})

As long as, we are representing the providers as classes (or types) there won’t be any naming collisions. But, there are the occasions when we require to create objects without a class representation in order to inject dependency into our application. For example, the configuration object (shown below) could be the simple object literal where no type is involved.

const CONFIGURATION = {
  Url: 'http://mywebsite.com',
  theme: 'cool-blue',
  title: 'My Website app',
  platform: ‘Angular 2’
};

Sometimes, we may require to inject a primitive value such as a string or a Boolean value for dependency injection into an application as shown below.

const ENABLE_FLAG = true;

In such cases, the use of a String or Boolean type are not recommended, as this is going to set a default value for place where we are asking for dependencies of these types in our application. Actually, there is no need to introduce a new type for the representation of these values. This is where the string tokens comes into picture which allows us to make the objects available through dependency injection without the introduction of an actual type as shown below.

let enabledFlagToken = 'enabled';
let configurationToken = 'config';

@Component({
  selector: 'my-employee-detail',
  templateUrl: 'resource/employee-detail.component.html',
  styleUrls: ['assets/employee-detail.component.css'],

  providers: [
  { provide: enabledFlagToken, useValue: ENABLE_FLAG },
  { provide: configurationToken, useValue: CONFIGURATION }]
})

In the above example, we are using a simple string as a token in the place of a type. We can inject this dependency using the ‘@Inject ()’ decorator as shown below. In the below example, we have used the ‘@Inject (enabledFlagToken) private enabled’ without any typing information such as ‘private enabled: Boolean’.

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

let enabledFlagToken = 'enabled';
let configurationToken = 'config';

@Component({
  selector: 'my-employee-detail',
  templateUrl: 'resource/employee-detail.component.html',
  styleUrls: ['assets/employee-detail.component.css'],

  providers: [
  { provide: enabledFlagToken, useValue: ENABLE_FLAG },
  { provide: configurationToken, useValue: CONFIGURATION }]
})

class MyComponent {

  constructor(
    @Inject(enabledFlagToken) private enabled,
    @Inject(configurationToken) private config
  ) {...}
}

This concludes that we can use either types or strings as tokens in order to inject dependencies into our application. But, use of string tokens are prone to the naming collisions. For example, we had used ‘config’ as the string token which happens to be very commonly used word in the programming world. If someone has used the same named string token then it will cause naming collision. Under such situation, the Providers are flattened. It means that, if there are multiple providers with the same token, then the last token will win.

Opaque Tokens to counter naming collisions
Angular 2 team was aware of the situation of naming collision for string tokens. Therefore, they come up with the concept of OpaqueToken which allows us to create string-based tokens without letting run our application into any type of naming collision. The following example demonstrate the creation of an OpaqueToken.

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

const CONFIGURATION_TOKEN = new OpaqueToken('config');

export const THIRD_PARTY_LIBRARY_PROVIDERS = [
  { provide: CONFIGURATION_TOKEN, useClass: ThirdPartyConfiguration }
];

Firstly, we need to import ‘OpaqueToken’ from ‘@angular/core’ and use it.
The above example demonstrates the third-party provider’s collection by using the OpaqueToken.
Now, let us create our application by using an OpaqueToken for our ‘config’ token as shown below.

import { OpaqueToken } from '@angular/core';
import THIRD_PARTY_LIBRARY_PROVIDERS from './third-party-library-pack';

const MY_CONFIGURATION_TOKEN = new OpaqueToken('config');

...
providers = [
  EmployeeService,
  THIRD_PARTY_LIBRARY_PROVIDERS,
  { provide: MY_CONFIGURATION_TOKEN, useValue: CONFIG } 
]

In the above scenario, there appears the situation of the naming collision between the third party library and our application as both are using same string token ‘config’. Since, we have created these string tokens as OpaqueToken. Therefore, the Angular 2 is smart enough to resolve the runtime dependencies and prevent the naming collisions.

Source code for the Prevent Name Collisions in Angular 2 Providers

Conclusion: –
In this article, we’ve differentiated between the string and type tokens and demonstrated the use of OpaqueToken for string tokens in Angular 2 in order to prevent the naming collisions.

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Exclusive content

- Advertisement -

Latest article

21,501FansLike
4,106FollowersFollow
106,000SubscribersSubscribe

More article

- Advertisement -