mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-03-17 15:40:22 +00:00
166 lines
6.1 KiB
Text
166 lines
6.1 KiB
Text
@workInProgress
|
|
@ngdoc overview
|
|
@name angular.service
|
|
|
|
@description
|
|
# Overview
|
|
Services are substituable objects, which are wired together using dependency injection (DI).
|
|
Each service could have dependencies (other services), which are passed in constructor.
|
|
Because JS is dynamicaly typed language, dependency injection can not use static types
|
|
to identify these dependencies, so each service must explicitely define its dependencies.
|
|
This is done by `$inject` property.
|
|
|
|
|
|
# Built-in services
|
|
angular provides a set of services for common operations. These services can be overriden by custom
|
|
services if needed.
|
|
|
|
Like other core angular variables and identifiers, the built-in services always start with `$`.
|
|
|
|
* `{@link angular.service.$browser $browser}`
|
|
* `{@link angular.service.$window $window}`
|
|
* `{@link angular.service.$document $document}`
|
|
* `{@link angular.service.$location $location}`
|
|
* `{@link angular.service.$log $log}`
|
|
* `{@link angular.service.$exceptionHandler $exceptionHandler}`
|
|
* `{@link angular.service.$hover $hover}`
|
|
* `{@link angular.service.$invalidWidgets $invalidWidgets}`
|
|
* `{@link angular.service.$route $route}`
|
|
* `{@link angular.service.$xhr $xhr}`
|
|
* `{@link angular.service.$xhr.error $xhr.error}`
|
|
* `{@link angular.service.$xhr.bulk $xhr.bulk}`
|
|
* `{@link angular.service.$xhr.cache $xhr.cache}`
|
|
* `{@link angular.service.$resource $resource}`
|
|
* `{@link angular.service.$cookies $cookies}`
|
|
* `{@link angular.service.$cookieStore $cookieStore}`
|
|
|
|
# Writing your own custom services
|
|
angular provides only set of basic services, so for any nontrivial application it will be necessary
|
|
to write one or more custom services. To do so, a factory function that creates a services needs to
|
|
be registered with angular's dependency injector. This factory function must return an object - the
|
|
service (it is not called with the `new` operator).
|
|
|
|
**angular.service** accepts three parameters:
|
|
|
|
- `{string} name` - Name of the service.
|
|
- `{function()} factory` - Factory function (called just once by DI).
|
|
- `{Object} config` - Configuration object with following properties:
|
|
- `$inject` - {Array.<string>} - Array of service ids that this service depends on. These
|
|
services will be passed as arguments into the factory function in the same order as specified
|
|
in the `$inject` array. Defaults to `[]`.
|
|
- `$eager` - {boolean} - If true, the service factory will be called and thus, the service will
|
|
be instantiated when angular boots. If false, service will be lazily instantiated when it is
|
|
first requested during instantiation of a dependant. Defaults to `false`.
|
|
|
|
The `this` of the factory function is bound to the root scope of the angular application.
|
|
|
|
angular enables services to participate in dependency injection (DI) by registering themselves with
|
|
angular's DI system (injector) under a `name` (id) as well as by declaring dependencies which need
|
|
to be provided for the factory function of the registered service. The ability to swap dependencies
|
|
for mocks/stubs/dummies in tests allows for services to be highly testable.
|
|
|
|
Here is an example of very simple service. This service requires $window service (it's
|
|
passed as a parameter to factory function) and it's just a function.
|
|
|
|
This service simple stores all notifications and after third one, it displays all of them by
|
|
window alert.
|
|
<pre>
|
|
angular.service('notify', function(win) {
|
|
var msgs = [];
|
|
return function(msg) {
|
|
msgs.push(msg);
|
|
if (msgs.length == 3) {
|
|
win.alert(msgs.join("\n"));
|
|
msgs = [];
|
|
}
|
|
};
|
|
}, {$inject: ['$window']});
|
|
</pre>
|
|
|
|
And here is a unit test for this service. We use Jasmine spy (mock) instead of real browser's alert.
|
|
<pre>
|
|
var mock, notify;
|
|
|
|
beforeEach(function() {
|
|
mock = {alert: jasmine.createSpy()};
|
|
notify = angular.service('notify')(mock);
|
|
});
|
|
|
|
it('should not alert first two notifications', function() {
|
|
notify('one');
|
|
notify('two');
|
|
expect(mock.alert).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('should alert all after third notification', function() {
|
|
notify('one');
|
|
notify('two');
|
|
notify('three');
|
|
expect(mock.alert).toHaveBeenCalledWith("one\ntwo\nthree");
|
|
});
|
|
|
|
it('should clear messages after alert', function() {
|
|
notify('one');
|
|
notify('two');
|
|
notify('third');
|
|
notify('more');
|
|
notify('two');
|
|
notify('third');
|
|
expect(mock.alert.callCount).toEqual(2);
|
|
expect(mock.alert.mostRecentCall.args).toEqual(["more\ntwo\nthird"]);
|
|
});
|
|
</pre>
|
|
|
|
# Injecting services into controllers
|
|
Using services as dependencies for controllers is very similar to using them as dependencies for
|
|
another service.
|
|
|
|
JavaScript is dynamic language, so DI is not able to figure out which services to inject by
|
|
static types (like in static typed languages). Therefore you must specify the service name
|
|
by the `$inject` property - it's an array that contains strings with names of services to be
|
|
injected. The name must match the id that service has been registered as with angular.
|
|
The order of the services in the array matters, because this order will be used when calling
|
|
the factory function with injected parameters. The names of parameters in factory function
|
|
don't matter, but by convention they match the service ids.
|
|
<pre>
|
|
function myController($loc, $log) {
|
|
this.firstMethod = function() {
|
|
// use $location service
|
|
$loc.setHash();
|
|
};
|
|
this.secondMethod = function() {
|
|
// use $log service
|
|
$log.info('...');
|
|
};
|
|
}
|
|
// which services to inject ?
|
|
myController.$inject = ['$location', '$log'];
|
|
</pre>
|
|
|
|
@example
|
|
<script type="text/javascript">
|
|
angular.service('notify', function(win) {
|
|
var msgs = [];
|
|
return function(msg) {
|
|
msgs.push(msg);
|
|
if (msgs.length == 3) {
|
|
win.alert(msgs.join("\n"));
|
|
msgs = [];
|
|
}
|
|
};
|
|
}, {$inject: ['$window']});
|
|
|
|
function myController(notifyService) {
|
|
this.callNotify = function(msg) {
|
|
notifyService(msg);
|
|
};
|
|
}
|
|
|
|
myController.$inject = ['notify'];
|
|
</script>
|
|
|
|
<div ng:controller="myController">
|
|
<p>Let's try this simple notify service, injected into the controller...</p>
|
|
<input ng:init="message='test'" type="text" name="message" />
|
|
<button ng:click="callNotify(message);">NOTIFY</button>
|
|
</div>
|