Angular2: single service for multiple apps

Angular2 is great for SPA (Single Page Application), but sometime you need to use multiple apps in a page because you just want to add some Angular “widgets” apps.

If you want to use shared data, you can use a service class, but the dependency injection in this case is not easy as it looks.

We have two Angular2 apps that use a simple service class:

// service.ts
import {Injectable, Inject} from 'angular2/angular2';
import {HTTP_BINDINGS, Http} from 'angular2/http';

@Injectable()
export class DataService {
    http: Http;

    updated = new EventEmitter();

    constructor(@Inject(Http) http:Http) {
        this.http = http;
    }

    [...]
}

This service needs the Http binding from the dependecy injection. The widgets apps use it in their methods:

// widget1.ts
import {Component, View, bind} from 'angular2/angular2';
import {HTTP_BINDINGS, Http} from 'angular2/http';
import {DataService} from './service';

@Component({
    selector: 'widget1',
})
@View({[...]})
class Widget1 {

    dataService: DataService;
    
    constructor(dataService: DataService) {
        this.dataService = dataService;
    }

    [...]
}

Here come the hard part: every bootstrap execution will create an instance of the DataService type, but we want only one single instance.

// DataService object created
bootstrap(Widget1, [
    HTTP_BINDINGS,        
    DataService
  ]
);

// DataService object created
bootstrap(Widget2, [
    HTTP_BINDINGS,        
    DataService
  ]
);

We can avoid this behaviour with a factory that will create the instance and act like a singleton:

// service.ts
var sdInstance = null;

export function DataServiceFactory(http:Http) {
    if(sdInstance == null) {
        sdInstance = new DataService(http);
    }

    return sdInstance;
}

Now we have to change the injector bindings into the bootstrap executions for every widget:

// DataService object created
bootstrap(Widget1, [
    HTTP_BINDINGS,        
    bind(DataService)
      .toFactory(DataServiceFactory, [Http]),
  ]
);

// DataService object not created
bootstrap(Widget2, [
    HTTP_BINDINGS,        
    bind(DataService)
      .toFactory(DataServiceFactory, [Http]),
  ]
);

This solution isn’t free from any defects. The DataService instance is injected by the injector of the first widget who calls the factory, so the service belongs it. But this pattern is easy to write and understand.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.