angular.service vs angular.factory

1065

J'ai vu angular.factory () et angular.service () utilisés pour déclarer des services; cependant, je ne trouve angular.service nulle part dans la documentation officielle.

Quelle est la différence entre les deux méthodes?
Qui devrait être utilisé pour quoi (en supposant qu'ils font des choses différentes)?

Jacob
la source
25
doublon possible de confusion sur le service par rapport à l'usine
Mark Rajcok
4
J'ai recherché "[angularjs] service factory", mais je me suis aussi souvenu qu'il y avait déjà une question à ce sujet (parce que j'ai pensé à écrire cette / cette question moi-même à un moment donné).
Mark Rajcok du
2
Dans une recherche, les crochets signifient-ils une balise?
jacob
11
@Jacob Square Brackets restreint votre recherche. [angularjs] directives - recherchera des «directives» pour les questions déjà étiquetées avec angularjs.
Mahbub
1
@Mahbub En d'autres termes, "oui" :)
Brian

Réponses:

1268
  angular.service('myService', myServiceFunction);
  angular.factory('myFactory', myFactoryFunction);

J'ai eu du mal à m'enrouler autour de ce concept jusqu'à ce que je le mette de cette façon:

Service : la fonction que vous écrivez sera nouvelle :

  myInjectedService  <----  new myServiceFunction()

Factory : la fonction (constructeur) que vous écrivez sera invoquée :

  myInjectedFactory  <---  myFactoryFunction()

Ce que vous en faites dépend de vous, mais il existe des modèles utiles ...

Telles que l'écriture d'une fonction de service pour exposer une API publique:

function myServiceFunction() {
  this.awesomeApi = function(optional) {
    // calculate some stuff
    return awesomeListOfValues;
  }
}
---------------------------------------------------------------------------------
// Injected in your controller
$scope.awesome = myInjectedService.awesomeApi();

Ou en utilisant une fonction d' usine pour exposer une API publique:

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();

Ou en utilisant une fonction d' usine pour renvoyer un constructeur:

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();

Lequel utiliser? ...

Vous pouvez accomplir la même chose avec les deux. Cependant, dans certains cas, l' usine vous donne un peu plus de flexibilité pour créer un injectable avec une syntaxe plus simple. En effet, alors que myInjectedService doit toujours être un objet, myInjectedFactory peut être un objet, une référence de fonction ou n'importe quelle 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é comme suit:

var myShinyNewObject = new myInjectedService.myFunction()

ce qui est sans doute moins souhaitable que cela:

var myShinyNewObject = new myInjectedFactory();

(Mais vous devez vous méfier de l'utilisation de ce type de modèle en premier lieu, car les nouveaux objets dans vos contrôleurs créent des dépendances difficiles à suivre qui sont difficiles à simuler pour les tests. Mieux vaut qu'un service gère une collection d'objets pour que d'utiliser new()wily-nilly.)


Encore une chose, ce sont tous des singletons ...

Gardez également à l'esprit que dans les deux cas, angular vous aide à gérer un singleton. Peu importe où ou combien de fois vous injectez votre service ou fonction, vous obtiendrez la même référence au même objet ou fonction. (À l'exception d'une usine qui renvoie simplement une valeur comme un nombre ou une chaîne. Dans ce cas, vous obtiendrez toujours la même valeur, mais pas une référence.)

Gil Birman
la source
2
Serait-il préférable de l'appeler un constructeur d'objets que Newable?
marksyzm
2
@Hugo, je montrais que vous pouvez effectivement accomplir la même chose avec les deux, c'est juste que la syntaxe sera différente.
Gil Birman
105
Je ne sais pas combien de fois je devrai lire la différence entre le service et l'usine avant d'être convaincu que les deux sont nécessaires
DMac the Destroyer
10
Nous avons déjà un verbe pour dire "à nouveau", c'est "instancier". Juste pour référence. :)
sscarduzio
7
Les usines sont des fonctions qui sont appelées, donc elles peuvent renvoyer n'importe quoi. En revanche, les services sont instanciés par via angulaire new fn(), ils doivent donc renvoyer une instance.
Gil Birman
318

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'));

Kirk Strobeck
la source
169
Mec, merci. Non pas que les détails des autres réponses ne soient pas valides, mais parfois vous avez besoin de la version 10 sec.
R Claven
4
La fonction de service ne renvoie rien. Le this.name = ... suffit pour montrer qu'il expose une API.
pixelbits
3
Cependant, si vous retournez et objectez, il l'utilisera à la place de cela. jsfiddle.net/Ne5P8/1221
MrB
@MrB, c'est une fonctionnalité JavaScript normale, non spécifique au contexte angulaire ou à cette question.
Om Shankar
@Om Shankar, La réponse ci-dessus montre que la différence est l'utilisation de cet objet par rapport à un objet retourné. Je montrais que "CECI" est la valeur par défaut qui sera utilisée avec un service, mais si vous retournez une valeur, elle agira presque exactement comme une usine. Cependant, d'un autre côté, une usine semble exiger une valeur renvoyée, sinon une erreur se produira - (illustré dans cet exemple - jsfiddle.net/hmoc0q3v/1 ).
MrB
247

Voici les principales différences:

Prestations de service

Syntaxe: module.service( 'serviceName', function );

Résultat: lorsque vous déclarez serviceName comme argument injectable, vous recevrez l' instance d'une fonction passée à module.service.

Utilisation: pourrait ê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.

Des usines

Syntaxe: module.factory( 'factoryName', function );

Résultat: lorsque vous déclarez factoryName en tant qu'argument injectable, vous recevrez la valeur renvoyée en appelant la référence de fonction passée à module.factory.

Utilisation: pourrait être utile pour renvoyer une fonction «classe» qui peut ensuite être renouvelée pour créer des instances.

Voici un exemple d'utilisation de services et d'usine . En savoir plus sur AngularJS Service vs Factory .

Vous pouvez également consulter la documentation AngularJS et une question similaire sur stackoverflow confus à propos du service par rapport à l'usine .

Manish Chhabra
la source
27
Je ne suis pas d'accord avec votre exemple d'utilisation d'une usine. Les services et les usines (en supposant qu'une fonction est renvoyée. Il peut s'agir simplement d'une valeur ou d'un objet) peuvent être renouvelés. En fait, un service est la seule option qui est garantie d'être nouveau car vous disposez d'une instance de fonction. Je dirais que l'avantage d'utiliser une USINE sur un SERVICE est qu'il permet un certain contrôle sur l'accès aux propriétés - privées et publiques en soi alors que toutes les propriétés du service sont par nature exposées. Et je pense à un fournisseur comme à l'usine d'une usine - seulement il est injectable et configurable au moment de la configuration.
Drew R
1
@DrewR Merci pour votre commentaire, j'ai trouvé un bon exemple de méthodes publiques et privées utilisant une usine: stackoverflow.com/a/14904891/65025
edzillion
Je dois être d'accord avec @DrewR sur celui-ci, en fait. J'ai déjà utilisé des usines pour retourner des objets, mais honnêtement, à ce stade, cela pourrait valoir la peine d'être utilisé $providerstout le temps.
jedd.ahyoung
le service instancie automatiquement le constructeur, non?
Martian2049
1
@DrewR - D'après ce que je comprends, il est vrai que vous pouvez obtenir le même effet nouveau du service comme vous le pouvez avec une usine, mais ce n'est pas ce à quoi il est destiné. Son objectif principal est lorsque vous voulez simplement renvoyer un objet utilitaire et pour cela, il fournit une syntaxe plus appropriée - vous pouvez simplement écrire this.myFunc = function(){}dans votre service (vous évite d'écrire du code pour créer l'objet comme vous le feriez avec une usine ).
BornToCode
137

TL; DR

1) Lorsque vous utilisez une usine, vous créez un objet, lui ajoutez des propriétés, puis retournez ce même objet. Lorsque vous transmettez cette usine à votre contrôleur, ces propriétés sur l'objet seront désormais disponibles dans ce contrôleur via votre usine.

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 le service , Angular l'instancie en arrière-plan avec le «nouveau» mot clé. Pour cette raison, vous ajouterez des propriétés à «ceci» et le service renverra «ceci». Lorsque vous transmettez le service à votre contrôleur, ces propriétés sur «ceci» 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) Les
usines d' usine sont le moyen le plus utilisé pour créer et configurer un service. Il n'y a vraiment pas beaucoup plus que ce que le TL; DR a dit. Il vous suffit de créer un objet, d'y ajouter des propriétés, puis de renvoyer ce même objet. Ensuite, lorsque vous passez l'usine dans votre contrôleur, ces propriétés sur l'objet seront désormais disponibles dans ce contrôleur via votre usine. Un exemple plus détaillé est présenté ci-dessous.

app.factory('myFactory', function(){
  var service = {};
  return service;
});

Maintenant, quelles que soient les propriétés que nous attachons au «service», nous serons disponibles lorsque nous passerons «myFactory» dans 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 finirons par mettre en place des méthodes getter / setter sur 'service' pour pouvoir modifier ces variables 'privées' si nécessaire.

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 au «service». Nous les créons simplement pour les utiliser ou les modifier ultérieurement.

  • baseUrl est l'URL de base requise par l'API iTunes
  • _artist est l'artiste que nous souhaitons rechercher
  • _finalUrl est l'URL finale et entièrement construite vers laquelle nous ferons l'appel à iTunes makeUrl est une fonction qui créera et renverra notre URL conviviale iTunes.

Maintenant que nos variables et fonctions auxiliaires / privées sont en place, ajoutons quelques propriétés à l'objet «service». Quoi que nous mettions sur «service», nous serons en mesure d'utiliser directement le contrôleur dans lequel nous passerons «myFactory».

Nous allons créer des 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 retourner une promesse qui se réalisera 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 fortement de les approfondir.

Ci-dessous, setArtist accepte un artiste et vous permet de définir l'artiste. getArtist renvoie l'artiste callItunes appelle d'abord makeUrl () afin de construire l'URL que nous utiliserons avec notre requête $ http. Ensuite, il configure un objet de promesse, fait une demande $ http avec notre URL finale, puis parce que $ http renvoie une promesse, nous sommes en mesure d'appeler .success ou .error après notre demande. Nous résolvons ensuite notre promesse avec les données iTunes, ou nous la rejetons avec un message disant «Il y a eu 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 pouvons désormais injecter «myFactory» dans n'importe quel contrôleur et nous pourrons ensuite appeler nos méthodes que nous avons attachées à notre objet de 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 des propriétés sur notre objet $ scope qui proviennent de données de «myFactory». Le seul code délicat ci-dessus est si vous n'avez jamais traité de promesses auparavant. Parce que callItunes renvoie une promesse, nous sommes en mesure d'utiliser la méthode .then () et de définir $ scope.data.artistData uniquement 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
La chose la plus importante à savoir lors de la création d'un service est peut-être qu'il est instancié avec le «nouveau» mot clé. Pour vous, gourous de JavaScript, cela devrait vous donner une grande idée de la nature du code. Pour ceux d'entre vous qui ont peu d'expérience en JavaScript ou pour ceux qui ne connaissent pas trop ce que fait le `` 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 se produisent lorsque vous appelez une fonction avec le mot-clé `` nouveau '', créons une fonction et appelez-la avec le mot clé `` nouveau '', puis montrons ce que fait l'interpréteur lorsqu'il voit le mot clé `` nouveau ''. Les résultats finaux seront les mêmes.

Créons d'abord notre constructeur.

var Person = function(name, age){
  this.name = name;
  this.age = age;
}

Il s'agit d'une fonction constructeur JavaScript typique. Désormais, chaque fois que nous invoquerons la fonction Person à l'aide du mot-clé 'new', 'this' sera lié à l'objet nouvellement créé.

Ajoutons maintenant une méthode sur le prototype de notre Person afin qu'elle soit disponible sur chaque instance de notre 'classe' Person.

Person.prototype.sayName = function(){
  alert('My name is ' + this.name);
}

Maintenant, comme nous avons placé 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 fait une instance de Person puis appelons la fonction sayName.

var tyler = new Person('Tyler', 23);
tyler.sayName(); //alerts 'My name is Tyler'

Donc, tous ensemble, le code pour créer un constructeur Person, ajouter une fonction à son prototype, créer une instance Person, puis 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 «nouveau» mot clé 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 le cas. Donc, tout d'abord, nous savons que notre constructeur Person retourne un objet, que nous puissions le voir dans le code ou non. Deuxièmement, nous savons que notre fonction sayName étant située sur le prototype et non directement sur l'instance Person, l'objet renvoyé par la fonction Person doit être délégué à son prototype en cas d'échec des recherches. En termes plus simples, lorsque nous appelons tyler.sayName (), l'interpréteur dit «OK, je vais regarder l'objet 'tyler' que nous venons de créer, localiser la fonction sayName, puis l'appeler. Attendez une minute, je ne le vois pas ici - tout ce que je vois c'est le nom et l'âge, permettez-moi de vérifier le prototype. Ouais, on dirait que c'est sur le prototype, permettez-moi de l'appeler. ”.

Vous trouverez ci-dessous un code permettant de savoir ce que le «nouveau» mot clé fait réellement en JavaScript. Il s'agit essentiellement d'un exemple de code du paragraphe ci-dessus. J'ai mis la «vue 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;
}

