mirror of
https://github.com/zhigang1992/angular.js.git
synced 2026-04-02 09:00:34 +08:00
298 lines
8.7 KiB
Plaintext
298 lines
8.7 KiB
Plaintext
@ngdoc overview
|
||
@name Services
|
||
@description
|
||
|
||
# Services
|
||
|
||
Angular services are substitutable objects that are wired together using {@link di dependency
|
||
injection (DI)}. You can use services to organize and share code across your app.
|
||
|
||
Angular services are:
|
||
|
||
* Lazily instantiated – Angular only instantiates a service when an application component depends
|
||
on it.
|
||
* Singletons – Each component dependent on a service gets a reference to the single instance
|
||
generated by the service factory.
|
||
|
||
Angular offers several useful services (like {@link ng.$http `$http`}), but for most applications
|
||
you'll also want to {@link services#creating-services create your own}.
|
||
|
||
<div class="alert alert-info">
|
||
**Note:** Like other core Angular identifiers built-in services always start with `$`
|
||
(e.g. `$http`).
|
||
</div>
|
||
|
||
|
||
## Using a Service
|
||
|
||
To use an Angular service, you add it as a dependency for the component (controller, service,
|
||
filter or directive) that depends on the service. Angular's {@link di dependency injection}
|
||
subsystem takes care of the rest.
|
||
|
||
<example module="myServiceModule">
|
||
<file name="index.html">
|
||
<div id="simple" ng-controller="MyController">
|
||
<p>Let's try this simple notify service, injected into the controller...</p>
|
||
<input ng-init="message='test'" ng-model="message" >
|
||
<button ng-click="callNotify(message);">NOTIFY</button>
|
||
<p>(you have to click 3 times to see an alert)</p>
|
||
</div>
|
||
</file>
|
||
|
||
<file name="script.js">
|
||
angular.
|
||
module('myServiceModule', []).
|
||
controller('MyController', ['$scope','notify', function ($scope, notify) {
|
||
$scope.callNotify = function(msg) {
|
||
notify(msg);
|
||
};
|
||
}]).
|
||
factory('notify', ['$window', function(win) {
|
||
var msgs = [];
|
||
return function(msg) {
|
||
msgs.push(msg);
|
||
if (msgs.length == 3) {
|
||
win.alert(msgs.join("\n"));
|
||
msgs = [];
|
||
}
|
||
};
|
||
}]);
|
||
</file>
|
||
|
||
<file name="protractor.js" type="protractor">
|
||
it('should test service', function() {
|
||
expect(element(by.id('simple')).element(by.model('message')).getAttribute('value'))
|
||
.toEqual('test');
|
||
});
|
||
</file>
|
||
</example>
|
||
|
||
<div class="alert alert-info">
|
||
**Note:** Angular uses
|
||
[**constructor injection**](http://misko.hevery.com/2009/02/19/constructor-injection-vs-setter-injection/).
|
||
</div>
|
||
|
||
### Explicit Dependency Injection
|
||
|
||
A component should explicitly define its dependencies using one of the {@link di injection
|
||
annotation} methods:
|
||
|
||
1. Inline array injection annotation (preferred):
|
||
```js
|
||
myModule.controller('MyController', ['$location', function($location) { ... }]);
|
||
```
|
||
|
||
2. `$inject` property:
|
||
```js
|
||
var MyController = function($location) { ... };
|
||
MyController.$inject = ['$location'];
|
||
myModule.controller('MyController', MyController);
|
||
```
|
||
|
||
<div class="alert alert-success">
|
||
**Best Practice:** Use the array annotation shown above.
|
||
</div>
|
||
|
||
### Implicit Dependency Injection
|
||
|
||
Even if you don't annotate your dependencies, Angular's DI can determine the dependency from the
|
||
name of the parameter. Let's rewrite the above example to show the use of this implicit dependency
|
||
injection of `$window`, `$scope`, and our `notify` service:
|
||
|
||
<example module="myServiceModuleDI">
|
||
<file name="index.html">
|
||
<div id="implicit" ng-controller="MyController">
|
||
<p>Let's try the notify service, that is implicitly injected into the controller...</p>
|
||
<input ng-init="message='test'" ng-model="message">
|
||
<button ng-click="callNotify(message);">NOTIFY</button>
|
||
<p>(you have to click 3 times to see an alert)</p>
|
||
</div>
|
||
</file>
|
||
|
||
<file name="script.js">
|
||
angular.module('myServiceModuleDI', []).
|
||
factory('notify', function($window) {
|
||
var msgs = [];
|
||
return function(msg) {
|
||
msgs.push(msg);
|
||
if (msgs.length == 3) {
|
||
$window.alert(msgs.join("\n"));
|
||
msgs = [];
|
||
}
|
||
};
|
||
}).
|
||
controller('MyController', function($scope, notify) {
|
||
$scope.callNotify = function(msg) {
|
||
notify(msg);
|
||
};
|
||
});
|
||
</file>
|
||
</example>
|
||
|
||
<div class="alert alert-danger">
|
||
**Careful:** If you plan to [minify](http://en.wikipedia.org/wiki/Minification_(programming) your code,
|
||
your variable names will get renamed unless you use one of the annotation techniques above.
|
||
</div>
|
||
|
||
|
||
## Creating Services
|
||
|
||
Application developers are free to define their own services by registering the service's name and
|
||
**service factory function**, with an Angular module.
|
||
|
||
The **service factory function** generates the single object or function that represents the
|
||
service to the rest of the application. The object or function returned by the service is
|
||
injected into any component (controller, service, filter or directive) that specifies a dependency
|
||
on the service.
|
||
|
||
### Registering Services
|
||
|
||
Services are registered to modules via the {@link angular.Module Module API}.
|
||
Typically you use the {@link angular.module Module#factory} API to register a service:
|
||
|
||
```javascript
|
||
var myModule = angular.module('myModule', []);
|
||
myModule.factory('serviceId', function() {
|
||
var shinyNewServiceInstance;
|
||
//factory function body that constructs shinyNewServiceInstance
|
||
return shinyNewServiceInstance;
|
||
});
|
||
```
|
||
|
||
Note that you are not registering a **service instance**, but rather a **factory function** that
|
||
will create this instance when called.
|
||
|
||
### Dependencies
|
||
|
||
Services can have their own dependencies. Just like declaring dependencies in a controller, you
|
||
declare dependencies by specifying them in the service's factory function signature.
|
||
|
||
The example module below has two services, each with various dependencies:
|
||
|
||
```js
|
||
var batchModule = angular.module('batchModule', []);
|
||
|
||
/**
|
||
* The `batchLog` service allows for messages to be queued in memory and flushed
|
||
* to the console.log every 50 seconds.
|
||
*
|
||
* @param {*} message Message to be logged.
|
||
*/
|
||
batchModule.factory('batchLog', ['$interval', '$log', function($interval, $log) {
|
||
var messageQueue = [];
|
||
|
||
function log() {
|
||
if (messageQueue.length) {
|
||
$log.log('batchLog messages: ', messageQueue);
|
||
messageQueue = [];
|
||
}
|
||
}
|
||
|
||
// start periodic checking
|
||
$interval(log, 50000);
|
||
|
||
return function(message) {
|
||
messageQueue.push(message);
|
||
}
|
||
}]);
|
||
|
||
/**
|
||
* `routeTemplateMonitor` monitors each `$route` change and logs the current
|
||
* template via the `batchLog` service.
|
||
*/
|
||
batchModule.factory('routeTemplateMonitor', ['$route', 'batchLog', '$rootScope',
|
||
function($route, batchLog, $rootScope) {
|
||
$rootScope.$on('$routeChangeSuccess', function() {
|
||
batchLog($route.current ? $route.current.template : null);
|
||
});
|
||
}]);
|
||
|
||
```
|
||
|
||
In the example, note that:
|
||
|
||
* The `batchLog` service depends on the built-in {@link ng.$interval `$interval`} and
|
||
{@link ng.$log `$log`} services.
|
||
* The `routeTemplateMonitor` service depends on the built-in {@link ngRoute.$route `$route`}
|
||
service and our custom `batchLog` service.
|
||
* Both services use the array notation to declare their dependencies.
|
||
* The order of identifiers in the array is the same as the order of argument
|
||
names in the factory function.
|
||
|
||
### Registering a Service with `$provide`
|
||
|
||
You can also register services via the {@link auto.$provide `$provide`} service inside of a
|
||
module's `config` function:
|
||
|
||
```javascript
|
||
angular.module('myModule', []).config(function($provide) {
|
||
$provide.factory('serviceId', function() {
|
||
var shinyNewServiceInstance;
|
||
//factory function body that constructs shinyNewServiceInstance
|
||
return shinyNewServiceInstance;
|
||
});
|
||
});
|
||
```
|
||
|
||
This technique is often used in unit tests to mock out a service's dependencies.
|
||
|
||
|
||
## Unit Testing
|
||
|
||
The following is a unit test for the `notify` service from the {@link services#creating-services
|
||
Creating Angular Services} example above. The unit test example uses a Jasmine spy (mock) instead
|
||
of a real browser alert.
|
||
|
||
```js
|
||
var mock, notify;
|
||
|
||
beforeEach(function() {
|
||
mock = {alert: jasmine.createSpy()};
|
||
|
||
module(function($provide) {
|
||
$provide.value('$window', mock);
|
||
});
|
||
|
||
inject(function($injector) {
|
||
notify = $injector.get('notify');
|
||
});
|
||
});
|
||
|
||
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"]);
|
||
});
|
||
```
|
||
|
||
|
||
## Related Topics
|
||
|
||
* {@link guide/di Dependency Injection in AngularJS}
|
||
|
||
## Related API
|
||
|
||
* {@link ./ng Angular Service API}
|
||
* {@link angular.injector Injector API}
|