Hvad er forskellene mellem en Service
, Provider
og Factory
i AngularJS?
Fra AngularJS-mailinglisten fik jeg en fantastisk tråd, der forklarer service vs. factory vs. provider og deres brug af injektion. Jeg samler svarene:
Syntax: Funktion: module.service( 'serviceName', function );
Resultat: Resultat: Når du erklærer serviceName som et injicerbart argument får du en instans af funktionen. Med andre ord new FunctionYouPassedToService()
.
Syntaks: Funktion: module.factory( 'factoryName', function );
Resultat: Når du erklærer factoryName som et injicerbart argument, får du den værdi, der returneres ved at påberåbe den funktionsreference, der er overgivet til module.factory.
Syntaks: Modul.provider( 'providerName', function );<br/> Resultat: Resultat: Når providerName deklareres som et injicerbart argument, får du **(new ProviderFunction()).$get()
. Konstruktorfunktionen instantieres, før $get-metoden kaldes - ProviderFunction
er den funktionsreference, der er overgivet til module.provider.
Providers har den fordel, at de kan konfigureres under modulkonfigurationsfasen.
Se her for den leverede kode.
Her'er en god yderligere forklaring af Misko:
provide.value('a', 123);
function Controller(a) {
expect(a).toEqual(123);
}
I dette tilfælde returnerer injektoren simpelthen værdien som den er. Men hvad nu hvis du vil beregne værdien? Så skal du bruge en fabrik
provide.factory('b', function(a) {
return a*2;
});
function Controller(b) {
expect(b).toEqual(246);
}
Så factory
er en funktion, som er ansvarlig for at skabe værdien. Bemærk, at fabriksfunktionen kan bede om andre afhængigheder.
Men hvad nu hvis du vil være mere OO og have en klasse der hedder Greeter?
function Greeter(a) {
this.greet = function() {
return 'Hello ' + a;
}
}
Så skal du for at instantiere skrive
provide.factory('greeter', function(a) {
return new Greeter(a);
});
Så kunne vi bede om 'greeter' i controller på denne måde
function Controller(greeter) {
expect(greeter instanceof Greeter).toBe(true);
expect(greeter.greet()).toEqual('Hello 123');
}
Men det er alt for mundret. En kortere måde at skrive det på ville være provider.service('greeter', Greeter);
Men hvad nu hvis vi ønsker at konfigurere Greeter
-klassen før injektionen? Så kunne vi skrive
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);
};
});
Så kan vi gøre dette:
angular.module('abc', []).config(function(greeter2Provider) {
greeter2Provider.setSalutation('Halo');
});
function Controller(greeter2) {
expect(greeter2.greet()).toEqual('Halo 123');
}
Som en sidebemærkning er service
, factory
og value
alle afledt af provider.
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;
});
};
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>
Jeg bemærkede noget interessant, da jeg legede med udbydere.
Synligheden af injicerbare elementer er anderledes for udbydere end for tjenester og fabrikker. Hvis du erklærer en AngularJS "konstant" (f.eks. myApp.constant('a', 'Robert');
), kan du injicere den i tjenester, fabrikker og udbydere.
Men hvis du erklærer en AngularJS "value" (f.eks. myApp.value('b', {name: 'Jones'});
), kan du injicere den i tjenester og fabrikker, men IKKE i den funktion, der opretter en provider. Du kan dog injicere den i funktionen $get
, som du definerer for din provider. Dette er nævnt i AngularJS-dokumentationen, men det er let at overse. Du kan finde det på %provide-siden i afsnittene om value- og konstantmetoderne.
<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>