Ayant maintenant cette connaissance de ce que le «nouveau» mot clé fait vraiment en JavaScript, la création d'un service en angulaire 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 «nouveau» mot clé. En combinant ces connaissances avec nos exemples ci-dessus, vous devez maintenant reconnaître que vous attacherez vos propriétés et méthodes directement à «ceci» qui sera ensuite renvoyé par le service lui-même. Jetons un coup d'oeil à 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 puis de le renvoyer car, comme mentionné plusieurs fois auparavant, nous avons utilisé le mot-clé 'new' pour que l'interpréteur crée cet objet, le déléguer à c'est un prototype, puis retournez-le sans que nous ayons à faire le travail.

Tout d'abord, créons notre fonction «privée» et d'assistance. Cela devrait sembler très familier puisque nous avons fait exactement la même chose avec notre usine. Je ne vais pas expliquer ce que fait chaque ligne ici parce que 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 nos méthodes qui seront disponibles dans notre contrôleur à «ceci».

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;
  }

});

Maintenant, tout comme dans notre usine, setArtist, getArtist et callItunes seront disponibles dans le contrôleur dans lequel nous transmettons myService. Voici le contrôleur myService (qui est presque exactement le même que 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 mentionné précédemment, une fois que vous comprenez vraiment ce que le «nouveau» fait, les services sont presque identiques aux usines d'Angular.

Tyler McGinnis
la source
12
Vous voudrez peut-être fournir un lien directement vers votre blog. tylermcginnis.com/angularjs-factory-vs-service-vs-provider J'ai trouvé cela un peu plus facile à lire.
Tyler Collier
3
Rien de mal à répéter votre blog ici, mais je conviens que c'est un article de blog greta.
R Claven
5
Bonne explication détaillée de ce que chacun fait sous le capot, mais toujours pas clair pourquoi et quand quelqu'un choisirait-il d'utiliser un service plutôt qu'une usine. En d'autres termes, quand préfèrerai-je avoir un nouvel objet par rapport à celui retourné par une usine. Je pense que c'est la plus grande confusion.
demisx
2
Fondamentalement, si vous souhaitez créer une connexion persistante à un service distant, comme l'API iTunes mentionnée dans l'exemple avec une connexion constante (état de connexion, historique des appels, stockage de données), vous pouvez opter pour Factory. Si vous l'implémentez en tant que service, chaque fois que vous voulez quelque chose de l'API, vous devez recréer la connexion et ne pouvez pas vraiment y stocker quoi que ce soit. Parce que chaque fois que vous recréez le service, vous obtiendrez un objet vide / par défaut.
Meki
4
Je ne pense pas que ce soit correct, @Aznim. Comme d'autres l'ont dit, les deux offrent des singletons.
Cryptovirus
35

L'indice est dans le nom

Les services et les usines sont similaires. Les deux produiront 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.

Les services sont destinés à implémenter un modèle de service

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 API ou un ensemble de logique métier.

Ceci est particulièrement important dans Angular car les modèles Angular ne sont généralement que des objets JSON extraits d'un serveur, et nous avons donc besoin d'un endroit pour mettre notre logique métier.

Voici un service Github par exemple. Il sait 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 mettent en œuvre un modèle d'usine

Les usines, d'autre part, 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 pouvons l'utiliser pour créer des modèles. Voici une fabrique qui renvoie 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 renvoient également des singletons.

Les usines peuvent renvoyer un constructeur

Parce qu'une usine retourne simplement un objet, elle peut retourner n'importe quel type d'objet que vous aimez, y compris une fonction constructeur, comme nous le voyons ci-dessus.

Les usines renvoient un objet; les services sont renouvelables

Une autre différence technique réside dans la composition des services et des usines. Une fonction de service sera nouvellement créée pour générer l'objet. Une fonction d'usine sera appelée et retournera l'objet.

  • Les services sont de nouveaux constructeurs.
  • Les usines sont simplement appelées et renvoient un objet.

Cela signifie que dans un service, nous ajoutons à "ceci" qui, dans le contexte d'un constructeur, pointera vers 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!";
      }
    }
  });
