aboutsummaryrefslogtreecommitdiffstats
path: root/docs/content/guide/providers.ngdoc
diff options
context:
space:
mode:
authorBrian Ford2013-11-06 08:26:45 -0800
committerBrian Ford2013-11-06 08:47:06 -0800
commit5614fd283a22d130cf5e09dade67be3f3c3a97c2 (patch)
tree3edbb5131c117e129527129143d5c1c943ca196a /docs/content/guide/providers.ngdoc
parentda344daa4023556f8abbef6d8ad87a16362b5861 (diff)
downloadangular.js-5614fd283a22d130cf5e09dade67be3f3c3a97c2.tar.bz2
docs(guide/providers): add providers documentation
Diffstat (limited to 'docs/content/guide/providers.ngdoc')
-rw-r--r--docs/content/guide/providers.ngdoc425
1 files changed, 425 insertions, 0 deletions
diff --git a/docs/content/guide/providers.ngdoc b/docs/content/guide/providers.ngdoc
new file mode 100644
index 00000000..adf13bd4
--- /dev/null
+++ b/docs/content/guide/providers.ngdoc
@@ -0,0 +1,425 @@
+@ngdoc overview
+@name Developer Guide: Providers
+@description
+
+# Providers
+
+Each web application you build is composed of objects that collaborate to get stuff done. These
+objects need to be instantiated and wired together for the app to work. In Angular apps most of
+these objects are instantiated and wired together automatically by the {@link api/AUTO.$injector
+injector service}.
+
+The injector creates two types of objects, **services** and **specialized objects**.
+
+Services are objects whose API is defined by the developer writing the service.
+
+Specialized objects conform to a specific Angular framework API. These objects are one of
+controllers, directives, filters or animations.
+
+The injector needs to know how to create these objects. You tell it by registering a "recipe" for
+creating your object with the injector. There are five recipe types.
+
+The most verbose, but also the most comprehensive one is a Provider recipe. The remaining four
+recipe types — Value, Factory, Service and Constant — are just syntactic sugar on top of a provider
+recipe.
+
+Let's take a look at the different scenarios for creating and using services via various recipe
+types. We'll start with the simplest case possible where various places in your code need a shared
+string and we'll accomplish this via Value recipe.
+
+
+## Note: A Word on Modules
+
+In order for the injector to know how to create and wire together all of these objects, it needs
+a registry of "recipes". Each recipe has an identifier of the object and the description of how to
+create this object.
+
+Each recipe belongs to an {@link api/angular.Module Angular module}. An Angular module is a bag
+that holds one or more recipes. And since manually keeping track of module dependencies is no fun,
+a module can contain information about dependencies on other modules as well.
+
+When an Angular application starts with a given application module, Angular creates a new instance
+of injector, which in turn creates a registry of recipes as a union of all recipes defined in the
+core "ng" module, application module and its dependencies. The injector then consults the recipe
+registry when it needs to create an object for your application.
+
+
+## Value Recipe
+
+Let's say that we want to have a very simple service called "clientId" that provides a string
+representing an authentication id used for some remote API. You would define it like this:
+
+```javascript
+var myApp = angular.module('myApp', []);
+myApp.value('clientId', 'a12345654321x');
+```
+
+Notice how we created an Angular module called `myApp`, and specified that this module definition
+contains a "recipe" for constructing the `clientId` service, which is a simple string in this case.
+
+And this is how you would display it via Angular's data-binding:
+
+
+```javascript
+myApp.controller('DemoController', ['clientId', function DemoController(clientId) {
+ this.clientId = clientId;
+}]);
+```
+
+```html
+<html ng-app="myApp">
+ <body ng-controller="DemoController as demo">
+ Client ID: {{demo.clientId}}
+ </body>
+</html>
+```
+
+In this example, we've used the Value recipe to define the value to provide when `DemoController`
+asks for the service with id "clientId".
+
+On to more complex examples!
+
+
+## Factory Recipe
+
+The Value recipe is very simple to write, but lacks some important features we often need when
+creating services. Let's now look at the Value recipe's more powerful sibling, the Factory.The
+Factory recipe adds the following abilities:
+
+* ability to use other services (have dependencies)
+* service initialization
+* delayed/lazy initialization
+
+The Factory recipe constructs a new service using a function with zero or more arguments (these
+are dependencies on other services). The return value of this function is the service instance
+created by this recipe.
+
+Note: All services in Angular are singletons. That means that the injector uses each recipe at most
+once to create the object. The injector then caches the reference for all future needs.
+
+Since Factory is more powerful version of Value recipe, you can construct the same service with it.
+Using our previous `clientId` Value recipe example, we can rewrite it as a Factory recipe like
+this:
+
+```javascript
+myApp.factory('clientId', function clientIdFactory() {
+ return 'a12345654321x';
+});
+```
+
+But given that the token is just a string literal, sticking with the Value recipe is still more
+appropriate as it makes the code easier to follow.
+
+Let's say, however, that we would also like create a service that computes a token used for
+authentication against a remote API. This token will be called 'apiToken' and will be computed
+based on the `clientId` value and a secret stored in browser's local storage:
+
+```javascript
+myApp.factory('apiToken', ['clientId', function apiTokenFactory(clientId) {
+ var encrypt = function(data1, data2) {
+ // NSA-proof encryption algorithm:
+ return (data1 + ':' + data2).toUpperCase();
+ };
+
+ var secret = window.localStorage.getItem('myApp.secret');
+ var apiToken = encrypt(clientId, secret);
+
+ return apiToken;
+}]);
+```
+
+In the code above, we see how the `apiToken` service is defined via the Factory recipe that depends
+on `clientId` service. The factory service then uses NSA-proof encryption to produce an authentication
+token.
+
+Note: It is a best practice to name the factory functions as "<serviceId>Factory"
+(e.g. apiTokenFactory). While this names are not required, they help when navigating the code base
+or looking at stack traces in the debugger.
+
+Just like with Value recipe, Factory recipe can create a service of any type, whether it be a
+primitive, object literal, function, or even an instance of a custom type.
+
+
+## Service Recipe
+
+JavaScript developers often use custom types to write object-oriented code. Let's explore how we
+could launch a unicorn into the space via our `unicornLauncher` service that is an instance of
+custom type:
+
+```javascript
+function UnicornLauncher(apiToken) {
+
+ this.launchedCount = 0;
+ this.launch() {
+ // make a request to the remote api and include the apiToken
+ ...
+ this.launchedCount++;
+ }
+}
+```
+
+We are now ready to launch unicorns, but notice that UnicornLauncher depends on our `apiToken`.
+We can satisfy this dependency on `apiToken` using the Factory recipe:
+
+```javascript
+myApp.factory('unicornLauncher', ["apiToken", function(apiToken) {
+ return new UnicornLauncher(apiToken);
+}]);
+```
+
+
+This is, however, exactly the use-case that Service recipe is the most suitable for.
+
+The Service recipe produces a service just like the Value or Factory recipes, but it does so by
+*invoking a constructor with the `new` operator*. The constructor can take zero or more arguments,
+which represent dependencies needed by the instance of this type.
+
+Note: Service recipes follow a design pattern called [constructor
+injection](http://www.martinfowler.com/articles/injection.html#ConstructorInjectionWithPicocontainer).
+
+Since we already have a constructor for our UnicornLauncher type, we can replace the Factory recipe
+above with a Service recipe like this:
+
+```javascript
+myApp.service('unicornLauncher', ["apiToken", UnicornLauncher]);
+```
+
+Much simpler!
+
+Note: Yes, we have called one of our service recipes 'Service'. We regret this and know that we'll
+be somehow punished for our mis-deed. It's like we named one of our offspring 'Children'. Boy,
+that would mess with the teachers.
+
+
+## Provider Recipe
+
+There are two more recipe types left to cover. They are both fairly specialized and are used
+infrequently. As already mentioned in the intro, the Provider recipe is the core recipe type and
+all the other recipe types are just syntactic sugar on top of it. It is the most verbose recipe
+with the most abilities, but for most services it's overkill.
+
+Provider recipe is syntactically defined as a custom type that implements a `$get` method. This
+method is a factory function just like the one we use in Factory recipe. In fact, if you define
+a Factory recipe, an empty Provider type with the `$get` method set to your factory function is
+automatically created under the hood.
+
+You should use the Provider recipe only when you want to expose an API for application-wide
+configuration that must be made before the application starts. This is usually interesting only
+for reusable services whose behavior might need to vary slightly between applications.
+
+Let's say that our `unicornLauncher` service is so awesome that many apps use it. By default the
+launcher shoots unicorns into space without any protective shielding. But on some planets the
+atmosphere is so thick that we must wrap every unicorn in tinfoil before sending it on its
+intergalactic trip, otherwise they would burn while passing through the atmosphere. It would then
+be great if we could configure the launcher to use the tinfoil shielding for each launch in apps
+that need it. We can make it configurable it like so:
+
+
+```javascript
+myApp.provider('unicornLauncher', function UnicornLauncherProvider() {
+ var useTinfoilShielding = false;
+
+ this.useTinfoilShielding = function(value) {
+ useTinfoilShielding = !!value;
+ };
+
+ this.$get = ["apiToken", function unicornLauncherFactory(apiToken) {
+
+ // let's assume that the UnicornLauncher constructor was also changed to
+ // accept and use the useTinfoilShielding argument
+ return new UnicornLauncher(apiToken, useTinfoilShielding);
+ }]);
+});
+```
+
+To turn the tinfoil shielding on in our app, we need to create a config function via the module
+API and have the UnicornLauncherProvider injected into it:
+
+```javascript
+myApp.config(["unicornLauncherProvider", function(unicornLauncherProvider) {
+ unicornLauncherProvider.useTinfoilShielding(true);
+}]);
+```
+
+Notice that the unicorn provider is injected into the config function. This injection is done by a
+provider injector which is different from the regular instance injector, in that it instantiates
+and wires (injects) all provider instances only.
+
+During application bootstrap, before Angular goes off creating all services, it configures and
+instantiates all providers. We call this the configuration phase of the application life-cycle.
+During this phase services aren't accessible because they haven't been created yet.
+
+Once the configuration phase is over, interaction with providers is disallowed and the process of
+creating services starts. We call this part of the application life-cycle the run phase.
+
+
+## Constant Recipe
+
+We've just learned how Angular splits the life-cycle into configuration phase and run phase and how
+you can provide configuration to your application via the config function. Since the config
+function runs in the configuration phase when no services are available, it doesn't have access
+even to simple value objects created via Value recipe.
+
+Since simple values, like url prefix, don't have dependencies or configuration, it is often handy
+to make them available in both the configuration and run phases. This is what the Constant recipe
+is for.
+
+Let's say that our `unicornLauncher` service can stamp a unicorn with the planet name it's being
+launched from if this name was provided during the configuration phase. The planet name is
+application specific and is used also by various controllers during the runtime of the application.
+We can then define the planet name as a constant like this:
+
+```javascript
+myApp.constant('planetName', 'Greasy Giant');
+```
+
+We could then configure the unicornLauncherProvider like this:
+
+```javascript
+myApp.config(['unicornLauncherProvider', 'planetName', function(unicornLauncherProvider, planetName) {
+ unicornLauncherProvider.useTinfoilShielding(true);
+ unicornLauncherProvider.stampText(planetName);
+}]);
+```
+
+And since Constant recipe makes the value also available at runtime just like the Value recipe, we
+can also use it in our controller and template:
+
+```javascript
+myApp.controller('DemoController', ["clientId", "planetName", function DemoController(clientId, planetName) {
+ this.clientId = clientId;
+ this.planetName = planetName;
+}]);
+```
+
+```html
+<html ng-app="myApp">
+ <body ng-controller="DemoController as demo">
+ Client ID: {{demo.clientId}}
+ <br>
+ Planet Name: {{demo.planetName}}
+ </body>
+</html>
+```
+
+
+## Special Purpose Objects
+
+Earlier we mentioned that we also have special purpose objects that are different from services.
+These objects extend the framework as plugins and therefore must implement interfaces specified by
+Angular. These interfaces are Controller, Directive, Filter and Animation.
+
+The instructions for the injector to create these special objects (with the exception of the
+Controller objects) use the Factory recipe behind the scenes.
+
+Let's take a look at how we would create a very simple component via the directive api that depends
+on the `planetName` constant we've just defined and displays the planet name, in our case:
+"Planet Name: Greasy Giant".
+
+Since the directives are registered via Factory recipe, we can use the same syntax as with factories.
+
+```javascript
+myApp.directive('myPlanet', ['planetName', function myPlanetDirectiveFactory(planetName) {
+ // directive definition object
+ return {
+ restrict: 'E',
+ scope: {},
+ link: function($scope, $element) { $element.text('Planet: ' + planetName); }
+ }
+}]);
+```
+
+We can then use the component like this:
+
+```html
+<html ng-app="myApp">
+ <body>
+ <my-planet></my-planet>
+ </body>
+</html>
+```
+
+Using Factory recipes you can also define Angular's filters and animations, but the controllers
+are a bit special. You create a controller as a custom type that declares its dependencies as
+arguments for its constructor function. This constructor is then registered with a module. Let's
+take a look at the `DemoController`, created in one of the early examples:
+
+```javascript
+myApp.controller('DemoController', ['clientId', function DemoController(clientId) {
+ this.clientId = clientId;
+}]);
+```
+
+The DemoController is instantiated via its constructor every time the app needs an instance of
+DemoController (in our simple app it's just once). So unlike services, controllers are not
+singletons. The constructor is called with all the requested services, in our case the `clientId`
+service.
+
+
+## Conclusion
+
+To wrap it up, let's summarize the most important points:
+
+- The injector uses recipes to create two types of objects: services and special purpose objects
+- There are five recipe types that define how to create objects: Value, Factory, Service, Provider
+ and Constant.
+- Factory and Service are the most commonly used recipes. The only difference between them is that
+ Service recipe works better for objects of custom type, while Factory can produce JavaScript
+ primitives and functions.
+- The Provider recipe is the core recipe type and all the other ones are just syntactic sugar on it.
+- Provider is the most complex recipe type. You don't need it unless you are building a reusable
+ piece of code that needs global configuration.
+- All special purpose objects except for Controller are defined via Factory recipes.
+
+<table class="table table-bordered code-table">
+<thead>
+<tr>
+ <th>Features / Recipe type</th>
+ <th>Factory</th>
+ <th>Service</th>
+ <th>Value</th>
+ <th>Constant</th>
+ <th>Provider</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>can have dependencies</td>
+ <td class="success">yes</td>
+ <td class="success">yes</td>
+ <td class="error">no</td>
+ <td class="error">no</td>
+ <td class="success">yes</td>
+</tr>
+<tr>
+ <td>uses type friendly injection</td>
+ <td class="error">no</td>
+ <td class="success">yes</td>
+ <td class="success">yes\*</td>
+ <td class="success">yes\*</td>
+ <td class="error">no</td>
+</tr>
+<tr>
+ <td>object available in config phase</td>
+ <td class="error">no</td>
+ <td class="error">no</td>
+ <td class="error">no</td>
+ <td class="success">yes</td>
+ <td class="success">yes\*\*</td>
+</tr>
+<tr>
+ <td>can create functions/primitives</td>
+ <td class="success">yes</td>
+ <td class="error">no</td>
+ <td class="success">yes</td>
+ <td class="success">yes</td>
+ <td class="success">yes</td>
+</tr>
+</tbody>
+</table>
+
+\* at the cost of eager initialization by using `new` operator directly
+
+\*\* the service object is not available during the config phase, but the provider instance is
+(see the `unicornLauncherProvider` example above).
+