AngularJS: service vs fournisseur vs usine

3321

Quelles sont les différences entre un Service, Provideret Factoryen AngularJS?

Lior
la source
244
J'ai trouvé que tous les termes angulaires intimidaient les débutants. Nous avons commencé avec ce cheatsheet qui était un peu plus facile à comprendre pour nos programmeurs lors de l'apprentissage d'Angular demisx.github.io/angularjs/2014/09/14/… . J'espère que cela aidera aussi votre équipe.
demisx
7
À mon avis, la meilleure façon de comprendre la différence est d'utiliser la propre documentation d'Angular: docs.angularjs.org/guide/providers, elle est extrêmement bien expliquée et utilise un exemple particulier pour vous aider à la comprendre.
Rafael Merlin
3
@Blaise Merci! Par mon commentaire dans le post, je l'ai laissé intentionnellement, car 99% des cas d'utilisation de mon expérience peuvent être traités avec succès via service.factory. Je ne voulais pas compliquer davantage ce sujet.
demisx
3
Je trouve cette discussion également très utile stackoverflow.com/questions/18939709/…
Anand Gupta
3
Voici quelques bonnes réponses surfaçon dontservices,factoriesetprovidersœuvres.
Mistalis

Réponses:

2866

De la liste de diffusion AngularJS, j'ai obtenu un fil étonnant qui explique le service vs l'usine vs le fournisseur et leur utilisation d'injection. Compilation des réponses:

Prestations de service

Syntaxe: module.service( 'serviceName', function );
Résultat: Lorsque vous déclarez serviceName comme argument injectable, vous recevrez une instance de la fonction. En d'autres termes new FunctionYouPassedToService() .

Des usines

Syntaxe: module.factory( 'factoryName', function );
Résultat: Lorsque vous déclarez factoryName en tant qu'argument injectable, la valeur renvoyée vous sera fournie en invoquant la référence de fonction passée à module.factory .

Fournisseurs

Syntaxe: module.provider( 'providerName', function );
Résultat: Lorsque vous déclarez providerName comme argument injectable, vous recevrez (new ProviderFunction()).$get() . La fonction constructeur est instanciée avant que la méthode $ get soit appelée - ProviderFunctionest la référence de fonction passée à module.provider.

Les fournisseurs ont l'avantage de pouvoir être configurés pendant la phase de configuration du module.

Voir ici pour le code fourni.

Voici une excellente explication supplémentaire de Misko:

provide.value('a', 123);

function Controller(a) {
  expect(a).toEqual(123);
}

Dans ce cas, l'injecteur renvoie simplement la valeur telle quelle. Mais que faire si vous voulez calculer la valeur? Utilisez ensuite une usine

provide.factory('b', function(a) {
  return a*2;
});

function Controller(b) {
  expect(b).toEqual(246);
}

Donc factory est de même d'une fonction qui est responsable de la création de valeur. Notez que la fonction d'usine peut demander d'autres dépendances.

Mais que se passe-t-il si vous voulez être plus OO et avoir une classe appelée Greeter?

function Greeter(a) {
  this.greet = function() {
    return 'Hello ' + a;
  }
}

Ensuite, pour instancier, il faudrait écrire

provide.factory('greeter', function(a) {
  return new Greeter(a);
});

Ensuite, nous pourrions demander «greeter» dans le contrôleur comme celui-ci

function Controller(greeter) {
  expect(greeter instanceof Greeter).toBe(true);
  expect(greeter.greet()).toEqual('Hello 123');
}

Mais c'est beaucoup trop verbeux. Une façon plus courte d'écrire ceci seraitprovider.service('greeter', Greeter);

Mais que faire si nous voulions configurer la Greeterclasse avant l'injection? Ensuite, nous pourrions écrire

provide.provider('greeter2', function() {
  var salutation = 'Hello';
  this.setSalutation = function(s) {
    salutation = s;
  }

  function Greeter(a) {
    this.greet = function() {
      return salutation + ' ' + a;
    }
  }

  this.$get = function(a) {
    return new Greeter(a);
  };
});

Ensuite, nous pouvons le faire:

angular.module('abc', []).config(function(greeter2Provider) {
  greeter2Provider.setSalutation('Halo');
});

function Controller(greeter2) {
  expect(greeter2.greet()).toEqual('Halo 123');
}

Comme une note de côté, service, factoryet valuesont tous dérivés du fournisseur.

provider.service = function(name, Class) {
  provider.provide(name, function() {
    this.$get = function($injector) {
      return $injector.instantiate(Class);
    };
  });
}

provider.factory = function(name, factory) {
  provider.provide(name, function() {
    this.$get = function($injector) {
      return $injector.invoke(factory);
    };
  });
}

provider.value = function(name, value) {
  provider.factory(name, function() {
    return value;
  });
};
Lior
la source
58
Voir également stackoverflow.com/a/13763886/215945 qui traite des différences entre le service et l'usine.
Mark Rajcok
3
Dans l'édition 611, j'ai ajouté l'utilisation de constantes et de valeurs angulaires. Démontrer les différences des autres déjà montrées. jsbin.com/ohamub/611/edit
Nick
17
Bien qu'un service soit appelé en créant une instance de la fonction. Il n'est en fait créé qu'une seule fois par injecteur, ce qui en fait un singleton. docs.angularjs.org/guide/dev_guide.services.creating_services
angelokh
33
Cet exemple pourrait être incroyable s'il utilisait un exemple pratique clair. Je me perds en essayant de comprendre à quoi servent les choses toEqualet ce qu'elles greeter.Greetsont. Pourquoi ne pas utiliser quelque chose d'un peu plus réel et relatable?
Kyle Pennell
5
L'utilisation de la fonction expect () est un mauvais choix pour expliquer quelque chose. Utilisez du code réel la prochaine fois.
Craig
812

JS Fiddle Demo

Exemple "Hello world" avec factory/ service/ provider:

var myApp = angular.module('myApp', []);

//service style, probably the simplest one
myApp.service('helloWorldFromService', function() {
    this.sayHello = function() {
        return "Hello, World!";
    };
});

//factory style, more involved but more sophisticated
myApp.factory('helloWorldFromFactory', function() {
    return {
        sayHello: function() {
            return "Hello, World!";
        }
    };
});
    
//provider style, full blown, configurable version     
myApp.provider('helloWorld', function() {

    this.name = 'Default';

    this.$get = function() {
        var name = this.name;
        return {
            sayHello: function() {
                return "Hello, " + name + "!";
            }
        }
    };

    this.setName = function(name) {
        this.name = name;
    };
});

//hey, we can configure a provider!            
myApp.config(function(helloWorldProvider){
    helloWorldProvider.setName('World');
});
        

function MyCtrl($scope, helloWorld, helloWorldFromFactory, helloWorldFromService) {
    
    $scope.hellos = [
        helloWorld.sayHello(),
        helloWorldFromFactory.sayHello(),
        helloWorldFromService.sayHello()];
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="myApp">
<div ng-controller="MyCtrl">
    {{hellos}}
</div>
</body>

EpokK
la source
2
Ne thischange pas de contexte dans la $getfonction? - vous ne faites plus référence au fournisseur instancié dans cette fonction.
Nate-Wilkins
12
@Nate: thisne change pas de contexte, en fait, car ce qui est appelé est new Provider(). $ Get (), où Providerest la fonction qui est passée app.provider. C'est-à-dire qu'on l' $get()appelle comme méthode sur le construit Provider, donc on thisse référera Providercomme le suggère l'exemple.
Brandon
1
@Brandon Ohh ok c'est plutôt bien alors. À première vue déroutant - merci pour la clarification!
Nate-Wilkins
3
Pourquoi est-ce que j'obtiens Unknown provider: helloWorldProvider <- helloWorlden exécutant ceci localement? Commentant, même erreur pour les 2 autres exemples. Existe-t-il une configuration de fournisseur cachée? (Angular 1.0.8) - Trouvé: stackoverflow.com/questions/12339272/…
Antoine
4
Est-ce la raison pour laquelle @Antoine obtient l'erreur "Unknown provide: helloWorldProvider" parce que dans votre code .config, vous utilisez 'helloWorldProvider', mais lorsque vous définissez le fournisseur dans myApp.provider ('helloWorld', function ()), vous utilisez 'Bonjour le monde'? En d'autres termes, dans votre code de configuration, comment angular sait-il que vous faites référence au fournisseur helloWorld? Merci
jmtoung
645

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 , AngularJS l'instancie dans les coulisses 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;
  }
});



3) Les fournisseurs sont le seul service que vous pouvez passer dans votre fonction .config (). Utilisez un fournisseur lorsque vous souhaitez fournir une configuration à l'échelle du module pour votre objet de service avant de le rendre disponible.

app.controller(‘myProvider’, function($scope, myProvider){
  $scope.artist = myProvider.getArtist();
  $scope.data.thingFromConfig = myProvider.thingOnConfig;
});

app.provider(‘myProvider’, function(){
 //Only the next two lines are available in the app.config()
 this._artist = ‘’;
 this.thingFromConfig = ‘’;
  this.$get = function(){
    var that = this;
    return {
      getArtist: function(){
        return that._artist;
      },
      thingOnConfig: that.thingFromConfig
    }
  }
});

app.config(function(myProviderProvider){
  myProviderProvider.thingFromConfig = This was set in config’;
});



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 appellerons 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». Tout ce que nous mettons sur «service» peut être directement utilisé à l'intérieur du contrôleur dans lequel nous passons «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 AngularJS, 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 les propriétés de notre objet $ scope avec les 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 «fin» (c'est une bonne pratique de codage). 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 certains 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 parce que notre fonction sayName est 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 below line creates an object(obj) 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 dans AngularJS 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 parce que, 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 pour nous 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'AngularJS.