superluminaire
la source
2
grande explication, merci! il existe également un type dans le code exemple Factories où le Authorparamètre d'injecteur doit être Person.
mikhail-t
Merci @ mik-T, j'ai corrigé les fautes de frappe.
superluminaire
1
Votre utilisation du modèle de service est incorrecte - cela devrait être une usine. Si vous appelez .factory () au lieu de .service (), vous verrez que cela fonctionne exactement de la même manière. Le modèle de service est censé être fourni avec une fonction constructeur, et non une fonction qui renvoie un nouvel objet. Angular (effectivement) appelle "new" sur votre fonction constructeur. La seule raison pour laquelle votre service fonctionne est que si vous appelez "new" sur une fonction constructeur qui retourne un objet, vous récupérez en fait l'objet retourné plutôt que l'objet construit. Et les usines peuvent être utilisées pour créer tout ce que vous voulez, pas seulement des modèles.
Dan King
27

Toutes les réponses ici semblent concerner le service et l'usine, et c'est valable puisque c'est ce qui était demandé. Mais il est également important de garder à l' esprit qu'il ya plusieurs autres , y compris provider(), value()et constant().

La clé à retenir est que chacun est un cas particulier de l'autre. Chaque cas spécial dans 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 venez de voir lequel vous permet de faire ce que vous voulez en moins de code. Voici une image illustrant à quel point ils sont similaires:

entrez la description de l'image ici

Pour une ventilation complète étape par étape et une référence rapide de l'utilisation de chacun, vous pouvez visiter le blog d'où j'ai obtenu cette image:

http://www.simplygoodcode.com/2015/11/the-difference-between-service-provider-and-factory-in-angularjs/

Luis Perez
la source
3
@jacob peut-être ainsi, mais je pense que le concept global de non seulement quand les utiliser, mais qu'ils sont tous essentiellement des variations de la même chose est important.
Luis Perez
1
@LuisPerez Le lien vers votre blog et la vidéo expliquant la différence c'est vraiment super. Il est plus facile de comprendre avec ces exemples de la vidéo :)
Alin Ciocan
24

app.factory ('fn', fn) vs app.service ('fn', fn)

Construction

Avec les usines, Angular invoquera 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 invoquera la fonction constructeur en appelant new . La fonction construite est mise en cache et injectée.

  //service
  var obj = new fn();
  return obj;

la mise en oeuvre

Les usines renvoient généralement un littéral objet parce que 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 référencer «this» car elles ont été construites à l'aide de «new».

app.service('fn', function () {
         var foo = 0;
         var bar = 0;
         this.setFoo = function (val) {
               foo = val;
         }
         this.setBar = function (val){
               bar = val;
         }
});

