Anche se lo scopo primario di Angular è la realizzazione di applicazioni SPA (Single Page Application), a volte può essere necessario instanziare più app nella pagina come se fossero widget e con la possibilità di condividere dati e funzionalità.
L’utilizzo di classi di servizio è stato notevolmente semplificato in Angular2, però questi casi rimangono una rogna soprattutto quando è necessario iniettare nel servizio alcune dipendenze con la dependency injection.
Supponiamo di avere due app Angular2, che d’ora in poi chiameremo impropriamente widget. Entrambi utilizzano una classe di servizio così definita:
// 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; } [...] }
Questo servizio necessita che Angular inietti nel costruttore un oggetto di tipo Http. I widget sono definiti come se fossero normali app:
// 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; } [...] }
Ora qui sorgono i problemi, ad ogni invocazione della funzione bootstrap noi creeremo una nuova instanza di DataService, mentre noi vorremmo utilizzarne solo una.
// Creazione prima istanza DataService bootstrap(Widget1, [ HTTP_BINDINGS, DataService ] ); // Creazione seconda istanza DataService bootstrap(Widget2, [ HTTP_BINDINGS, DataService ] );
Fortunatamente possiamo creare un factory per ricevere sempre una sola instanza, come se fosse un singleton:
// service.ts var sdInstance = null; export function DataServiceFactory(http:Http) { if(sdInstance == null) { sdInstance = new DataService(http); } return sdInstance; }
Ovviamente dobbiamo assicurarci di utilizzare la stessa funzione factory in tutte le attivazioni dei widget:
// Creazione prima istanza DataService bootstrap(Widget1, [ HTTP_BINDINGS, bind(DataService) .toFactory(DataServiceFactory, [Http]), ] ); // Utilizzo instanza creata in precedenza bootstrap(Widget2, [ HTTP_BINDINGS, bind(DataService) .toFactory(DataServiceFactory, [Http]), ] );
Questa potrebbe non essere la soluzione ideale, soprattutto perché gli oggetti iniettati nel servizio in realtà appartengono solo a una delle applicazioni/widget Angular, ma è un ottimo compromesso di facile applicazione e comprensione per chi si troverà a dover poi interpretare il codice.