3) Fournisseur

La principale chose à retenir à propos des fournisseurs est qu'ils sont le seul service que vous pouvez passer dans la partie app.config de votre application. Cela est extrêmement important si vous devez modifier une partie de votre objet de service avant qu'il ne soit disponible partout ailleurs dans votre application. Bien que très similaire aux services / usines, il y a quelques différences dont nous discuterons.

Nous avons d'abord configuré notre fournisseur de la même manière que nous l'avons fait avec notre service et notre usine. Les variables ci-dessous sont notre fonction «privée» et d'assistance.

app.provider('myProvider', function(){
   var baseUrl = 'https://itunes.apple.com/search?term=';
  var _artist = '';
  var _finalUrl = '';

  //Going to set this property on the config function below.
  this.thingFromConfig = ‘’;

  var makeUrl = function(){
    _artist = _artist.split(' ').join('+');
    _finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK'
    return _finalUrl;
  }
}

* Encore une fois, si une partie du code ci-dessus prête à confusion, consultez la section Usine où j'explique ce que tout cela fait plus de détails.

Vous pouvez considérer les fournisseurs comme ayant trois sections. La première section concerne les variables / fonctions «privées» qui seront modifiées / définies plus tard (voir ci-dessus). La deuxième section est les variables / fonctions qui seront disponibles dans votre fonction app.config et sont donc disponibles pour être modifiées avant d'être disponibles ailleurs (également indiqué ci-dessus). Il est important de noter que ces variables doivent être attachées au mot-clé «this». Dans notre exemple, seul 'thingFromConfig' sera disponible pour être modifié dans app.config. La troisième section (illustrée ci-dessous) comprend toutes les variables / fonctions qui seront disponibles dans votre contrôleur lorsque vous passerez le service «myProvider» à ce contrôleur spécifique.

Lors de la création d'un service avec Provider, les seules propriétés / méthodes qui seront disponibles dans votre contrôleur sont les propriétés / méthodes renvoyées par la fonction $ get (). Le code ci-dessous met $ get sur 'this' (qui, nous le savons, sera éventuellement renvoyé par cette fonction). Maintenant, cette fonction $ get renvoie toutes les méthodes / propriétés que nous voulons être disponibles dans le contrôleur. Voici un exemple de code.

this.$get = function($http, $q){
    return {
      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;
      },
      setArtist: function(artist){
        _artist = artist;
      },
      getArtist: function(){
        return _artist;
      },
      thingOnConfig: this.thingFromConfig
    }
  }

Maintenant, le code complet du fournisseur ressemble à ceci

app.provider('myProvider', function(){
  var baseUrl = 'https://itunes.apple.com/search?term=';
  var _artist = '';
  var _finalUrl = '';

  //Going to set this property on the config function below
  this.thingFromConfig = '';

  var makeUrl = function(){
    _artist = _artist.split(' ').join('+');
    _finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK'
    return _finalUrl;
  }

  this.$get = function($http, $q){
    return {
      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;
      },
      setArtist: function(artist){
        _artist = artist;
      },
      getArtist: function(){
        return _artist;
      },
      thingOnConfig: this.thingFromConfig
    }
  }
});

