mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-03-16 23:30:23 +00:00
docs(guide/providers): add providers documentation
This commit is contained in:
parent
da344daa40
commit
5614fd283a
4 changed files with 437 additions and 1 deletions
|
|
@ -193,7 +193,9 @@ directive.table = function() {
|
|||
return {
|
||||
restrict: 'E',
|
||||
link: function(scope, element, attrs) {
|
||||
element.addClass('table table-bordered table-striped code-table');
|
||||
if (!attrs.class) {
|
||||
element.addClass('table table-bordered table-striped code-table');
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
|||
425
docs/content/guide/providers.ngdoc
Normal file
425
docs/content/guide/providers.ngdoc
Normal file
|
|
@ -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).
|
||||
|
||||
|
|
@ -1189,6 +1189,7 @@ var GUIDE_PRIORITY = [
|
|||
'dev_guide.templates',
|
||||
|
||||
'di',
|
||||
'providers',
|
||||
'module',
|
||||
'scope',
|
||||
'expression',
|
||||
|
|
|
|||
|
|
@ -531,3 +531,11 @@ pre ol li {
|
|||
padding-bottom:30px;
|
||||
border-bottom:1px solid #aaa;
|
||||
}
|
||||
|
||||
td.success {
|
||||
background-color: #dff0d8;
|
||||
}
|
||||
|
||||
td.error {
|
||||
background-color: #f2dede;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue