Wat zijn de verschillen tussen een Service
, Provider
en Factory
in AngularJS?
Van de AngularJS mailing list kreeg ik een geweldige thread die service vs factory vs provider en hun injectie gebruik uitlegt. Compileren van de antwoorden:
Syntax: module.service( 'serviceName', function );
Resultaat: Als je serviceName als injectable argument declareert krijg je een instantie van de functie. Met andere woorden new FunctionYouPassedToService()
.
Syntax: module.factory( 'factoryName', function );
Resultaat: Wanneer je factoryName als een injecteerbaar argument declareert, krijg je de waarde die wordt geretourneerd door het aanroepen van de functieverwijzing die aan module.factory is doorgegeven.
Syntaxis: module.provider( 'providerName', function );
Resultaat: Bij het declareren van providerName als een injecteerbaar argument krijg je (new ProviderFunction()).$get()
. De constructor functie wordt geïnstantieerd voordat de $get methode wordt aangeroepen - ProviderFunction
is de functie referentie die wordt doorgegeven aan module.provider.
Providers hebben het voordeel dat ze geconfigureerd kunnen worden tijdens de module configuratie fase.
Zie hier voor de meegeleverde code.
Hier's een geweldige verdere uitleg door Misko:
provide.value('a', 123);
function Controller(a) {
expect(a).toEqual(123);
}
In dit geval geeft de injector gewoon de waarde terug zoals die is. Maar wat als je de waarde wilt berekenen? Gebruik dan een fabriek
provide.factory('b', function(a) {
return a*2;
});
function Controller(b) {
expect(b).toEqual(246);
}
Dus factory
is een functie die verantwoordelijk is voor het creëren van de waarde. Merk op dat de factory functie kan vragen om andere afhankelijkheden.
Maar wat als je meer OO wilt zijn en een klasse wilt hebben met de naam Greeter?
function Greeter(a) {
this.greet = function() {
return 'Hello ' + a;
}
}
Dan zou je om te instantiëren moeten schrijven
provide.factory('greeter', function(a) {
return new Greeter(a);
});
Dan zouden we kunnen vragen om 'greeter' in controller zoals dit
function Controller(greeter) {
expect(greeter instanceof Greeter).toBe(true);
expect(greeter.greet()).toEqual('Hello 123');
}
Maar dat is veel te wijd. Een kortere manier om dit te schrijven zou zijn provider.service('greeter', Greeter);
Maar wat als we de Greeter
klasse willen configureren voor de injectie? Dan zouden we kunnen schrijven
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);
};
});
Dan kunnen we dit doen:
angular.module('abc', []).config(function(greeter2Provider) {
greeter2Provider.setSalutation('Halo');
});
function Controller(greeter2) {
expect(greeter2.greet()).toEqual('Halo 123');
}
Terzijde: service
, factory
, en value
zijn allemaal afgeleid van 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>
Ik merkte iets interessants op toen ik met providers speelde.
Zichtbaarheid van injectables is anders voor providers dan het is voor services en factories. Als je een AngularJS "constant" declareert (bijvoorbeeld, myApp.constant('a', 'Robert');
), kun je het injecteren in services, factories, en providers.
Maar als je een AngularJS "value" declareert (bijvoorbeeld, myApp.value('b', {name: 'Jones'});
), dan kun je het injecteren in services en factories, maar NIET in de provider-creërende functie. Je kunt het echter wel injecteren in de $get
functie die je definieert voor je provider. Dit wordt vermeld in de AngularJS documentatie, maar het'is gemakkelijk te missen. Je kunt het vinden op de %provide pagina in de secties over de value en constant methods.
<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>