Maintenant, tout comme dans notre usine et notre service, setArtist, getArtist et callItunes seront disponibles dans le contrôleur dans lequel nous transmettons myProvider. Voici le contrôleur myProvider (qui est presque exactement le même que notre contrôleur d'usine / service).

app.controller('myProviderCtrl', function($scope, myProvider){
  $scope.data = {};
  $scope.updateArtist = function(){
    myProvider.setArtist($scope.data.artist);
  };

  $scope.submitArtist = function(){
    myProvider.callItunes()
      .then(function(data){
        $scope.data.artistData = data;
      }, function(data){
        alert(data);
      })
  }

  $scope.data.thingFromConfig = myProvider.thingOnConfig;
});

Comme mentionné précédemment, l'intérêt de créer un service avec Provider est de pouvoir modifier certaines variables via la fonction app.config avant que l'objet final ne soit transmis au reste de l'application. Voyons un exemple de cela.

app.config(function(myProviderProvider){
  //Providers are the only service you can pass into app.config
  myProviderProvider.thingFromConfig = 'This sentence was set in app.config. Providers are the only service that can be passed into config. Check out the code to see how it works';
});

Maintenant, vous pouvez voir comment 'thingFromConfig' est une chaîne vide dans notre fournisseur, mais quand cela apparaîtra dans le DOM, ce sera 'Cette phrase a été définie ...'.

Tyler McGinnis
la source
11
La seule partie qui manque dans cette excellente rédaction est les avantages relatifs de l'utilisation du service par rapport à une usine; ce qui est clairement expliqué dans la réponse acceptée par Lior
infinity
2
FWIW (peut-être pas beaucoup), voici un blogueur qui prend des problèmes avec Angular, et n'aime pas providerProvider codeofrob.com/entries/you-have-ruined-javascript.html
barlop
3
La punchline des «gourous JavaScript» était sournoise. : DI pense que cette réponse clarifie beaucoup les choses. Superbement écrit.
amarmishra
4
Votre TLDR a besoin d'un TLDR.
JensB
3
@JensB tl; dr - Learn React.
Tyler McGinnis
512

Tous les services sont des singletons ; ils sont instanciés une fois par application. Ils peuvent être de n'importe quel type , qu'il s'agisse d'une fonction primitive, d'un littéral d'objet, ou même d'une instance d'un type personnalisé.

Le value, factory, service, constant, et les providerméthodes sont tous les fournisseurs. Ils enseignent à l'injecteur comment instancier les services.

La recette la plus détaillée, mais aussi la plus complète, est une recette de fournisseur. Les quatre types de recettes restants - Valeur, Usine, Service et Constante - ne sont que du sucre syntaxique en plus d'une recette de fournisseur .

  • La recette de valeur est le cas le plus simple, où vous instanciez le service vous-même et fournissez le valeur instanciée à l'injecteur.
  • La recette d'usine donne à l'injecteur une fonction d'usine qu'il appelle lorsqu'il a besoin d'instancier le service. Lorsqu'elle est appelée, la fonction d'usine crée et renvoie l'instance de service. Les dépendances du Service sont injectées comme arguments des fonctions. Donc, l'utilisation de cette recette ajoute les capacités suivantes:
    • Capacité à utiliser d'autres services (avoir des dépendances)
    • Initialisation du service
    • Initialisation retardée / paresseuse
  • La recette de service est presque la même que la recette d'usine, mais ici, l'injecteur invoque un constructeur avec le nouvel opérateur au lieu d'une fonction d'usine.
  • La recette du fournisseur est généralement exagérée . Il ajoute une couche d'indirection supplémentaire en vous permettant de configurer la création de l'usine.

    Vous ne devez utiliser la recette du fournisseur que lorsque vous souhaitez exposer une API pour la configuration à l'échelle de l'application qui doit être effectuée avant le démarrage de l'application. Cela n'est généralement intéressant que pour les services réutilisables dont le comportement peut devoir varier légèrement d'une application à l'autre.

  • La recette Constant est exactement comme la recette Value, sauf qu'elle vous permet de définir les services disponibles dans la phase de configuration . Plus tôt que les services créés à l'aide de la recette Value. Contrairement aux valeurs, elles ne peuvent pas être décorées en utilisantdecorator .
Voir la documentation du fournisseur .

flup
la source
2
Le service et l'usine sont donc essentiellement les mêmes? L'utilisation de l'autre ne fournit rien d'autre qu'une syntaxe alternative?
Matt
2
@Matt, oui, le service est un moyen concis lorsque vous avez déjà votre propre fonction que vous souhaitez exposer en tant que service. Depuis docs: myApp.factory ('unicornLauncher', ["apiToken", function (apiToken) {return new UnicornLauncher (apiToken);}]); vs: myApp.service ('unicornLauncher', ["apiToken", UnicornLauncher]);
janek
5
@joshperry En tant que débutant, j'ai googlé la différence entre le service et l'usine pendant un certain temps. Je suis d'accord que c'est la meilleure réponse de tous les temps! Je comprendrais le service comme une classe de service (par exemple, une classe d'encodeur / décodeur), qui pourrait avoir des propriétés privées. Et l'usine fournit un ensemble de méthodes d'assistance sans état.
stanleyxu2005
3
Les exemples Yaa dans les autres réponses ci-dessus ne parviennent pas à expliquer très clairement la différence fondamentale entre les services et les fournisseurs n / b, ce qui est injecté au moment où ces recettes sont instanciées.
Ashish Singh
223

Comprendre l'usine, le service et le fournisseur AngularJS

Tous ces éléments sont utilisés pour partager des objets singleton réutilisables. Il permet de partager du code réutilisable sur votre application / divers composants / modules.

De Docs Service / Factory :

  • Instancié paresseusement - Angular instancie uniquement un service / usine lorsqu'un composant d'application en dépend.
  • Singletons - Chaque composant dépendant d'un service obtient une référence à l'instance unique générée par la fabrique de services.

Usine

Une fabrique est une fonction où vous pouvez manipuler / ajouter de la logique avant de créer un objet, puis l'objet nouvellement créé est renvoyé.

app.factory('MyFactory', function() {
    var serviceObj = {};
    //creating an object with methods/functions or variables
    serviceObj.myFunction = function() {
        //TO DO:
    };
    //return that object
    return serviceObj;
});

Usage

Il peut s'agir simplement d'une collection de fonctions comme une classe. Par conséquent, il peut être instancié dans différents contrôleurs lorsque vous l'injectez dans vos fonctions contrôleur / usine / directive. Il n'est instancié qu'une seule fois par application.

Un service

Tout en regardant les services, pensez au prototype de la baie. Un service est une fonction qui instancie un nouvel objet à l'aide du mot-clé 'new'. Vous pouvez ajouter des propriétés et des fonctions à un objet de service à l'aide du thismot clé. Contrairement à une fabrique, elle ne retourne rien (elle retourne un objet qui contient des méthodes / propriétés).

app.service('MyService', function() {
    //directly binding events to this context
    this.myServiceFunction = function() {
        //TO DO:
    };
});

Usage

Utilisez-le lorsque vous devez partager un seul objet dans l'application. Par exemple, les détails de l'utilisateur authentifié, les méthodes / données partageables, les fonctions utilitaires, etc.

Fournisseur

Un fournisseur est utilisé pour créer un objet de service configurable. Vous pouvez configurer le paramètre de service à partir de la fonction de configuration. Il renvoie une valeur en utilisant la $get()fonction. La $getfonction s'exécute sur la phase d'exécution en angulaire.

app.provider('configurableService', function() {
    var name = '';
    //this method can be be available at configuration time inside app.config.
    this.setName = function(newName) {
        name = newName;
    };
    this.$get = function() {
        var getName = function() {
             return name;
        };
        return {
            getName: getName //exposed object to where it gets injected.
        };
    };
});

Usage

Lorsque vous devez fournir une configuration par module pour votre objet de service avant de le rendre disponible, par exemple. supposons que vous souhaitiez définir l'URL de votre API en fonction de votre environnement dev, stageouprod

REMARQUE

Seul le fournisseur sera disponible en phase de configuration angulaire, tandis que le service et l'usine ne le sont pas.

J'espère que cela a clarifié votre compréhension de l' usine, du service et du fournisseur .

Pankaj Parkar
la source
1
Que ferais-je si je voulais avoir un service avec une interface particulière, mais avoir deux implémentations différentes, et injecter chacune dans un contrôleur mais lié à des états différents en utilisant ui-router? par exemple, faire des appels à distance dans un état, mais écrire dans le stockage local à la place dans un autre. Les fournisseurs de documents disent d'utiliser only when you want to expose an API for application-wide configuration that must be made before the application starts. This is usually interesting only for reusable services whose behavior might need to vary slightly between applications, donc cela ne semble pas possible, non?
qix
191

Pour moi, la révélation est venue quand j'ai réalisé qu'ils fonctionnent tous de la même manière: en exécutant quelque chose une fois , en stockant la valeur qu'ils obtiennent, puis en crachant cette même valeur stockée lorsqu'ils sont référencés via l' injection de dépendances .

Disons que nous avons:

app.factory('a', fn);
app.service('b', fn);
app.provider('c', fn);

La différence entre les trois est que:

  1. aLa valeur stockée provient de l'exécution fn .
  2. bLa valeur stockée vient de newingfn .
  3. cLa valeur stockée provient d'abord de l'obtention d'une instance par newing fn, puis de l'exécution d'une $getméthode de l'instance.

Ce qui signifie qu'il y a quelque chose comme un objet cache dans AngularJS, dont la valeur de chaque injection n'est affectée qu'une seule fois, lors de leur première injection, et où:

cache.a = fn()
cache.b = new fn()
cache.c = (new fn()).$get()

C'est pourquoi nous utilisons des thisservices et définissons un this.$getfournisseur.

Lucia
la source
2
J'aime aussi le plus cette réponse. Le point de chacun d'eux est de fournir l'accès à un objet chaque fois que nécessaire via DI. Normalement, vous vous débrouillez bien avec l' factoryart. La seule raison pour laquelle serviceil existe des langages comme CoffeeScript, TypeScript, ES6, etc., vous pouvez donc utiliser leur syntaxe de classe. Vous n'avez besoin de providers que si votre module est utilisé dans plusieurs applications avec des paramètres différents en utilisant app.config(). Si votre service est un simple singleton ou est capable de créer des instances de quelque chose ne dépend que de votre implémentation.
Andreas Linnert
137

Service vs fournisseur vs usine:

J'essaie de rester simple. Il s'agit du concept de base de JavaScript.

Tout d'abord, parlons des services dans AngularJS!

Qu'est-ce que le service: dans AngularJS, servicen'est rien d'autre qu'un objet JavaScript singleton qui peut stocker des méthodes ou des propriétés utiles. Cet objet singleton est créé par base ngApp (application angulaire) et il est partagé entre tous les contrôleurs de l'application actuelle. Lorsque Angularjs instancie un objet de service, il enregistre cet objet de service avec un nom de service unique. Ainsi, chaque fois que nous avons besoin d'une instance de service, Angular recherche dans le registre ce nom de service et renvoie la référence à l'objet de service. Tels que nous pouvons invoquer la méthode, accéder aux propriétés, etc. sur l'objet de service. Vous pouvez vous demander si vous pouvez également mettre des propriétés, des méthodes sur l'objet de portée des contrôleurs! Alors pourquoi avez-vous besoin d'un objet de service? Les réponses sont les suivantes: les services sont partagés entre plusieurs contrôleurs. Si vous placez certaines propriétés / méthodes dans un objet de portée d'un contrôleur, elles ne seront disponibles que pour la portée actuelle.

Donc, s'il y a trois portées de contrôleur, que ce soit controllerA, controllerB et controllerC, toutes partageront la même instance de service.

<div ng-controller='controllerA'>
    <!-- controllerA scope -->
</div>
<div ng-controller='controllerB'>
    <!-- controllerB scope -->
</div>
<div ng-controller='controllerC'>
    <!-- controllerC scope -->
</div>

Comment créer un service?

AngularJS propose différentes méthodes pour enregistrer un service. Ici, nous nous concentrerons sur trois méthodes: usine (..), service (..), fournisseur (..);

Utilisez ce lien pour la référence du code

Fonction d'usine:

Nous pouvons définir une fonction d'usine comme ci-dessous.

factory('serviceName',function fnFactory(){ return serviceInstance;})

AngularJS fournit la méthode 'factory (' serviceName ', fnFactory)' qui prend deux paramètres, serviceName et une fonction JavaScript. Angular crée une instance de service en appelant la fonction fnFactory () comme ci-dessous.

var serviceInstace = fnFactory();

La fonction passée peut définir un objet et renvoyer cet objet. AngularJS stocke simplement cette référence d'objet dans une variable qui est passée comme premier argument. Tout ce qui est renvoyé par fnFactory sera lié à serviceInstance. Au lieu de renvoyer un objet, nous pouvons également retourner une fonction, des valeurs, etc., tout ce que nous retournerons, sera disponible pour l'instance de service.

Exemple:

var app= angular.module('myApp', []);
//creating service using factory method
app.factory('factoryPattern',function(){
  var data={
    'firstName':'Tom',
    'lastName':' Cruise',
    greet: function(){
      console.log('hello!' + this.firstName + this.lastName);
    }
  };

  //Now all the properties and methods of data object will be available in our service object
  return data;
});

Fonction de service:

service('serviceName',function fnServiceConstructor(){})

C'est une autre façon, nous pouvons enregistrer un service. La seule différence est la façon dont AngularJS essaie d'instancier l'objet de service. Cette fois, angular utilise le mot-clé 'new' et appelle la fonction constructeur quelque chose comme ci-dessous.

var serviceInstance = new fnServiceConstructor();

Dans la fonction constructeur, nous pouvons utiliser le mot clé «this» pour ajouter des propriétés / méthodes à l'objet de service. exemple:

//Creating a service using the service method
var app= angular.module('myApp', []);
app.service('servicePattern',function(){
  this.firstName ='James';
  this.lastName =' Bond';
  this.greet = function(){
    console.log('My Name is '+ this.firstName + this.lastName);
  };
});

Fonction fournisseur:

La fonction Provider () est un autre moyen de créer des services. Intéressons-nous à créer un service qui affiche simplement un message de bienvenue à l'utilisateur. Mais nous voulons également fournir une fonctionnalité permettant à l'utilisateur de définir son propre message d'accueil. En termes techniques, nous voulons créer des services configurables. Comment peut-on le faire ? Il doit y avoir un moyen, afin que l'application puisse passer leurs messages d'accueil personnalisés et Angularjs le rendrait disponible pour la fonction usine / constructeur qui crée notre instance de services. Dans un tel cas, la fonction provider () fait le travail. en utilisant la fonction provider (), nous pouvons créer des services configurables.

Nous pouvons créer des services configurables en utilisant la syntaxe du fournisseur comme indiqué ci-dessous.

/*step1:define a service */
app.provider('service',function serviceProviderConstructor(){});

/*step2:configure the service */
app.config(function configureService(serviceProvider){});

Comment fonctionne la syntaxe du fournisseur en interne?

1.L'objet fournisseur est créé à l'aide de la fonction constructeur que nous avons définie dans notre fonction fournisseur.

var serviceProvider = new serviceProviderConstructor();

2.La fonction que nous avons passée dans app.config (), est exécutée. C'est ce qu'on appelle la phase de configuration, et ici nous avons la possibilité de personnaliser notre service.

configureService(serviceProvider);

3.Enfin, l'instance de service est créée en appelant la méthode $ get de serviceProvider.

serviceInstance = serviceProvider.$get()

Exemple de code pour créer un service à l'aide de la syntaxe provide:

var app= angular.module('myApp', []);
app.provider('providerPattern',function providerConstructor(){
  //this function works as constructor function for provider
  this.firstName = 'Arnold ';
  this.lastName = ' Schwarzenegger' ;
  this.greetMessage = ' Welcome, This is default Greeting Message' ;
  //adding some method which we can call in app.config() function
  this.setGreetMsg = function(msg){
    if(msg){
      this.greetMessage =  msg ;
    }
  };

  //We can also add a method which can change firstName and lastName
  this.$get = function(){
    var firstName = this.firstName;
    var lastName = this.lastName ;
    var greetMessage = this.greetMessage;
    var data={
       greet: function(){
         console.log('hello, ' + firstName + lastName+'! '+ greetMessage);
       }
    };
    return data ;
  };
});

app.config(
  function(providerPatternProvider){
    providerPatternProvider.setGreetMsg(' How do you do ?');
  }
);

Démo de travail

Sommaire:


Factory utilise une fonction d'usine qui renvoie une instance de service. serviceInstance = fnFactory ();

Le service utilise une fonction constructeur et Angular invoque cette fonction constructeur en utilisant le mot clé 'new' pour créer l'instance de service. serviceInstance = new fnServiceConstructor ();

Le fournisseur définit une fonction providerConstructor, cette fonction providerConstructor définit une fonction d'usine $ get . Angular appelle $ get () pour créer l'objet de service. La syntaxe du fournisseur a l'avantage supplémentaire de configurer l'objet de service avant qu'il ne soit instancié. serviceInstance = $ get ();

Une fourmi
la source
63

Usine

Vous donnez une fonction à AngularJS, AngularJS mettra en cache et injectera la valeur de retour lorsque l'usine sera demandée.

Exemple:

app.factory('factory', function() {
    var name = '';
    // Return value **is** the object that will be injected
    return {
        name: name;
    }
})

Usage:

app.controller('ctrl', function($scope, factory) {
     $scope.name = factory.name;
});

Un service

Vous donnez une fonction à AngularJS, AngularJS appellera new pour l'instancier. C'est l'instance créée par AngularJS qui sera mise en cache et injectée lorsque le service sera demandé. Puisque new a été utilisé pour instancier le service, le mot - clé this est valide et fait référence à l'instance.

Exemple:

app.service('service', function() {
     var name = '';
     this.setName = function(newName) {
         name = newName;
     }
     this.getName = function() {
         return name;
     }
});

Usage:

app.controller('ctrl', function($scope, service) {
   $scope.name = service.getName();
});

Fournisseur

Vous donnez à AngularJS une fonction, et AngularJS appellera sa $getfonction. C'est la valeur de retour de la $getfonction qui sera mise en cache et injectée lorsque le service sera demandé.

Les fournisseurs vous permettent de configurer le fournisseur avant qu'AngularJS appelle la $getméthode pour obtenir l'injectable.

Exemple:

app.provider('provider', function() {
     var name = '';
     this.setName = function(newName) {
          name = newName;
     }
     this.$get = function() {
         return {
            name: name
         }
     }
})

Utilisation (comme injectable dans un contrôleur)

app.controller('ctrl', function($scope, provider) {
    $scope.name = provider.name;
});

Utilisation (la configuration du fournisseur avant $getest appelée pour créer l'injectable)

app.config(function(providerProvider) {
    providerProvider.setName('John');
});
pixelbits
la source
56

J'ai remarqué quelque chose d'intéressant en jouant avec les fournisseurs.

La visibilité des injectables est différente pour les prestataires que pour les services et les usines. Si vous déclarez une «constante» AngularJS (par exemple, myApp.constant('a', 'Robert');), vous pouvez l'injecter dans les services, les usines et les fournisseurs.

Mais si vous déclarez une "valeur" AngularJS (par exemple., myApp.value('b', {name: 'Jones'});), Vous pouvez l'injecter dans les services et les usines, mais PAS dans la fonction de création de fournisseur. Vous pouvez cependant l'injecter dans la $getfonction que vous définissez pour votre fournisseur. Ceci est mentionné dans la documentation AngularJS, mais c'est facile à manquer. Vous pouvez le trouver sur la page% provide dans les sections sur la valeur et les méthodes constantes.

http://jsfiddle.net/R2Frv/1/

<div ng-app="MyAppName">
    <div ng-controller="MyCtrl">
        <p>from Service: {{servGreet}}</p>
        <p>from Provider: {{provGreet}}</p>
    </div>
</div>
<script>
    var myApp = angular.module('MyAppName', []);

    myApp.constant('a', 'Robert');
    myApp.value('b', {name: 'Jones'});

    myApp.service('greetService', function(a,b) {
        this.greeter = 'Hi there, ' + a + ' ' + b.name;
    });

    myApp.provider('greetProvider', function(a) {
        this.firstName = a;
        this.$get = function(b) {
            this.lastName = b.name;
            this.fullName = this.firstName + ' ' + this.lastName;
            return this;
        };
    });

    function MyCtrl($scope, greetService, greetProvider) {
        $scope.servGreet = greetService.greeter;
        $scope.provGreet = greetProvider.fullName;
    }
</script>
Juste regarder
la source
45

C'est une partie très déroutante pour les débutants et j'ai essayé de le clarifier en termes simples

Service AngularJS: est utilisé pour partager des fonctions utilitaires avec la référence de service dans le contrôleur. Le service est de nature singleton, donc pour un service, une seule instance est créée dans le navigateur et la même référence est utilisée tout au long de la page.

Dans le service, nous créons des noms de fonction en tant que propriété avec cet objet.

AngularJS Factory: le but de Factory est également le même que Service mais dans ce cas nous créons un nouvel objet et ajoutons des fonctions comme propriétés de cet objet et à la fin nous retournons cet objet.

Fournisseur AngularJS: le but de ceci est encore le même mais le fournisseur donne la sortie de sa fonction $ get.

La définition et l'utilisation de Service, Factory et Provider sont expliquées sur http://www.dotnetfunda.com/articles/show/3156/difference-between-angularjs-service-factory-and-provider

Sheo Narayan
la source
2
L'usine et les fournisseurs sont également des objets singleton? Tout scanrio où les usines sont recommandées par rapport aux services?
Sunil Garg
34

Pour moi, la meilleure et la plus simple façon de comprendre la différence est:

var service, factory;
service = factory = function(injection) {}

Comment AngularJS instancie des composants particuliers (simplifié):

// service
var angularService = new service(injection);

// factory
var angularFactory = factory(injection);

Ainsi, pour le service, ce qui devient le composant AngularJS est l'instance d'objet de la classe qui est représentée par la fonction de déclaration de service. Pour l'usine, c'est le résultat renvoyé par la fonction de déclaration d'usine. L'usine peut se comporter de la même manière que le service:

var factoryAsService = function(injection) {
  return new function(injection) {
    // Service content
  }
}

La façon la plus simple de penser est la suivante:

  • Le service est une instance d'objet singleton. Utilisez les services si vous souhaitez fournir un objet singleton pour votre code.
  • L'usine est une classe. Utilisez des usines si vous souhaitez fournir des classes personnalisées pour votre code (impossible avec les services car ils sont déjà instanciés).

L'exemple de «classe» d'usine est fourni dans les commentaires, ainsi que la différence de fournisseur.

Lukasz Frankowski
la source
comment un service peut-il être un singleton s'il est instancié à chaque fois qu'il est utilisé? je peux comprendre ce qui se passe ...
joe
Le service n'est instancié qu'une seule fois pendant la résolution des dépendances, puis lorsque vous demandez le service à l'injecteur, vous obtenez toujours la même instance. Il peut être facilement vérifié ici: jsfiddle.net/l0co/sovtu55t/1 , veuillez l'exécuter avec la console. La console montre que le service n'est instancié qu'une seule fois.
Lukasz Frankowski du
Oh je vois. je m'attendais à pouvoir littéralement new MyService()ou quelque chose :)
joe
33

Ma clarification à ce sujet:

Fondamentalement, tous les types mentionnés (service, usine, fournisseur, etc.) ne font que créer et configurer des variables globales (qui sont bien sûr globales pour l'ensemble de l'application), tout comme les variables globales à l'ancienne.

Bien que les variables globales ne soient pas recommandées, l'utilisation réelle de ces variables globales est de fournir une injection de dépendance , en transmettant la variable au contrôleur approprié.

La création des valeurs des "variables globales" présente de nombreux niveaux de complications:

  1. Constante
    Ceci définit une constante réelle qui ne doit pas être modifiée pendant toute l'application, tout comme les constantes dans d'autres langues (ce qui manque à JavaScript).
  2. Valeur
    Il s'agit d'une valeur ou d'un objet modifiable, et elle sert de variable globale, qui peut même être injectée lors de la création d'autres services ou usines (voir plus loin). Cependant, il doit s'agir d'une " valeur littérale ", ce qui signifie que l'on doit écrire la valeur réelle et ne peut utiliser aucune logique de calcul ou de programmation (en d'autres termes, 39 ou myText ou {prop: "value"} sont OK, mais 2 +2 ne l'est pas).
  3. Usine
    Une valeur plus générale, qui peut être calculée immédiatement. Il fonctionne en passant une fonction à AngularJS avec la logique nécessaire pour calculer la valeur et AngularJS l'exécute, et il enregistre la valeur de retour dans la variable nommée.
    Notez qu'il est possible de renvoyer un objet (auquel cas il fonctionnera comme un service ) ou une fonction (qui sera enregistrée dans la variable en tant que fonction de rappel).
  4. Service
    Un service est une version d' usine plus simplifiée qui n'est valide que lorsque la valeur est un objet, et il permet d'écrire toute logique directement dans la fonction (comme s'il s'agissait d'un constructeur), ainsi que de déclarer et d'accéder à les propriétés de l'objet à l'aide du mot - clé this .
  5. Fournisseur
    Contrairement à un service qui est une version simplifiée de l' usine , un fournisseur est un moyen plus complexe mais plus flexible d'initialiser les variables "globales", la plus grande flexibilité étant l'option de définition de valeurs à partir de app.config.
    Cela fonctionne comme l'utilisation d'une combinaison de service et de fournisseur , en passant au fournisseur une fonction qui a des propriétés déclarées à l'aide du mot - clé this , qui peut être utilisé à partir du app.config.
    Ensuite, il doit avoir une fonction $ .get distincte qui est exécutée par AngularJS après avoir défini les propriétés ci-dessus via le app.configfichier, et cette fonction $ .get se comporte exactement comme le usine ci-dessus, en ce que sa valeur de retour est utilisée pour initialiser les variables "globales".
yoel halb
la source
26

Ma compréhension est très simple ci-dessous.

Usine: il vous suffit de créer un objet à l'intérieur de l'usine et de le renvoyer.

Un service:

Vous avez juste une fonction standard qui utilise ce mot-clé pour définir une fonction.

Fournisseur:

Il y a un $getobjet que vous définissez et il peut être utilisé pour obtenir l'objet qui renvoie des données.

sajan
la source
N'avez-vous pas mélangé l'usine et le service? Les services créent là où l'usine revient.
Flavien Volken
Lorsque vous déclarez le nom du service comme argument injectable, vous recevrez une instance de la fonction. En d'autres termes, nouvelle FunctionYouPassedToService (). Cette instance d'objet devient l'objet de service qu'AngularJS enregistre et injecte ultérieurement à d'autres services / contrôleurs si nécessaire. // fabrique Lorsque vous déclarez fabrique comme argument injectable, la valeur renvoyée vous sera fournie en appelant la référence de fonction passée à module.factory.
sajan
D'accord, donc… en angulaire, l'usine est un singleton où le "service" est en fait une usine (en termes de modèles de conception courants)
Flavien Volken
25

Résumé des documents angulaires :

  • Il existe cinq types de recettes qui définissent comment créer des objets: Valeur , Usine , Service , Fournisseur et Constante .
  • L'usine et le service sont les recettes les plus utilisées. La seule différence entre eux est que la recette du service fonctionne mieux pour les objets d'un type personnalisé, tandis que l' usine peut produire des primitives et des fonctions JavaScript.
  • La recette du fournisseur est le type de recette de base et toutes les autres ne sont que du sucre syntaxique.
  • Le fournisseur est le type de recette le plus complexe. Vous n'en avez pas besoin sauf si vous créez un morceau de code réutilisable qui nécessite une configuration globale.

entrez la description de l'image ici


Meilleures réponses de SO:

https://stackoverflow.com/a/26924234/165673 (<- BON) https://stackoverflow.com/a/27263882/165673
https://stackoverflow.com/a/16566144/165673

Yarin
la source
20

Toutes les bonnes réponses déjà. Je voudrais ajouter quelques points supplémentaires sur le service et l' usine . Avec la différence entre service / usine. Et on peut aussi avoir des questions comme:

  1. Dois-je utiliser le service ou l'usine? Quelle est la différence?
  2. Font-ils la même chose ou ont-ils le même comportement?

Commençons par la différence entre le service et l'usine:

  1. Les deux sont des singletons : chaque fois qu'Angular les trouve comme dépendance pour la première fois, il crée une seule instance de service / usine. Une fois l'instance créée, la même instance est utilisée pour toujours.

  2. Peut être utilisé pour modéliser un objet avec un comportement : ils peuvent tous deux avoir des méthodes, des variables d'état interne, etc. Bien que la façon dont vous écrivez ce code soit différente.

Prestations de service:

Un service est une fonction constructeur, et Angular l'instanciera en appelant new yourServiceName(). Cela signifie plusieurs choses.

  1. Les fonctions et les variables d'instance seront des propriétés de this.
  2. Vous n'avez pas besoin de retourner une valeur. Lorsque Angular appelle new yourServiceName(), il recevra l' thisobjet avec toutes les propriétés que vous lui aurez attribuées.

Exemple d'échantillon:

angular.service('MyService', function() {
  this.aServiceVariable = "Ved Prakash"
  this.aServiceMethod = function() {
    return //code
  };
});

Lorsque Angular injecte ce MyServiceservice dans un contrôleur qui en dépend, ce contrôleur obtiendra une MyServicefonction sur laquelle il peut appeler des fonctions, par exemple MyService.aServiceMethod ().

Soyez prudent avecthis :

Étant donné que le service construit est un objet, les méthodes qu'il contient peuvent s'y référer lorsqu'elles sont appelées:

angular.service('ScoreKeeper', function($http) {
  this.score = 0;

  this.getScore = function() {
    return this.score;
  };

  this.setScore = function(newScore) {
    this.score = newScore;
  };

  this.addOne = function() {
    this.score++;
  };
});

Vous pourriez être tenté d'appeler ScoreKeeper.setScoredans une chaîne de promesses, par exemple si vous avez initialisé le score en le saisissant sur le serveur: $http.get('/score').then(ScoreKeeper.setScore).le problème est que ce ScoreKeeper.setScoresera appelé avec thislié à nullet vous obtiendrez des erreurs. La meilleure façon serait $http.get('/score').then(ScoreKeeper.setScore.bind(ScoreKeeper)). Que vous choisissiez de l'utiliser ou non dans vos méthodes de service, faites attention à la façon dont vous les appelez.

Renvoyer une valeur à partir d'unService :

En raison du fonctionnement des constructeurs JavaScript, si vous renvoyez une valeur complexe à (i.e., an Object)partir d'une constructorfonction, l'appelant obtiendra cet objet au lieu de cette instance.

Cela signifie que vous pouvez essentiellement copier-coller l'exemple d'usine par le bas, le remplacer factorypar service, et cela fonctionnera:

angular.service('MyService', function($http) {
  var api = {};

  api.aServiceMethod= function() {
    return $http.get('/users');
  };
  return api;
});

Ainsi, lorsque Angular construit votre service avec le nouveau MyService (), il obtiendra cet objet api au lieu de l'instance MyService.

C'est le comportement pour toutes les valeurs complexes (objets, fonctions) mais pas pour les types primitifs.

Des usines:

Une usine est une ancienne fonction qui renvoie une valeur. La valeur de retour est ce qui est injecté dans des choses qui dépendent de l'usine. Un modèle d'usine typique dans Angular consiste à renvoyer un objet avec des fonctions comme propriétés, comme ceci:

angular.factory('MyFactory', function($http) {
  var api = {};

  api.aFactoryMethod= function() {
    return $http.get('/users');
  };

  return api;
});

La valeur injectée pour une dépendance d'usine est la valeur de retour de l'usine, et il ne doit pas nécessairement être un objet. Cela pourrait être une fonction

Réponses aux questions 1 et 2 ci-dessus:

Pour la plupart, restez avec l'utilisation d'usines pour tout. Leur comportement est plus facile à comprendre. Il n'y a pas de choix à faire pour renvoyer une valeur ou non, et en outre, aucun bogue à introduire si vous faites la mauvaise chose.

Je les qualifie toujours de «services» quand je parle de les injecter comme des dépendances.

Le comportement du service / de l'usine est très similaire, et certaines personnes diront que l'un ou l'autre est correct. C'est un peu vrai, mais je trouve plus facile de suivre les conseils du guide de style de John Papa et de rester fidèle aux usines. **

Ved
la source
16

Une précision supplémentaire est que les usines peuvent créer des fonctions / primitives, contrairement aux services. Découvrez ce jsFiddle basé sur Epokk: http://jsfiddle.net/skeller88/PxdSP/1351/ .

La fabrique renvoie une fonction qui peut être invoquée:

myApp.factory('helloWorldFromFactory', function() {
  return function() {
    return "Hello, World!";
  };
});

La fabrique peut également renvoyer un objet avec une méthode qui peut être invoquée:

myApp.factory('helloWorldFromFactory', function() {
  return {
    sayHello: function() {
      return "Hello, World!";
    }
  };
});

Le service renvoie un objet avec une méthode qui peut être invoquée:

myApp.service('helloWorldFromService', function() {
  this.sayHello = function() {
     return "Hello, World!";
  };
});

Pour plus de détails, voir un article que j'ai écrit sur la différence: http://www.shanemkeller.com/tldr-services-vs-factories-in-angular/

skeller88
la source
16

Il y a déjà de bonnes réponses, mais je veux juste partager celle-ci.

Tout d'abord: le fournisseur est le moyen / la recette pour créer unservice (objet singleton) qui suppose être injecté par $ injector (comment AngulaJS traite le modèle IoC).

Et Value, Factory, Service et Constant (4 voies) - le sucre syntaxique par rapport à la méthode Provider / recette.

Il y a une Service vs Factorypartie a été couverte: https://www.youtube.com/watch?v=BLzNCkPn3ao

Le service est avant tout un newmot clé qui, comme nous le savons, fait 4 choses:

  1. crée un tout nouvel objet
  2. le relie à son prototypeobjet
  3. se connecte contextàthis
  4. et retourne this

Et Factory est tout sur Factory Pattern - contient des fonctions qui renvoient des objets comme ce service.

  1. pouvoir utiliser d'autres services (avoir des dépendances)
  2. initialisation de service
  3. initialisation retardée / paresseuse

Et cette vidéo simple / courte: couvre également le fournisseur : https://www.youtube.com/watch?v=HvTZbQ_hUZY (là vous pouvez voir comment ils vont de l'usine au fournisseur)

La recette du fournisseur est principalement utilisée dans la configuration de l'application, avant que l'application ne soit complètement démarrée / initialisée.

ses
la source
14

Après avoir lu tous ces articles, cela a créé plus de confusion pour moi .. Mais tout est toujours utile .. enfin j'ai trouvé le tableau suivant qui donnera des informations avec une comparaison simple

  • L'injecteur utilise des recettes pour créer deux types d'objets: les services et les objets spéciaux
  • Il existe cinq types de recettes qui définissent comment créer des objets: Valeur, Usine, Service, Fournisseur et Constante.
  • L'usine et le service sont les recettes les plus utilisées. La seule différence entre eux est que la recette du service fonctionne mieux pour les objets d'un type personnalisé, tandis que la fabrique peut produire des primitives et des fonctions JavaScript.
  • La recette du fournisseur est le type de recette de base et toutes les autres ne sont que du sucre syntaxique.
  • Le fournisseur est le type de recette le plus complexe. Vous n'en avez pas besoin sauf si vous créez un morceau de code réutilisable qui nécessite une configuration globale.
  • Tous les objets à usage spécial, à l'exception du contrôleur, sont définis via des recettes d'usine.

entrez la description de l'image ici

Et pour les débutants, comprenez: - Cela peut ne pas corriger le cas d'utilisation, mais à un niveau élevé, c'est ce qui convient à ces trois.

  1. Si vous souhaitez utiliser dans le module angulaire, la fonction de configuration doit être créée en tant que fournisseur

angular.module('myApp').config(function($testProvider){
$testProvider.someFunction();
})

  1. L'appel Ajax ou les intégrations tierces doivent être des services .
  2. Pour les manipulations de données, créez-le en usine

Pour les scénarios de base, Factory & Service se comporte de la même manière.

BEJGAM SHIVA PRASAD
la source
13

Voici du code de broilplate que j'ai trouvé comme modèle de code pour la fabrique d'objets dans AngularjS. J'ai utilisé un Car / CarFactory comme exemple pour illustrer. Permet un code d'implémentation simple dans le contrôleur.

     <script>
        angular.module('app', [])
            .factory('CarFactory', function() {

                /**
                 * BroilerPlate Object Instance Factory Definition / Example
                 */
                this.Car = function() {

                    // initialize instance properties
                    angular.extend(this, {
                        color           : null,
                        numberOfDoors   : null,
                        hasFancyRadio   : null,
                        hasLeatherSeats : null
                    });

                    // generic setter (with optional default value)
                    this.set = function(key, value, defaultValue, allowUndefined) {

                        // by default,
                        if (typeof allowUndefined === 'undefined') {
                            // we don't allow setter to accept "undefined" as a value
                            allowUndefined = false;
                        }
                        // if we do not allow undefined values, and..
                        if (!allowUndefined) {
                            // if an undefined value was passed in
                            if (value === undefined) {
                                // and a default value was specified
                                if (defaultValue !== undefined) {
                                    // use the specified default value
                                    value = defaultValue;
                                } else {
                                    // otherwise use the class.prototype.defaults value
                                    value = this.defaults[key];
                                } // end if/else
                            } // end if
                        } // end if

                        // update 
                        this[key] = value;

                        // return reference to this object (fluent)
                        return this;

                    }; // end this.set()

                }; // end this.Car class definition

                // instance properties default values
                this.Car.prototype.defaults = {
                    color: 'yellow',
                    numberOfDoors: 2,
                    hasLeatherSeats: null,
                    hasFancyRadio: false
                };

                // instance factory method / constructor
                this.Car.prototype.instance = function(params) {
                    return new 
                        this.constructor()
                                .set('color',           params.color)
                                .set('numberOfDoors',   params.numberOfDoors)
                                .set('hasFancyRadio',   params.hasFancyRadio)
                                .set('hasLeatherSeats', params.hasLeatherSeats)
                    ;
                };

                return new this.Car();

            }) // end Factory Definition
            .controller('testCtrl', function($scope, CarFactory) {

                window.testCtrl = $scope;

                // first car, is red, uses class default for:
                // numberOfDoors, and hasLeatherSeats
                $scope.car1     = CarFactory
                                    .instance({
                                        color: 'red'
                                    })
                                ;

                // second car, is blue, has 3 doors, 
                // uses class default for hasLeatherSeats
                $scope.car2     = CarFactory
                                    .instance({
                                        color: 'blue',
                                        numberOfDoors: 3
                                    })
                                ;
                // third car, has 4 doors, uses class default for 
                // color and hasLeatherSeats
                $scope.car3     = CarFactory
                                    .instance({
                                        numberOfDoors: 4
                                    })
                                ;
                // sets an undefined variable for 'hasFancyRadio',
                // explicitly defines "true" as default when value is undefined
                $scope.hasFancyRadio = undefined;
                $scope.car3.set('hasFancyRadio', $scope.hasFancyRadio, true);

                // fourth car, purple, 4 doors,
                // uses class default for hasLeatherSeats
                $scope.car4     = CarFactory
                                    .instance({
                                        color: 'purple',
                                        numberOfDoors: 4
                                    });
                // and then explicitly sets hasLeatherSeats to undefined
                $scope.hasLeatherSeats = undefined;
                $scope.car4.set('hasLeatherSeats', $scope.hasLeatherSeats, undefined, true);

                // in console, type window.testCtrl to see the resulting objects

            });
    </script>

Voici un exemple plus simple. J'utilise quelques bibliothèques tierces qui attendent un objet "Position" exposant la latitude et la longitude, mais via différentes propriétés d'objet. Je ne voulais pas pirater le code du fournisseur, j'ai donc ajusté les objets "Position" que je faisais circuler.

    angular.module('app')
.factory('PositionFactory', function() {

    /**
     * BroilerPlate Object Instance Factory Definition / Example
     */
    this.Position = function() {

        // initialize instance properties 
        // (multiple properties to satisfy multiple external interface contracts)
        angular.extend(this, {
            lat         : null,
            lon         : null,
            latitude    : null,
            longitude   : null,
            coords: {
                latitude: null,
                longitude: null
            }
        });

        this.setLatitude = function(latitude) {
            this.latitude           = latitude;
            this.lat                = latitude;
            this.coords.latitude    = latitude;
            return this;
        };
        this.setLongitude = function(longitude) {
            this.longitude          = longitude;
            this.lon                = longitude;
            this.coords.longitude   = longitude;
            return this;
        };

    }; // end class definition

    // instance factory method / constructor
    this.Position.prototype.instance = function(params) {
        return new 
            this.constructor()
                    .setLatitude(params.latitude)
                    .setLongitude(params.longitude)
        ;
    };

    return new this.Position();

}) // end Factory Definition

.controller('testCtrl', function($scope, PositionFactory) {
    $scope.position1 = PositionFactory.instance({latitude: 39, longitude: 42.3123});
    $scope.position2 = PositionFactory.instance({latitude: 39, longitude: 42.3333});
}) // end controller

;

James Earlywine
la source
12

En utilisant comme référence cette page et la documentation (qui semble s'être considérablement améliorée depuis la dernière fois que j'ai regardé), j'ai mis en place la démo du monde réel (-ish) qui utilise 4 des 5 versions du fournisseur; Valeur, constante, usine et fournisseur complet.

HTML:

<div ng-controller="mainCtrl as main">
    <h1>{{main.title}}*</h1>
    <h2>{{main.strapline}}</h2>
    <p>Earn {{main.earn}} per click</p>
    <p>You've earned {{main.earned}} by clicking!</p>
    <button ng-click="main.handleClick()">Click me to earn</button>
    <small>* Not actual money</small>
</div>

app

var app = angular.module('angularProviders', []);

// A CONSTANT is not going to change
app.constant('range', 100);

// A VALUE could change, but probably / typically doesn't
app.value('title', 'Earn money by clicking');
app.value('strapline', 'Adventures in ng Providers');

// A simple FACTORY allows us to compute a value @ runtime.
// Furthermore, it can have other dependencies injected into it such
// as our range constant.
app.factory('random', function randomFactory(range) {
    // Get a random number within the range defined in our CONSTANT
    return Math.random() * range;
});

// A PROVIDER, must return a custom type which implements the functionality 
// provided by our service (see what I did there?).
// Here we define the constructor for the custom type the PROVIDER below will 
// instantiate and return.
var Money = function(locale) {

    // Depending on locale string set during config phase, we'll
    // use different symbols and positioning for any values we 
    // need to display as currency
    this.settings = {
        uk: {
            front: true,
            currency: '£',
            thousand: ',',
            decimal: '.'
        },
        eu: {
            front: false,
            currency: '€',
            thousand: '.',
            decimal: ','
        }
    };

    this.locale = locale;
};

// Return a monetary value with currency symbol and placement, and decimal 
// and thousand delimiters according to the locale set in the config phase.
Money.prototype.convertValue = function(value) {

    var settings = this.settings[this.locale],
        decimalIndex, converted;

    converted = this.addThousandSeparator(value.toFixed(2), settings.thousand);

    decimalIndex = converted.length - 3;

    converted = converted.substr(0, decimalIndex) +
        settings.decimal +
        converted.substr(decimalIndex + 1);    

    converted = settings.front ?
            settings.currency + converted : 
            converted + settings.currency; 

    return converted;   
};

// Add supplied thousand separator to supplied value
Money.prototype.addThousandSeparator = function(value, symbol) {
   return value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, symbol);
};

// PROVIDER is the core recipe type - VALUE, CONSTANT, SERVICE & FACTORY
// are all effectively syntactic sugar built on top of the PROVIDER construct
// One of the advantages of the PROVIDER is that we can configure it before the
// application starts (see config below).
app.provider('money', function MoneyProvider() {

    var locale;

    // Function called by the config to set up the provider
    this.setLocale = function(value) {
        locale = value;   
    };

    // All providers need to implement a $get method which returns
    // an instance of the custom class which constitutes the service
    this.$get = function moneyFactory() {
        return new Money(locale);
    };
});

// We can configure a PROVIDER on application initialisation.
app.config(['moneyProvider', function(moneyProvider) {
    moneyProvider.setLocale('uk');
    //moneyProvider.setLocale('eu'); 
}]);

// The ubiquitous controller
app.controller('mainCtrl', function($scope, title, strapline, random, money) {

    // Plain old VALUE(s)
    this.title = title;
    this.strapline = strapline;

    this.count = 0;

    // Compute values using our money provider    
    this.earn = money.convertValue(random); // random is computed @ runtime
    this.earned = money.convertValue(0);

    this.handleClick = function() { 
        this.count ++;
        this.earned = money.convertValue(random * this.count);
    };
});

Démo de travail .

net.uk.sweet
la source
12

Cette réponse aborde le sujet / la question

comment Factory, Service et Constant - ne sont-ils que du sucre syntaxique en plus d'une recette de fournisseur?

OU

comment l'usine, le service et les fournisseurs sont simailar en interne

essentiellement ce qui se passe est

Lorsque vous créez un factory()il définit le functionsecond argument que vous avez fourni au fournisseur $getet que vous le renvoyez ( provider(name, {$get:factoryFn })), tout ce que vous obtenez est providermais il n'y a pas de propriété / méthode autre que$get celle-ci provider(signifie que vous ne pouvez pas configurer cela)

Code source de l'usine

function factory(name, factoryFn, enforce) {
    return provider(name, {
      $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
    });
};

Lorsque vous faites un service()retour, vous fournissez une fabrique () avec un functionqui injecte le constructor(retourne l'instance du constructeur que vous avez fourni dans votre service) et le retourne

Code source du service

function service(name, constructor) {
    return factory(name, ['$injector', function($injector) {
      return $injector.instantiate(constructor);
    }]);
};

Donc, fondamentalement, dans les deux cas, vous obtenez finalement un fournisseur $ get défini sur la fonction que vous avez fournie, mais vous pouvez donner quelque chose de plus que $ get comme vous pouvez initialement le fournir dans provider () pour le bloc de configuration

UN B
la source
11

Je connais beaucoup d'excellentes réponses, mais je dois partager mon expérience de l'utilisation de
1. servicepour la plupart des cas de défaut
2. factoryutilisé pour créer le service de cette instance spécifique

// factory.js ////////////////////////////
(function() {
'use strict';
angular
    .module('myApp.services')
    .factory('xFactory', xFactoryImp);
xFactoryImp.$inject = ['$http'];

function xFactoryImp($http) {
    var fac = function (params) {
        this._params = params; // used for query params
    };

    fac.prototype.nextPage = function () {
        var url = "/_prc";

        $http.get(url, {params: this._params}).success(function(data){ ...
    }
    return fac;
}
})();

// service.js //////////////////////////
(function() {
'use strict';
angular
    .module('myApp.services')
    .service('xService', xServiceImp);
xServiceImp.$inject = ['$http'];

function xServiceImp($http) {  
    this._params = {'model': 'account','mode': 'list'};

    this.nextPage = function () {
        var url = "/_prc";

        $http.get(url, {params: this._params}).success(function(data){ ...
    }       
}
})();

et en utilisant:

controller: ['xFactory', 'xService', function(xFactory, xService){

        // books = new instance of xFactory for query 'book' model
        var books = new xFactory({'model': 'book', 'mode': 'list'});

        // accounts = new instance of xFactory for query 'accounts' model
        var accounts = new xFactory({'model': 'account', 'mode': 'list'});

        // accounts2 = accounts variable
        var accounts2 = xService;
... 
nguyên
la source
10

Un peu tard pour la fête. Mais je pensais que cela était plus utile pour ceux qui aimeraient apprendre (ou avoir de la clarté) sur le développement de services personnalisés Angular JS en utilisant les méthodologies d'usine, de service et de fournisseur.

Je suis tombé sur cette vidéo qui explique clairement les méthodologies d'usine, de service et de fournisseur pour développer AngularJS Custom Services:

https://www.youtube.com/watch?v=oUXku28ex-M

Code source: http://www.techcbt.com/Post/353/Angular-JS-basics/how-to-develop-angularjs-custom-service

Le code affiché ici est copié directement à partir de la source ci-dessus, pour le bénéfice des lecteurs.

Le code du service personnalisé basé en usine est le suivant (qui va avec les versions sync et async avec l'appel du service http):

var app = angular.module("app", []);
app.controller('emp', ['$scope', 'calcFactory',
  function($scope, calcFactory) {
    $scope.a = 10;
    $scope.b = 20;

    $scope.doSum = function() {
      //$scope.sum = calcFactory.getSum($scope.a, $scope.b); //synchronous
      calcFactory.getSum($scope.a, $scope.b, function(r) { //aynchronous
        $scope.sum = r;
      });
    };

  }
]);

app.factory('calcFactory', ['$http', '$log',
  function($http, $log) {
    $log.log("instantiating calcFactory..");
    var oCalcService = {};

    //oCalcService.getSum = function(a,b){
    //	return parseInt(a) + parseInt(b);
    //};

    //oCalcService.getSum = function(a, b, cb){
    //	var s = parseInt(a) + parseInt(b);
    //	cb(s);
    //};

    oCalcService.getSum = function(a, b, cb) { //using http service

      $http({
        url: 'http://localhost:4467/Sum?a=' + a + '&b=' + b,
        method: 'GET'
      }).then(function(resp) {
        $log.log(resp.data);
        cb(resp.data);
      }, function(resp) {
        $log.error("ERROR occurred");
      });
    };

    return oCalcService;
  }
]);

Le code de la méthodologie «service» pour les services personnalisés (il est assez similaire à «usine», mais différent du point de vue de la syntaxe):

var app = angular.module("app", []);
app.controller('emp', ['$scope', 'calcService', function($scope, calcService){
	$scope.a = 10;
	$scope.b = 20;

	$scope.doSum = function(){
		//$scope.sum = calcService.getSum($scope.a, $scope.b);
		
		calcService.getSum($scope.a, $scope.b, function(r){
			$scope.sum = r;
		});		
	};

}]);

app.service('calcService', ['$http', '$log', function($http, $log){
	$log.log("instantiating calcService..");
	
	//this.getSum = function(a,b){
	//	return parseInt(a) + parseInt(b);
	//};

	//this.getSum = function(a, b, cb){
	//	var s = parseInt(a) + parseInt(b);
	//	cb(s);
	//};

	this.getSum = function(a, b, cb){
		$http({
			url: 'http://localhost:4467/Sum?a=' + a + '&b=' + b,
			method: 'GET'
		}).then(function(resp){
			$log.log(resp.data);
			cb(resp.data);
		},function(resp){
			$log.error("ERROR occurred");
		});
	};

}]);

Le code de la méthodologie «fournisseur» pour les services personnalisés (cela est nécessaire si vous souhaitez développer un service qui pourrait être configuré):

var app = angular.module("app", []);
app.controller('emp', ['$scope', 'calcService', function($scope, calcService){
	$scope.a = 10;
	$scope.b = 20;

	$scope.doSum = function(){
		//$scope.sum = calcService.getSum($scope.a, $scope.b);
		
		calcService.getSum($scope.a, $scope.b, function(r){
			$scope.sum = r;
		});		
	};

}]);

app.provider('calcService', function(){

	var baseUrl = '';

	this.config = function(url){
		baseUrl = url;
	};

	this.$get = ['$log', '$http', function($log, $http){
		$log.log("instantiating calcService...")
		var oCalcService = {};

		//oCalcService.getSum = function(a,b){
		//	return parseInt(a) + parseInt(b);
		//};

		//oCalcService.getSum = function(a, b, cb){
		//	var s = parseInt(a) + parseInt(b);
		//	cb(s);	
		//};

		oCalcService.getSum = function(a, b, cb){

			$http({
				url: baseUrl + '/Sum?a=' + a + '&b=' + b,
				method: 'GET'
			}).then(function(resp){
				$log.log(resp.data);
				cb(resp.data);
			},function(resp){
				$log.error("ERROR occurred");
			});
		};		

		return oCalcService;
	}];

});

app.config(['calcServiceProvider', function(calcServiceProvider){
	calcServiceProvider.config("http://localhost:4467");
}]);

Enfin l'interface utilisateur qui fonctionne avec l'un des services ci-dessus:

<html>
<head>
	<title></title>
	<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js" ></script>
	<script type="text/javascript" src="t03.js"></script>
</head>
<body ng-app="app">
	<div ng-controller="emp">
		<div>
			Value of a is {{a}},
			but you can change
			<input type=text ng-model="a" /> <br>

			Value of b is {{b}},
			but you can change
			<input type=text ng-model="b" /> <br>

		</div>
		Sum = {{sum}}<br>
		<button ng-click="doSum()">Calculate</button>
	</div>
</body>
</html>

user203687
la source
10

Juste pour clarifier les choses, à partir de la source AngularJS, vous pouvez voir qu'un service appelle simplement la fonction d'usine qui à son tour appelle la fonction de fournisseur:

function factory(name, factoryFn) { 
    return provider(name, { $get: factoryFn }); 
}

function service(name, constructor) {
    return factory(name, ['$injector', function($injector) {
      return $injector.instantiate(constructor);
    }]);
}
Ricardo Rossi
la source
9

Discutons des trois façons de gérer la logique métier dans AngularJS de manière simple: ( Inspiré par le cours Coursera AngularJS de Yaakov )

SERVICE :

Syntaxe:

app.js

 var app = angular.module('ServiceExample',[]);
 var serviceExampleController =
              app.controller('ServiceExampleController', ServiceExampleController);
 var serviceExample = app.service('NameOfTheService', NameOfTheService);

 ServiceExampleController.$inject = ['NameOfTheService'] //protects from minification of js files

function ServiceExampleController(NameOfTheService){
     serviceExampleController = this;
     serviceExampleController.data = NameOfTheService.getSomeData();
 }

function NameOfTheService(){
     nameOfTheService = this;
     nameOfTheService.data = "Some Data";
     nameOfTheService.getSomeData = function(){
           return nameOfTheService.data;
     }     
}

index.html

<div ng-controller = "ServiceExampleController as serviceExample">
   {{serviceExample.data}}
</div>

Caractéristiques du service:

  1. Paresseusement instancié : s'il n'est pas injecté, il ne sera jamais instancié. Donc, pour l'utiliser, il faudra l'injecter dans un module.
  2. Singleton : s'ils sont injectés dans plusieurs modules, tous n'auront accès qu'à une seule instance particulière. C'est pourquoi il est très pratique de partager des données entre différents contrôleurs.

USINE

Voyons d'abord la syntaxe:

app.js :

var app = angular.module('FactoryExample',[]);
var factoryController = app.controller('FactoryController', FactoryController);
var factoryExampleOne = app.factory('NameOfTheFactoryOne', NameOfTheFactoryOne);
var factoryExampleTwo = app.factory('NameOfTheFactoryTwo', NameOfTheFactoryTwo);

//first implementation where it returns a function
function NameOfTheFactoryOne(){
   var factory = function(){
      return new SomeService();
    }
   return factory;
}

//second implementation where an object literal would be returned
function NameOfTheFactoryTwo(){
   var factory = {
      getSomeService : function(){
          return new SomeService();
       }
    };
   return factory;
}

Maintenant, en utilisant les deux ci-dessus dans le contrôleur:

 var factoryOne = NameOfTheFactoryOne() //since it returns a function
 factoryOne.someMethod();

 var factoryTwo = NameOfTheFactoryTwo.getSomeService(); //accessing the object
 factoryTwo.someMethod();

Caractéristiques de l'usine:

  1. Suit le modèle de conception d'usine. L'usine est un lieu central qui produit de nouveaux objets ou fonctions.
  2. Non seulement produit des services singleton, mais personnalisables.
  3. La .service()méthode est une usine qui produit toujours le même type de service, qui est un singleton, et sans aucun moyen facile de configurer son comportement. Cette .service()méthode est généralement utilisée comme raccourci pour quelque chose qui ne nécessite aucune configuration.

FOURNISSEUR

Revoyons d'abord la syntaxe:

angular.module('ProviderModule', [])
.controller('ProviderModuleController', ProviderModuleController)
.provider('ServiceProvider', ServiceProvider)
.config(Config); //optional

Config.$inject = ['ServiceProvider'];
function Config(ServiceProvider) {
  ServiceProvider.defaults.maxItems = 10; //some default value
}


ProviderModuleController.$inject = ['ServiceProvider'];
function ProviderModuleController(ServiceProvider) {
  //some methods
}

function ServiceProvider() {
  var provider = this;

  provider.defaults = {
    maxItems: 10
  };

  provider.$get = function () {
    var someList = new someListService(provider.defaults.maxItems);

    return someList;
  };
}

}

Caractéristiques du fournisseur:

  1. Le fournisseur est la méthode la plus flexible pour créer des services dans Angular.
  2. Non seulement nous pouvons créer une usine configurable dynamiquement, mais au moment de l'utilisation de l'usine, avec la méthode du fournisseur, nous pourrions configurer l'usine une seule fois au démarrage de notre application entière.
  3. L'usine peut ensuite être utilisée dans toute l'application avec des paramètres personnalisés. En d'autres termes, nous pouvons configurer cette usine avant le démarrage de l'application. En fait, dans la documentation angulaire, il est mentionné que la méthode du fournisseur est ce qui est réellement exécuté en arrière-plan lorsque nous configurons nos services avec l'une .serviceou l' autre des .factoryméthodes.
  4. La $getest une fonction directement attachée à l'instance de fournisseur. Cette fonction est une fonction d' usine . En d'autres termes, c'est comme celui que nous utilisons pour fournir à la .factoryméthode. Dans cette fonction, nous créons notre propre service. Cette $getpropriété, qui est une fonction, est ce qui fait du fournisseur un fournisseur . AngularJS s'attend à ce que le fournisseur ait une propriété $ get dont la valeur est une fonction qu'Angular traitera comme une fonction d'usine. Mais ce qui rend cette configuration de fournisseur très spéciale, c'est le fait que nous pouvons fournir un configobjet à l'intérieur du fournisseur de services, et cela vient généralement avec des valeurs par défaut que nous pouvons remplacer plus tard à l'étape, où nous pouvons configurer l'application entière.
Pritam Banerjee
la source
7

Usine: l'usine dans laquelle vous créez un objet à l'intérieur de l'usine et le renvoyez.
service: le service dont vous venez de disposer d'une fonction standard qui utilise le mot-clé this pour définir la fonction.
provider: Le fournisseur il y a un $ get vous définissez et il peut être utilisé pour obtenir l'objet qui retourne les données.

Mohanrajan
la source
7

Essentiellement, le fournisseur, l'usine et le service sont tous des services. Une Factory est un cas particulier de Service lorsque vous n'avez besoin que d'une fonction $ get (), vous permettant de l'écrire avec moins de code.

Les principales différences entre les services, les usines et les fournisseurs sont leurs complexités. Les services sont la forme la plus simple, les usines sont un peu plus robustes et les fournisseurs sont configurables au moment de l'exécution.

Voici un résumé de l'utilisation de chacun:

Usine : La valeur que vous fournissez doit être calculée sur la base d'autres données.

Service : vous renvoyez un objet avec des méthodes.

Fournisseur : Vous voulez pouvoir configurer, pendant la phase de configuration, l'objet qui va être créé avant sa création. Utilisez le fournisseur principalement dans la configuration de l'application, avant que l'application ne soit complètement initialisée.

eGhoul
la source
euh. Value, Factory, Service et Constant - ne sont que du sucre syntaxique en plus d'une recette de fournisseur. Angularjs docs - fournisseurs
Sudarshan_SMD
oui je suis d'accord, maintenant avec angular 4 nous n'avons plus ce mal de tête
eGhoul
4

Les services sont des objets singleton qui sont créés lorsque cela est nécessaire et qui ne sont jamais nettoyés jusqu'à la fin du cycle de vie de l'application (lorsque le navigateur est fermé). Les contrôleurs sont détruits et nettoyés lorsqu'ils ne sont plus nécessaires.

2.La façon la plus simple de créer un service est d'utiliser la méthode factory (). La méthode factory () nous permet de définir un service en renvoyant un objet qui contient des fonctions de service et des données de service. La fonction de définition de service est l'endroit où nous plaçons nos services injectables, tels que $ http et $ q. Ex:

angular.module('myApp.services')
.factory('User', function($http) { // injectables go here
var backendUrl = "http://localhost:3000"; var service = {
    // our factory definition
user: {},
setName: function(newName) {
      service.user['name'] = newName;
    },
setEmail: function(newEmail) { service.user['email'] = newEmail;
},
save: function() {
return $http.post(backendUrl + '/users', { user: service.user
}); }
};
return service; });

Utiliser l'usine () dans notre application

Il est facile d'utiliser l'usine dans notre application car nous pouvons simplement l'injecter là où nous en avons besoin au moment de l'exécution.

angular.module('myApp')
.controller('MainController', function($scope, User) {
  $scope.saveUser = User.save;
});
  1. La méthode service (), d'autre part, nous permet de créer un service en définissant une fonction constructeur. Nous pouvons utiliser un objet prototypique pour définir notre service, au lieu d'un objet javascript brut. Semblable à la méthode factory (), nous allons également définir les injectables dans la définition de la fonction.
  2. La façon la plus simple de créer un service consiste à utiliser la méthode provide (). C'est le seul moyen de créer un service que nous pouvons configurer à l'aide de la fonction .config (). Contrairement aux méthodes précédentes, nous allons placer les injectables dans une définition de fonction définie this. $ Get ().
Shankar Gangadhar
la source
-3

Le sucre syntaxique est la différence . Seul le fournisseur est nécessaire. Ou en d'autres termes, seul le fournisseur est le véritable angulaire, tous les autres sont dérivés (pour réduire le code). Il existe également une version simple, appelée Value () qui ne renvoie que la valeur, sans calcul ni fonction. Même la valeur est dérivée du fournisseur!

Alors pourquoi de telles complications, pourquoi ne pouvons-nous pas simplement utiliser le fournisseur et oublier tout le reste? Il est censé nous aider à écrire du code facilement et à mieux communiquer. Et la réponse toungue-in-cheek serait, plus il devient complexe, meilleure sera la vente d'un cadre.


  • Un fournisseur qui peut renvoyer value = Value
  • Un fournisseur qui peut simplement instancier et retourner = Factory (+ Value)
  • Un fournisseur qui peut instancier + faire quelque chose = Service (+ Factory, + Value)
  • Un fournisseur = doit contenir une propriété appelée $ get (+ Factory, + Service, + Value)

L'injection angulaire nous donne le premier indice pour arriver à cette conclusion.

"$ injector est utilisé pour récupérer les instances d'objets définies par le fournisseur " pas le service, pas la fabrique mais le fournisseur.

Et une meilleure réponse serait la suivante: "Un service angulaire est créé par une fabrique de services. Ces fabriques de services sont des fonctions qui, à leur tour, sont créées par un fournisseur de services. Les fournisseurs de services sont des fonctions de constructeur. Lorsqu'ils sont instanciés, ils doivent contenir une propriété appelé $ get, qui contient la fonction de fabrique de services. "

Donc fournisseur principal et injecteur et tout se mettra en place :). Et cela devient intéressant dans Typescript lorsque $ get peut être implémenté dans un fournisseur en héritant de IServiceProvider.

Nuages ​​bleus
la source