Was sind die Unterschiede zwischen einem Service
, Provider
und Factory
in AngularJS?
Von der AngularJS-Mailingliste erhielt ich einen erstaunlichen Thread, der Service vs. Factory vs. Provider und deren Injektionsverwendung erklärt. Kompilieren Sie die Antworten:
Syntax: module.service( 'serviceName', function );
Ergebnis: Wenn Sie serviceName als injizierbares Argument deklarieren, werden Sie mit einer Instanz der Funktion versorgt. Mit anderen Worten neue FunktionYouPassedToService()
.
*Fabriken
Syntax: module.factory( 'factoryName', function );
Ergebnis: Wenn Sie factoryName als injizierbares Argument deklarieren, erhalten Sie den Wert, der durch den Aufruf der an module.factory übergebenen Funktionsreferenz zurückgegeben wird.
Syntax: module.provider( 'providerName', function );
Ergebnis: Wenn Sie providerName als injizierbares Argument deklarieren, werden Sie mit (new ProviderFunction()).$get()
versorgt. Die Konstruktorfunktion wird instanziiert, bevor die $get-Methode aufgerufen wird - ProviderFunction
ist die Funktionsreferenz, die an module.provider übergeben wird.
Provider haben den Vorteil, dass sie während der Konfigurationsphase des Moduls konfiguriert werden können.
Siehe hier für den bereitgestellten Code.
Hier's eine große weitere Erklärung von Misko:
provide.value('a', 123);
function Controller(a) {
expect(a).toEqual(123);
}
In diesem Fall gibt der Injektor den Wert einfach so zurück, wie er ist. Aber was ist, wenn Sie den Wert berechnen wollen? Dann verwenden Sie eine Fabrik
provide.factory('b', function(a) {
return a*2;
});
function Controller(b) {
expect(b).toEqual(246);
}
Fabrik" ist also eine Funktion, die für die Erstellung des Wertes verantwortlich ist. Beachten Sie, dass die Fabrikfunktion nach anderen Abhängigkeiten fragen kann.
Was aber, wenn Sie mehr OO sein wollen und eine Klasse namens Greeter haben?
function Greeter(a) {
this.greet = function() {
return 'Hello ' + a;
}
}
Dann müssten Sie zum Instanziieren schreiben
provide.factory('greeter', function(a) {
return new Greeter(a);
});
Dann könnten wir nach 'greeter' im Controller wie folgt fragen
function Controller(greeter) {
expect(greeter instanceof Greeter).toBe(true);
expect(greeter.greet()).toEqual('Hello 123');
}
Aber das ist viel zu wortreich. Ein kürzerer Weg, dies zu schreiben, wäre provider.service('greeter', Greeter);
Was aber, wenn wir die Klasse Greeter
vor der Injektion konfigurieren wollten? Dann könnten wir schreiben
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);
};
});
Dann können wir dies tun:
angular.module('abc', []).config(function(greeter2Provider) {
greeter2Provider.setSalutation('Halo');
});
function Controller(greeter2) {
expect(greeter2.greet()).toEqual('Halo 123');
}
Nebenbei bemerkt, service
, factory
und value
sind alle von provider abgeleitet.
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>
Beim Herumspielen mit Anbietern ist mir etwas Interessantes aufgefallen.
Die Sichtbarkeit von Injectables ist bei Providern anders als bei Services und Factories. Wenn Sie eine AngularJS "Konstante" deklarieren (z. B. myApp.constant('a', 'Robert');
), können Sie sie in Dienste, Factories und Anbieter injizieren.
Wenn Sie jedoch einen AngularJS "Wert" deklarieren (z. B. myApp.value('b', {Name: 'Jones'});
), können Sie ihn in Dienste und Factories injizieren, aber NICHT in die Funktion zur Erstellung des Providers. Sie können es jedoch in die Funktion "$get" injizieren, die Sie für Ihren Anbieter definieren. Dies wird in der AngularJS-Dokumentation erwähnt, ist aber leicht zu übersehen. Sie finden es auf der Seite %provide in den Abschnitten über die Methoden value und constant.
<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>