diff options
| author | Brian Ford | 2014-03-03 12:30:33 -0800 | 
|---|---|---|
| committer | Brian Ford | 2014-03-03 12:30:33 -0800 | 
| commit | 220e7bf2d448fd80d98d5c2f3cfac3902433df8f (patch) | |
| tree | 9b46c834e26a813705a6c39a418f7cef51f6e45b /docs/content/guide/services.ngdoc | |
| parent | 8d6eed21d219e459331a9f08fd46f8c67a9553da (diff) | |
| download | angular.js-220e7bf2d448fd80d98d5c2f3cfac3902433df8f.tar.bz2 | |
docs(guide/services): rewrite services documentation
Diffstat (limited to 'docs/content/guide/services.ngdoc')
| -rw-r--r-- | docs/content/guide/services.ngdoc | 297 | 
1 files changed, 297 insertions, 0 deletions
| diff --git a/docs/content/guide/services.ngdoc b/docs/content/guide/services.ngdoc new file mode 100644 index 00000000..d92be142 --- /dev/null +++ b/docs/content/guide/services.ngdoc @@ -0,0 +1,297 @@ +@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 is 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 `$` +(i.e. `$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-error"> +**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 and array notation to declare their dependencies. +* That 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 is 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} | 
