J'ai vu les deux angular.factory () et angular.service () utilisé pour déclarer des services; cependant, je ne peut pas trouverangular.service
n'importe où dans la documentation officielle.
Quelle est la différence entre les deux méthodes? Ce qui devrait être utilisé pour quoi (en supposant qu'ils fassent des choses différentes)?
angular.service('myService', myServiceFunction);
angular.factory('myFactory', myFactoryFunction);
J'ai eu du mal à comprendre ce concept avant de le formuler ainsi:
Service: la fonction que vous écrivez sera nouvelle - ed:
myInjectedService <---- new myServiceFunction()
Factory: la fonction (constructeur) que vous écrivez sera invoquée:
myInjectedFactory <--- myFactoryFunction()
Ce que vous faites avec cela dépend de vous, mais il y a quelques modèles utiles ...
function myServiceFunction() {
this.awesomeApi = function(optional) {
// calculate some stuff
return awesomeListOfValues;
}
}
---------------------------------------------------------------------------------
// Injected in your controller
$scope.awesome = myInjectedService.awesomeApi();
function myFactoryFunction() {
var aPrivateVariable = "yay";
function hello() {
return "hello mars " + aPrivateVariable;
}
// expose a public API
return {
hello: hello
};
}
---------------------------------------------------------------------------------
// Injected in your controller
$scope.hello = myInjectedFactory.hello();
function myFactoryFunction() {
return function() {
var a = 2;
this.a2 = function() {
return a*2;
};
};
}
---------------------------------------------------------------------------------
// Injected in your controller
var myShinyNewObject = new myInjectedFactory();
$scope.four = myShinyNewObject.a2();
Vous pouvez accomplir la même chose avec les deux. Cependant, dans certains cas, la fabrique vous donne un peu plus de flexibilité pour créer un injectable avec une syntaxe plus simple. En effet, même si myInjectedService doit toujours être un objet, myInjectedFactory peut être un objet, une référence de fonction ou toute autre valeur. Par exemple, si vous avez écrit un service pour créer un constructeur (comme dans le dernier exemple ci-dessus), il devrait être instancié de la manière suivante:
var myShinyNewObject = new myInjectedService.myFunction()
ce qui est sans doute moins souhaitable que cela:
var myShinyNewObject = new myInjectedFactory();
(Mais vous devez d’abord vous méfier de l’utilisation de ce type de motif car de nouveaux objets - dans vos contrôleurs créent des dépendances difficiles à suivre Il est difficile de se moquer des tests. Il est préférable d’avoir un service qui gère une collection d’objets pour vous plutôt que d’utiliser new()
wily-nilly.
N'oubliez pas non plus que dans les deux cas, angular vous aide à gérer un singleton. Quels que soient le lieu ou le nombre de fois où vous injectez votre service ou votre fonction, vous obtiendrez la même référence au même objet ou à la même fonction. (À l'exception des cas où une usine renvoie simplement une valeur telle qu'un nombre ou une chaîne. Dans ce cas, vous obtiendrez toujours la même valeur, mais pas une référence.)
Tout simplement ..
// Service
service = (a, b) => {
a.lastName = b;
return a;
};
// Factory
factory = (a, b) => Object.assign({}, a, { lastName: b });
const fullName = { firstName: 'john' };
// Service
const lastNameService = (a, b) => {
a.lastName = b;
return a;
};
console.log(lastNameService(fullName, 'doe'));
// Factory
const lastNameFactory = (a, b) =>
Object.assign({}, a, { lastName: b })
console.log(lastNameFactory(fullName, 'doe'));
Voici les principales différences:
Syntaxe: module.service( 'serviceName', function );
Résultat: lors de la déclaration de serviceName en tant qu'argument injectable, l'instance d'une fonction sera transmise à module.service
.
Utilisation: Peut être utile pour partager des fonctions utilitaires qu'il est utile d'invoquer en ajoutant simplement ( )
à la référence de fonction injectée. Peut également être exécuté avec injectedArg.call( this )
ou similaire.
Syntaxe: module.factory( 'factoryName', function );
Résultat: lors de la déclaration de factoryName en tant qu'argument injectable, vous recevrez la valeur renvoyée en appelant la référence de fonction transmise à module.factory
.
Utilisation: Peut être utile pour renvoyer une fonction 'class' qui peut ensuite être créée pour créer des instances.
Voici exemple utilisant services et factory . En savoir plus sur AngularJS Service vs Factory .
Vous pouvez également consulter le documentation AngularJS et une question similaire sur stackoverflow confus sur le service vs l'usine .
TL; DR
1) Lorsque vous utilisez un Factory, vous créez un objet, ajoutez-lui des propriétés, puis retournez le même objet. Lorsque vous transmettez cette fabrique à votre contrôleur, les propriétés de l'objet sont désormais disponibles dans ce contrôleur via votre fabrique.
app.controller('myFactoryCtrl', function($scope, myFactory){
$scope.artist = myFactory.getArtist();
});
app.factory('myFactory', function(){
var _artist = 'Shakira';
var service = {};
service.getArtist = function(){
return _artist;
}
return service;
});
2) Lorsque vous utilisez Service, Angular l'instancie en arrière-plan avec le mot clé "new". De ce fait, vous ajouterez des propriétés à "this" et le service renverra "this". Lorsque vous transmettez le service à votre contrôleur, les propriétés de ‘this’ seront désormais disponibles sur ce contrôleur via votre service.
app.controller('myServiceCtrl', function($scope, myService){
$scope.artist = myService.getArtist();
});
app.service('myService', function(){
var _artist = 'Nelly';
this.getArtist = function(){
return _artist;
}
});
Non TL; DR
1) Usine
Les usines sont le moyen le plus populaire de créer et de configurer un service. Il n’ya vraiment pas grand chose de plus que ce que dit le TL, DR. Vous créez simplement un objet, lui ajoutez des propriétés, puis vous retournez ce même objet. Ensuite, lorsque vous transmettez la fabrique à votre contrôleur, les propriétés de l'objet sont désormais disponibles dans ce contrôleur via votre fabrique. Un exemple plus complet est ci-dessous.
app.factory('myFactory', function(){
var service = {};
return service;
});
Désormais, les propriétés que nous attachons à ‘service’ seront disponibles lorsque nous transmettons ‘myFactory’ à notre contrôleur.
Ajoutons maintenant quelques variables "privées" à notre fonction de rappel. Celles-ci ne seront pas directement accessibles depuis le contrôleur, mais nous allons éventuellement configurer certaines méthodes d’affichage/définition sur le service afin de pouvoir modifier ces variables "privées" en cas de besoin.
app.factory('myFactory', function($http, $q){
var service = {};
var baseUrl = 'https://iTunes.Apple.com/search?term=';
var _artist = '';
var _finalUrl = '';
var makeUrl = function(){
_artist = _artist.split(' ').join('+');
_finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK';
return _finalUrl
}
return service;
});
Ici, vous remarquerez que nous n’attachons pas ces variables/fonctions à ‘service’. Nous les créons simplement pour pouvoir les utiliser ou les modifier ultérieurement.
Maintenant que nos variables et fonctions auxiliaires/privées sont en place, ajoutons quelques propriétés à l’objet 'service'. Quel que soit le type de service que nous mettons en service, nous pourrons l’utiliser directement sur le contrôleur par lequel nous passons ‘myFactory’.
Nous allons créer les méthodes setArtist et getArtist qui renvoient ou définissent simplement l'artiste. Nous allons également créer une méthode qui appellera l'API iTunes avec notre URL créée. Cette méthode va renvoyer une promesse qui sera remplie une fois que les données seront revenues de l'API iTunes. Si vous n’avez pas beaucoup d’expérience dans l’utilisation des promesses dans Angular, je vous recommande vivement de les approfondir.
En dessous de setArtist accepte un artiste et vous permet de le définir. getArtist renvoie l'artiste callItunes appelle d'abord makeUrl () afin de générer l'URL que nous utiliserons avec notre requête $ http. Ensuite, il configure un objet de promesse, fait une requête $ http avec notre URL finale, puis, puisque $ http renvoie une promesse, nous pouvons appeler .success ou .error après notre requête. Nous résolvons ensuite notre promesse avec les données iTunes ou nous la rejetons avec un message disant "Il y avait une erreur".
app.factory('myFactory', function($http, $q){
var service = {};
var baseUrl = 'https://iTunes.Apple.com/search?term=';
var _artist = '';
var _finalUrl = '';
var makeUrl = function(){
_artist = _artist.split(' ').join('+');
_finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK'
return _finalUrl;
}
service.setArtist = function(artist){
_artist = artist;
}
service.getArtist = function(){
return _artist;
}
service.callItunes = function(){
makeUrl();
var deferred = $q.defer();
$http({
method: 'JSONP',
url: _finalUrl
}).success(function(data){
deferred.resolve(data);
}).error(function(){
deferred.reject('There was an error')
})
return deferred.promise;
}
return service;
});
Maintenant, notre usine est terminée. Nous sommes maintenant en mesure d’injecter ‘myFactory’ dans n’importe quel contrôleur et nous pourrons ensuite appeler les méthodes que nous avons attachées à notre objet service (setArtist, getArtist et callItunes).
app.controller('myFactoryCtrl', function($scope, myFactory){
$scope.data = {};
$scope.updateArtist = function(){
myFactory.setArtist($scope.data.artist);
};
$scope.submitArtist = function(){
myFactory.callItunes()
.then(function(data){
$scope.data.artistData = data;
}, function(data){
alert(data);
})
}
});
Dans le contrôleur ci-dessus, nous injectons dans le service ‘myFactory’. Nous définissons ensuite les propriétés de notre objet $ scope provenant de données provenant de ‘myFactory’. Le seul code difficile ci-dessus est si vous n’avez jamais tenu vos promesses auparavant. Puisque callItunes renvoie une promesse, nous pouvons utiliser la méthode .then () et définir uniquement $ scope.data.artistData une fois que notre promesse est remplie avec les données iTunes. Vous remarquerez que notre contrôleur est très "mince". Toutes nos données logiques et persistantes se trouvent dans notre service, pas dans notre contrôleur.
2) Service
Peut-être que la chose la plus importante à savoir lors de la création d’un service est qu’il est instancié avec le mot-clé "nouveau". Pour vous, gourous de JavaScript, cela devrait vous donner un indice important sur la nature du code. Pour ceux d'entre vous qui ont une connaissance limitée de JavaScript ou ceux qui ne sont pas trop au courant du contenu du "nouveau" mot clé, passons en revue quelques principes fondamentaux de JavaScript qui nous aideront éventuellement à comprendre la nature d'un service.
Pour vraiment voir les changements qui surviennent lorsque vous appelez une fonction avec le mot-clé ‘nouveau’, créez une fonction et appelez-la avec le mot-clé ‘nouveau’, puis montrons ce que l’interprète fait quand il voit le mot-clé ‘nouveau’. Les résultats finaux seront les mêmes.
Commençons par créer notre constructeur.
var Person = function(name, age){
this.name = name;
this.age = age;
}
C'est une fonction constructeur JavaScript typique. Maintenant, chaque fois que nous appelons la fonction Person à l’aide du mot-clé "new", "this" sera lié au nouvel objet créé.
Ajoutons maintenant une méthode sur le prototype de notre personne afin qu’elle soit disponible sur chaque instance de notre classe de personnes.
Person.prototype.sayName = function(){
alert('My name is ' + this.name);
}
Maintenant, comme nous mettons la fonction sayName sur le prototype, chaque instance de Person pourra appeler la fonction sayName afin d’alerter le nom de cette instance.
Maintenant que nous avons notre fonction constructeur Person et notre fonction sayName sur son prototype, créons en réalité une instance de Person puis appelons la fonction sayName.
var tyler = new Person('Tyler', 23);
tyler.sayName(); //alerts 'My name is Tyler'
Donc, ensemble, le code permettant de créer un constructeur Person, d’ajouter une fonction à son prototype, de créer une instance Person, puis d’appeler la fonction sur son prototype ressemble à ceci.
var Person = function(name, age){
this.name = name;
this.age = age;
}
Person.prototype.sayName = function(){
alert('My name is ' + this.name);
}
var tyler = new Person('Tyler', 23);
tyler.sayName(); //alerts 'My name is Tyler'
Voyons maintenant ce qui se passe réellement lorsque vous utilisez le mot-clé "nouveau" en JavaScript. La première chose que vous devriez remarquer est qu’après avoir utilisé "new" dans notre exemple, nous pouvons appeler une méthode (sayName) sur "tyler" comme si c’était un objet - c’est parce que c’est. Donc, d’abord, nous savons que notre constructeur Personne renvoie un objet, que nous puissions ou non le voir dans le code. Deuxièmement, nous savons que, puisque notre fonction sayName est située sur le prototype et non directement sur l'instance de personne, l'objet renvoyé par la fonction Person doit être délégué à son prototype lors de recherches en échec. En termes plus simples, lorsque nous appelons tyler.sayName (), l’interprète dit "OK, je vais regarder sur l’objet" tyler "que nous venons de créer, recherchez la fonction sayName, puis appelez-le. Attendez une minute, je ne le vois pas ici - je ne vois que le nom et l’âge, laissez-moi vérifier le prototype. Oui, on dirait que c'est sur le prototype, laissez-moi l'appeler. ”.
Vous trouverez ci-dessous un code indiquant comment vous pouvez penser à ce que le "nouveau" mot-clé fait réellement en JavaScript. Il s’agit d’un exemple de code du paragraphe ci-dessus. J'ai mis la "vue de l'interprète" ou la façon dont l'interprète voit le code à l'intérieur des notes.
var Person = function(name, age){
//The line below this creates an obj object that will delegate to the person's prototype on failed lookups.
//var obj = Object.create(Person.prototype);
//The line directly below this sets 'this' to the newly created object
//this = obj;
this.name = name;
this.age = age;
//return this;
}
Maintenant que vous savez maintenant ce que le "nouveau" mot-clé fait réellement en JavaScript, la création d'un service dans Angular devrait être plus facile à comprendre.
La chose la plus importante à comprendre lors de la création d’un service est de savoir que les services sont instanciés avec le mot clé "nouveau". En combinant ces connaissances avec nos exemples ci-dessus, vous devez maintenant reconnaître que vous allez associer vos propriétés et méthodes directement à ‘this’, qui seront ensuite renvoyées par le service lui-même. Voyons cela en action.
Contrairement à ce que nous avons fait à l’origine avec l’exemple Factory, nous n’avons pas besoin de créer un objet, mais de le renvoyer, car, comme cela a été mentionné à maintes reprises, nous avons utilisé le mot clé 'new' pour que l’interprète crée cet objet et le délègue à. c'est un prototype, puis retournez-le nous sans que nous ayons à faire le travail.
Commençons par créer notre fonction "privée" et notre fonction d’aide. Cela devrait sembler très familier puisque nous avons fait exactement la même chose avec notre usine. Je n’expliquerai pas ce que chaque ligne fait ici car je l’ai fait dans l’exemple d’usine. Si vous êtes confus, relisez l’exemple d’usine.
app.service('myService', function($http, $q){
var baseUrl = 'https://iTunes.Apple.com/search?term=';
var _artist = '';
var _finalUrl = '';
var makeUrl = function(){
_artist = _artist.split(' ').join('+');
_finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK'
return _finalUrl;
}
});
Maintenant, nous allons attacher toutes les méthodes qui seront disponibles dans notre contrôleur à ‘this’.
app.service('myService', function($http, $q){
var baseUrl = 'https://iTunes.Apple.com/search?term=';
var _artist = '';
var _finalUrl = '';
var makeUrl = function(){
_artist = _artist.split(' ').join('+');
_finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK'
return _finalUrl;
}
this.setArtist = function(artist){
_artist = artist;
}
this.getArtist = function(){
return _artist;
}
this.callItunes = function(){
makeUrl();
var deferred = $q.defer();
$http({
method: 'JSONP',
url: _finalUrl
}).success(function(data){
deferred.resolve(data);
}).error(function(){
deferred.reject('There was an error')
})
return deferred.promise;
}
});
Désormais, comme dans notre usine, setArtist, getArtist et callItunes seront disponibles dans le contrôleur par lequel nous passerons à myService. Voici le contrôleur myService (qui est presque identique à notre contrôleur d’usine).
app.controller('myServiceCtrl', function($scope, myService){
$scope.data = {};
$scope.updateArtist = function(){
myService.setArtist($scope.data.artist);
};
$scope.submitArtist = function(){
myService.callItunes()
.then(function(data){
$scope.data.artistData = data;
}, function(data){
alert(data);
})
}
});
Comme je l’ai déjà mentionné, une fois que vous comprenez vraiment ce que fait le "nouveau", les services sont presque identiques aux usines Angular.
Les services et les usines sont semblables les uns aux autres. Les deux vont donner un objet singleton qui peut être injecté dans d'autres objets, et sont donc souvent utilisés de manière interchangeable.
Ils sont destinés à être utilisés sémantiquement pour implémenter différents modèles de conception.
Un modèle de service est un modèle dans lequel votre application est divisée en unités de fonctionnalité logiquement cohérentes. Un exemple peut être un accesseur d'API ou un ensemble de logique métier.
Ceci est particulièrement important dans Angular parce que les modèles Angular ne sont généralement que des objets JSON extraits d'un serveur. Nous avons donc besoin d'un emplacement pour placer notre logique métier.
Voici un service Github par exemple. Il sait comment parler à Github. Il connaît les URL et les méthodes. Nous pouvons l'injecter dans un contrôleur, et il générera et retournera une promesse.
(function() {
var base = "https://api.github.com";
angular.module('github', [])
.service('githubService', function( $http ) {
this.getEvents: function() {
var url = [
base,
'/events',
'?callback=JSON_CALLBACK'
].join('');
return $http.jsonp(url);
}
});
)();
Les usines, par contre, sont destinées à mettre en œuvre un modèle d’usine. Un modèle d'usine dans lequel nous utilisons une fonction d'usine pour générer un objet. En règle générale, nous pourrions l’utiliser pour construire des modèles. Voici une usine qui retourne un constructeur Author:
angular.module('user', [])
.factory('User', function($resource) {
var url = 'http://simple-api.herokuapp.com/api/v1/authors/:id'
return $resource(url);
})
Nous utiliserions ceci comme ceci:
angular.module('app', ['user'])
.controller('authorController', function($scope, User) {
$scope.user = new User();
})
Notez que les usines retournent aussi des singletons.
Puisqu'une usine renvoie simplement un objet, elle peut renvoyer n'importe quel type d'objet, y compris une fonction constructeur, comme nous le voyons ci-dessus.
Une autre différence technique réside dans la composition des services et des usines. Une fonction de service sera créée pour générer l'objet. Une fonction d'usine sera appelée et retournera l'objet.
Cela signifie que dans un service, nous ajoutons "ce" qui, dans le contexte d'un constructeur, désignera l'objet en construction.
Pour illustrer cela, voici le même objet simple créé à l'aide d'un service et d'une fabrique:
angular.module('app', [])
.service('helloService', function() {
this.sayHello = function() {
return "Hello!";
}
})
.factory('helloFactory', function() {
return {
sayHello: function() {
return "Hello!";
}
}
});
Toutes les réponses ici semblent concerner le service et l’usine, ce qui est valable puisque c’est ce qui a été demandé. Mais il est également important de garder à l'esprit qu'il en existe plusieurs autres, dont provider()
, value()
et constant()
.
La clé à retenir est que chacun est un cas particulier de l'autre. Chaque cas particulier en bas de la chaîne vous permet de faire la même chose avec moins de code. Chacun ayant également une limitation supplémentaire.
Pour décider quand utiliser lequel vous voyez lequel vous permet de faire ce que vous voulez avec moins de code. Voici une image illustrant à quel point elles sont similaires:
Pour une description complète étape par étape et une référence rapide du moment où vous souhaitez les utiliser, vous pouvez visiter le blog où j'ai obtenu cette image:
Construction
Avec les usines, Angular appellera la fonction pour obtenir le résultat. C'est le résultat qui est mis en cache et injecté.
//factory
var obj = fn();
return obj;
Avec les services, Angular appellera la fonction constructeur en appelant new . La fonction construite est mise en cache et injectée.
//service
var obj = new fn();
return obj;
Mise en oeuvre
Les usines renvoient généralement un littéral d'objet car la valeur de retour est ce qui est injecté dans les contrôleurs, les blocs d'exécution, les directives, etc.
app.factory('fn', function(){
var foo = 0;
var bar = 0;
function setFoo(val) {
foo = val;
}
function setBar (val){
bar = val;
}
return {
setFoo: setFoo,
serBar: setBar
}
});
Les fonctions de service ne renvoient généralement rien. Au lieu de cela, ils effectuent l'initialisation et exposent des fonctions. Les fonctions peuvent également faire référence à 'this' puisqu'elle a été construite avec 'new'.
app.service('fn', function () {
var foo = 0;
var bar = 0;
this.setFoo = function (val) {
foo = val;
}
this.setBar = function (val){
bar = val;
}
});
Conclusion
En ce qui concerne l'utilisation des usines ou des services, ils sont très similaires. Ils sont injectés dans des contrôleurs, des directives, des blocs d'exécution, etc., et utilisés dans le code client de la même manière. Ce sont également des singletons, ce qui signifie que la même instance est partagée entre tous les endroits où le service/l’usine est injecté.
Alors, que devriez-vous préférer? L'un ou l'autre - ils sont si semblables que les différences sont triviales. Si vous choisissez l’une sur l’autre, sachez simplement comment elles sont construites pour pouvoir les implémenter correctement.
J'ai passé du temps à essayer de comprendre la différence.
Et je pense que la fonction usine utilise le modèle de module et que la fonction de service utilise le modèle de constructeur de script standard Java.
Le modèle d'usine est plus flexible car il peut renvoyer des fonctions et des valeurs ainsi que des objets.
Le modèle de service IMHO n'a pas beaucoup de sens, car vous pouvez tout aussi facilement le faire avec une usine. Les exceptions pourraient être:
On peut soutenir que le modèle de service est un moyen légèrement plus agréable de créer un nouvel objet du point de vue de la syntaxe, mais il est également plus coûteux d’instancier. D'autres ont indiqué que angular utilise "new" pour créer le service, mais ce n'est pas tout à fait vrai - cela ne peut pas être fait car chaque constructeur de service a un nombre différent de paramètres. En réalité, angular utilise le modèle d'usine en interne pour envelopper votre fonction constructeur. Ensuite, il fait un jeu de jiggery intelligent pour simuler le "nouvel" opérateur de javascript, invoquant votre constructeur avec un nombre variable d'arguments injectables - mais vous pouvez laisser cette étape de côté si vous utilisez simplement le motif d'usine directement. augmentant très légèrement l'efficacité de votre code.