How to Manage Global Objects in Angular: Best Practices

How to Manage Global Objects in Angular: Best Practices

Global Object Management in Angular Tips and Tricks

When we use external libraries, it is widespread to declare and use a global object. But the price to pay is to get a complex testing scenario and, of course global an object like magic is not a “good practice”.

How do you tell Angular about an external library declared as global?

My example was using the leaflet library, the InjectionToken class, and @Inject.

If you want to read more about it.

Install Leaflet

Install the leaflet package and register into angular.json to load the library.

npm install leaflet

Open the angular.json file and add leaflet.css and leaflet.js assets.

     "styles": [
         "src/styles.css",
         "node_modules/leaflet/dist/leaflet.css"
        ],
     "scripts": [
         "node_modules/leaflet/dist/leaflet.js"             
       ]
},
          "configurations": { ...

Leaflet API

We define the contract with the global object to use the methods provided by the Leaflet. It's optional but makes our code easy to follow, so create an interface with the public methods.

export interface LeafletAPI { 
    map(id:string):object;
   setView(points: [], id:number): object;
   tileLayer(url:string, options:object): object;
   addTo(map:object):void;
}

Use the Injection Token Class

Import the InjectionToken class from @angular/core, and it helps us create a new instance, given the LeafletAPI. And find the global object using a string name. The leaflet value is “L”.

import { InjectionToken} from '@angular/core';
export let LEAFLET_TOKEN = new InjectionToken<LeafletAPI>('L');

Provide the Leaflet

In the AppModule, declare a variable for the L, register the LEAFLET_TOKEN and set the useValue to L, into the providers.

Now, Angular returns an instance of L when someone when requests the LEAFLET_TOKEN to be injected.

import { NgModule } from '@angular/core'; 
import { BrowserModule } from '@angular/platform-browser'; 
import { AppComponent } from './app.component';
import { LealefAPI, LEALEF_TOKEN } from './services/lealef.injector'; declare let L: LealefAPI;

@NgModule(
{ 
declarations: [ AppComponent ], 
imports: [BrowserModule], 
providers: [ { provide: LEALEF_TOKEN, useValue: L} ], 
bootstrap: [AppComponent] })
export class AppModule { }

Using @Inject

The @Inject() allows us to let Angular know which object must be injected, so using the token, the DI will return the value declared in the providers for our token.

In our case, the key is the LEAFLET_TOKEN. Angular load it from our register provider and create a new service MapService, in the constructor, use declare leaflet field using @Inject and the token.

import { Inject, Injectable } from '@angular/core';
import { LeafletAPI, LEAFLET_TOKEN } from './lealef.injector';

@Injectable()
export class MapService {
    constructor(@Inject(LEAFLET_TOKEN) private _leaflet: LealefAPI) { }

The Leaflet was injected on the MapService by the Angular dependency injector, and we are ready to use the methods provided by LealefAPI.

@Injectable()
export class MapService {
   constructor(@Inject(LEAFLET_TOKEN) private _leaflet: LealefAPI) { }

   showMenorca(): void {
        let map = this._leaflef.map('mapid').setView([39.9255, 4.032], 13);
        const tiles = this._leaflef.tileLayer(
            'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
            {
                maxZoom: 8,
                minZoom: 3
            }
        );        
        tiles.addTo(map);       
    }
   }
}

That's it!

Hopefully, that will give you a bit of help with how to avoid global objects and use InjectionToken and @Inject. If you enjoyed this post, share it!

Photo by Fernando @cferdo on Unsplash