Conclusion

Quand il s'agit d'utiliser des usines ou des services, ils sont tous deux très similaires. Ils sont injectés dans des contrôleurs, des directives, un bloc d'exécution, etc., et utilisés dans le code client à peu près de la même manière. Ils sont également tous deux 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 tellement similaires que les différences sont insignifiantes. Si vous choisissez l'un plutôt que l'autre, sachez simplement comment ils sont construits, afin de pouvoir les implémenter correctement.

pixelbits
la source
Les fonctions de service ne "renvoient rien", elles renvoient implicitement l'objet construit SI vous ne spécifiez pas votre propre instruction de retour (dans ce dernier cas, l'objet que vous avez renvoyé est ce qui sera créé et mis en cache, comme une fabrique).
Cryptovirus
Je pense que vous l'avez mal interprété ... Quand je dis retour, je veux dire du point de vue de la mise en œuvre de la fonction de service
pixelbits
êtes-vous sûr que l'usine est également une seule ville?
Martian2049
5

J'ai passé quelque temps à essayer de comprendre la différence.

Et je pense que la fonction d'usine utilise le modèle de module et la fonction de service utilise le modèle de constructeur de script Java standard.

ps.
la source
2

Le modèle d'usine est plus flexible car il peut renvoyer des fonctions et des valeurs ainsi que des objets.

