Hva er forskjellen mellom en Service
, Provider
og Factory
i AngularJS?
Fra AngularJS-postlisten fikk jeg en fantastisk tråd som forklarer service vs fabrikk vs leverandør og deres injeksjonsbruk. Kompilerer svarene:
Syntaks: module.service( 'serviceName', function );
Resultat: Når du deklarerer serviceName som et injiserbart argument vil du få en forekomst av funksjonen. Med andre ord new FunctionYouPassedToService()
.
Syntaks: module.factory( 'factoryName', function );
Resultat: Når du deklarerer factoryName som et injiserbart argument, vil du få verdien som returneres ved å påkalle funksjonsreferansen som sendes til module.factory.
Syntaks: module.provider( 'providerName', function );
Resultat: Når du deklarerer providerName som et injiserbart argument vil du få (new ProviderFunction()).$get()
. Konstruktørfunksjonen instansieres før $get-metoden kalles - ProviderFunction
er funksjonsreferansen som sendes til module.provider.
Providers har den fordelen at de kan konfigureres under modulkonfigurasjonsfasen.
Se her for den medfølgende koden.
Her er en god ytterligere forklaring av Misko:
provide.value('a', 123);
function Controller(a) {
expect(a).toEqual(123);
}
I dette tilfellet returnerer injektoren ganske enkelt verdien som den er. Men hva om du vil beregne verdien? Bruk da en fabrikk
provide.factory('b', function(a) {
return a*2;
});
function Controller(b) {
expect(b).toEqual(246);
}
Så factory
er en funksjon som er ansvarlig for å skape verdien. Legg merke til at factory-funksjonen kan be om andre avhengigheter.
Men hva om du vil være mer OO og ha en klasse som heter Greeter?
function Greeter(a) {
this.greet = function() {
return 'Hello ' + a;
}
}
For å instansiere må du da skrive
provide.factory('greeter', function(a) {
return new Greeter(a);
});
Deretter kan vi be om 'greeter' i kontrolleren slik
function Controller(greeter) {
expect(greeter instanceof Greeter).toBe(true);
expect(greeter.greet()).toEqual('Hello 123');
}
Men det er altfor omstendelig. En kortere måte å skrive dette på ville være provider.service('greeter', Greeter);
Men hva om vi ønsket å konfigurere Greeter
klasse før injeksjon? Da 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);
};
});
Da kan vi gjøre dette:
angular.module('abc', []).config(function(greeter2Provider) {
greeter2Provider.setSalutation('Halo');
});
function Controller(greeter2) {
expect(greeter2.greet()).toEqual('Halo 123');
}
Som en side bemerkning er service
, factory
og value
alle avledet fra 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 la merke til noe interessant da jeg lekte med leverandører.
Synligheten av injeksjoner er annerledes for leverandører enn for tjenester og fabrikker. Hvis du erklærer en AngularJS "konstant" (for eksempel myApp.constant('a', 'Robert');
), kan du injisere den i tjenester, fabrikker og leverandører.
Men hvis du erklærer en AngularJS "verdi" (for eksempel., myApp.value('b', {name: 'Jones'});
), kan du injisere den i tjenester og fabrikker, men IKKE i leverandøropprettingsfunksjonen. Du kan imidlertid injisere den i $get
-funksjonen som du definerer for leverandøren din. Dette er nevnt i AngularJS-dokumentasjonen, men det er lett å overse. Du finner det på %provide-siden i avsnittene om verdi- og konstantmetoder.
<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>