J'utilise AngularJS et TypeScript. Je veux implémenter un service AngularJS à l'aide d'une classe TypeScript, comme ceci:
class HelloService {
public getWelcomeMessage():String {
return "Hello";
}
}
angular.module('app.services.helloService', []).factory('helloService', () => {
return new HelloService();
});
Cela se compile dans le code javascript suivant:
var HelloService = (function () {
function HelloService() {
}
HelloService.prototype.getWelcomeMessage = function () {
return "Hello";
};
return HelloService;
})();
angular.module('app.services.helloService', []).factory('helloService', function () {
return new HelloService();
});
Cela pollue l'espace de noms global avec la variable HelloService, ce que je ne veux évidemment pas. (En utilisant la console de Chrome, j'ai vérifié que HelloService était un objet.) Comment puis-je résoudre/éviter ce problème?
J'ai essayé l'évidence:
angular.module('app.services.helloService', []).factory('helloService', function () {
class HelloService { ...}
return new HelloService();
});
mais cela me donne une erreur de compilation ("Jeton inattendu; 'instruction' attendue.").
Une solution possible à laquelle je peux penser est d'utiliser l'importation et l'exportation de TypeScript d'une manière ou d'une autre, qui à son tour utilisera RequireJS. Cela enveloppera probablement le HelloService dans une fonction de définition, évitant ainsi la pollution de la portée globale avec HelloService. Cependant, je ne veux pas utiliser RequireJS dans mon application AngularJS pour l'instant, car je pense que AngularJS est assez bon pour mon utilisation, et cela ajoute de la complexité.
Donc, ma question est, comment puis-je définir un service AngularJS en utilisant une classe TypeScript qui ne pollue pas la portée globale?
Je devrais fournir ce que j'ai fini par faire:
module MyModule {
export class HelloService {
public getWelcomeMessage():String {
return "Hello";
}
}
angular.module('app.services.helloService', []).factory('helloService', () => {
return new HelloService();
});
}
De cette façon, je peux utiliser
return new HelloService();
au lieu de
return new MyModule.HelloService();
Le tableau et le constructeur statiques $inject
Restent inchangés par rapport à l'exemple précédent.
La seule modification consiste à diviser les classes en plusieurs fichiers et à utiliser les modules ES6 pour extraire les définitions de classe.
/lib/HelloService.ts:
export class HelloService {
public getWelcomeMessage():String {
return "Hello from HelloService";
}
}
/lib/AnotherService.ts:
import {HelloService} from './HelloService';
/**
* Service that depends on HelloService.
*/
export class AnotherService {
// Define `HelloService` as a dependency.
static $inject = ['HelloService'];
constructor(
// Add the parameter and type definition.
public HelloService: HelloService
){}
public getWelcomeMessage():String {
// Access the service as: `this.HelloService`
// Enjoy auto-completion and type safety :)
var helloMsg = this.HelloService.getWelcomeMessage();
return "Welcome from AnotherService, " + helloMsg;
}
}
/index.ts:
// Using the services.
import {HelloService} from './lib/HelloService';
import {AnotherService} from './lib/AnotherService';
angular.module('HelloApp', [])
.service('HelloService', HelloService)
.service('AnotherService', AnotherService)
.run(['AnotherService', function(AnotherService: AnotherService){
console.log(AnotherService.getWelcomeMessage());
}]);
Construire à partir de réponse de Steve Fenton :
Pour permettre l'injection de dépendances, ajoutez un tableau statique $inject
Sur votre classe.
Voir la documentation Angular $ injector sur le fonctionnement du tableau $inject
.
Les dépendances seront injectées dans votre constructeur dans l'ordre donné par le tableau (et le feront fonctionner avec la minification).
Exemple d'injection de dépendance:
namespace MyModule {
/**
* Angular Service
*/
export class HelloService {
public getWelcomeMessage():String {
return "Hello from HelloService";
}
}
/**
* Service that depends on HelloService.
*/
export class AnotherService {
// Define `HelloService` as a dependency.
static $inject = ['HelloService'];
constructor(
// Add the parameter and type definition.
public HelloService: MyModule.HelloService
){}
public getWelcomeMessage():String {
// Access the service as: `this.HelloService`
// Enjoy auto-completion and type safety :)
var helloMsg = this.HelloService.getWelcomeMessage();
return "Welcome from AnotherService, " + helloMsg;
}
}
}
// Using the services.
angular.module('app.services.helloService', [])
.service('HelloService', MyModule.HelloService)
.service('AnotherService', MyModule.AnotherService)
.run(['AnotherService', function(AnotherService: MyModule.AnotherService){
console.log(AnotherService.getWelcomeMessage());
}]);
J'ai deux solutions, la première vous donne une syntaxe basée sur les classes, la seconde ne laisse absolument rien dans la portée globale ...
Vous pouvez compromettre légèrement en ajoutant une seule poignée à la portée globale (cela s'applique vraiment si vous avez plusieurs classes que vous souhaitez éviter de placer dans la portée globale, car vous n'avez actuellement qu'une seule classe).
Le code suivant ne laisse que le module dans la portée globale.
module MyModule {
export class HelloService {
public getWelcomeMessage():String {
return "Hello";
}
}
export class AnotherService {
public getWelcomeMessage():String {
return "Hello";
}
}
}
angular.module('app.services.helloService', []).factory('helloService', () => {
return new MyModule.HelloService();
});
angular.module('app.services.anotherService', []).factory('anotherService', () => {
return new MyModule.AnotherService();
});
Alternativement, pour ne pas laisser une seule chose dans la portée globale, vous pouvez éviter la syntaxe de classe et utiliser "JavaScript ancien simple":
angular.module('app.services.helloService', []).factory('helloService', () => {
var HelloService = (function () {
function HelloService() {
}
HelloService.prototype.getWelcomeMessage = function () {
return "Hello";
};
return HelloService;
})();
return new HelloService();
});