Il n'y a pas beaucoup d'intérêt dans le modèle de service à mon humble avis, car tout ce qu'il fait, vous pouvez le faire aussi facilement avec une usine. Les exceptions peuvent être:

  • Si vous vous souciez du type déclaré de votre service instancié pour une raison quelconque - si vous utilisez le modèle de service, votre constructeur sera le type du nouveau service.
  • Si vous avez déjà une fonction constructeur que vous utilisez ailleurs que vous souhaitez également utiliser en tant que service (bien que probablement pas très utile si vous souhaitez y injecter quoi que ce soit!).

On peut dire que le modèle de service est un moyen légèrement plus agréable de créer un nouvel objet d'un point de vue syntaxique, mais il est également plus coûteux à instancier. D'autres ont indiqué que angular utilise "new" pour créer le service, mais ce n'est pas tout à fait vrai - il n'est pas en mesure de le faire car chaque constructeur de service a un nombre différent de paramètres. Ce que fait réellement angulaire est d'utiliser le modèle d'usine en interne pour envelopper votre fonction constructeur. Ensuite, il fait un peu 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 si vous utilisez directement le modèle d'usine, augmentant ainsi très légèrement l'efficacité de votre code.

Dan King
la source
Les services sont plus efficaces à construire que les usines, car les usines utilisent des fermetures relativement coûteuses et les services (classes) peuvent tirer parti du prototype.
jacob
@jacob Vous ne savez pas ce que vous voulez dire sur les fermetures? L'usine n'est qu'une fonction qui renvoie un objet. Vous ne devez utiliser une fermeture que si votre objet retourné nécessite un état "privé". Vous devrez toujours faire la même chose si vous utilisez un constructeur (service). Je prends note de votre point de vue sur le prototype - bien que vous puissiez toujours le faire dans une usine si vous le souhaitez.
Dan King
function MyFactory(dep1) { var $$foo = 'bar', factory = {}; Object.defineProperties(factory.prototype, { foo: { value: $$foo } }); return factory; } function MyService(dep1) { var $$foo = 'bar'; Object.defineProperties(MyService.prototype, { foo: { value: $$foo } }); } Alors que MyFactory et MyService utilisent un prototype, MyFactory prend toujours un coup de performance d'avoir à construire l'objet qui est retourné. Dans les deux exemples, ils ont des utilisateurs privés, mais dans MyService, il n'y a relativement aucune différence de performances.
jacob
1
Pour moi, la différence est de savoir si je veux utiliser l'usine directement sans méthode: MyFactory(someArgument)(ex $http()). Ce n'est pas possible avec un service que vous seriez référencez le constructeur: MyService(someArgument).
jacob
Sur le temps de construction des objets, je ne vois pas vraiment comment factory = {} est un impact sur les performances, plus que javascript initialisant "this" pour vous quand il appelle votre constructeur? Et je pense que le plus gros coup de performance est du côté angulaire quand il enveloppe votre constructeur dans une usine et doit ensuite sauter à travers des cerceaux afin de simuler "nouveau" afin qu'il puisse injecter vos dépendances.
Dan King