@ -5,51 +5,6 @@ Dependency Injection is a powerful pattern for managing code dependencies.
|
||||
This cookbook explores many of the features of Dependency Injection (DI) in Angular.
|
||||
{@a toc}
|
||||
|
||||
<!--
|
||||
|
||||
# Contents
|
||||
|
||||
* [Application-wide dependencies](guide/dependency-injection-in-action#app-wide-dependencies)
|
||||
* [External module configuration](guide/dependency-injection-in-action#external-module-configuration)
|
||||
* [`@Injectable()` and nested service dependencies](guide/dependency-injection-in-action#nested-dependencies)
|
||||
|
||||
* [`@Injectable()`](guide/dependency-injection-in-action#injectable-1)
|
||||
|
||||
* [Limit service scope to a component subtree](guide/dependency-injection-in-action#service-scope)
|
||||
* [Multiple service instances (sandboxing)](guide/dependency-injection-in-action#multiple-service-instances)
|
||||
* [Qualify dependency lookup with `@Optional()` and `@Host()`](guide/dependency-injection-in-action#qualify-dependency-lookup)
|
||||
|
||||
* [Demonstration](guide/dependency-injection-in-action#demonstration)
|
||||
|
||||
* [Inject the component's DOM element](guide/dependency-injection-in-action#component-element)
|
||||
* [Define dependencies with providers](guide/dependency-injection-in-action#providers)
|
||||
|
||||
* [Defining providers](guide/dependency-injection-in-action#defining-providers)
|
||||
* [The *provide* object literal](guide/dependency-injection-in-action#provide)
|
||||
* [`useValue`—the *value provider*](guide/dependency-injection-in-action#usevalue)
|
||||
* [`useClass`—the *class provider*](guide/dependency-injection-in-action#useclass)
|
||||
* [`useExisting`—the *alias provider*](guide/dependency-injection-in-action#useexisting)
|
||||
* [`useFactory`—the *factory provider*](guide/dependency-injection-in-action#usefactory)
|
||||
|
||||
* [Provider token alternatives: the class-interface and `InjectionToken`](guide/dependency-injection-in-action#tokens)
|
||||
|
||||
* [class-interface](guide/dependency-injection-in-action#class-interface)
|
||||
* [`InjectionToken`](guide/dependency-injection-in-action#injection-token)
|
||||
|
||||
* [Inject into a derived class](guide/dependency-injection-in-action#di-inheritance)
|
||||
* [Find a parent component by injection](guide/dependency-injection-in-action#find-parent)
|
||||
|
||||
* [Find parent with a known component type](guide/dependency-injection-in-action#known-parent)
|
||||
* [Cannot find a parent by its base class](guide/dependency-injection-in-action#base-parent)
|
||||
* [Find a parent by its class-interface](guide/dependency-injection-in-action#class-interface-parent)
|
||||
* [Find a parent in a tree of parents with `@SkipSelf()`](guide/dependency-injection-in-action#parent-tree)
|
||||
* [The `Parent` class-interface](guide/dependency-injection-in-action#parent-token)
|
||||
* [A `provideParent()` helper function](guide/dependency-injection-in-action#provideparent)
|
||||
|
||||
* [Break circularities with a forward class reference (*forwardRef*)](guide/dependency-injection-in-action#forwardref)
|
||||
|
||||
-->
|
||||
|
||||
See the <live-example name="dependency-injection-in-action"></live-example>
|
||||
of the code in this cookbook.
|
||||
|
||||
@ -79,7 +34,7 @@ is all the registration you need.
|
||||
|
||||
A *provider* is something that can create or deliver a service.
|
||||
Angular creates a service instance from a class provider by using `new`.
|
||||
Read more about providers in the [Dependency Injection](guide/dependency-injection#injector-providers)
|
||||
Read more about providers in the [Dependency Injection](guide/dependency-injection#register-providers-ngmodule)
|
||||
guide.
|
||||
|
||||
</div>
|
||||
|
167
aio/content/guide/dependency-injection-pattern.md
Normal file
167
aio/content/guide/dependency-injection-pattern.md
Normal file
@ -0,0 +1,167 @@
|
||||
# The Dependency Injection pattern
|
||||
|
||||
**Dependency injection** is an important application design pattern.
|
||||
It's used so widely that almost everyone just calls it _DI_.
|
||||
|
||||
Angular has its own dependency injection framework, and
|
||||
you really can't build an Angular application without it.
|
||||
|
||||
This page covers what DI is and why it's useful.
|
||||
|
||||
When you've learned the general pattern, you're ready to turn to
|
||||
the [Angular Dependency Injection](guide/dependency-injection) guide to see how it works in an Angular app.
|
||||
|
||||
{@a why-di }
|
||||
|
||||
## Why dependency injection?
|
||||
|
||||
To understand why dependency injection is so important, consider an example without it.
|
||||
Imagine writing the following code:
|
||||
|
||||
<code-example path="dependency-injection/src/app/car/car-no-di.ts" region="car" title="src/app/car/car.ts (without DI)">
|
||||
</code-example>
|
||||
|
||||
The `Car` class creates everything it needs inside its constructor.
|
||||
What's the problem?
|
||||
The problem is that the `Car` class is brittle, inflexible, and hard to test.
|
||||
|
||||
This `Car` needs an engine and tires. Instead of asking for them,
|
||||
the `Car` constructor instantiates its own copies from
|
||||
the very specific classes `Engine` and `Tires`.
|
||||
|
||||
What if the `Engine` class evolves and its constructor requires a parameter?
|
||||
That would break the `Car` class and it would stay broken until you rewrote it along the lines of
|
||||
`this.engine = new Engine(theNewParameter)`.
|
||||
The `Engine` constructor parameters weren't even a consideration when you first wrote `Car`.
|
||||
You may not anticipate them even now.
|
||||
But you'll *have* to start caring because
|
||||
when the definition of `Engine` changes, the `Car` class must change.
|
||||
That makes `Car` brittle.
|
||||
|
||||
What if you want to put a different brand of tires on your `Car`? Too bad.
|
||||
You're locked into whatever brand the `Tires` class creates. That makes the
|
||||
`Car` class inflexible.
|
||||
|
||||
Right now each new car gets its own `engine`. It can't share an `engine` with other cars.
|
||||
While that makes sense for an automobile engine,
|
||||
surely you can think of other dependencies that should be shared, such as the onboard
|
||||
wireless connection to the manufacturer's service center. This `Car` lacks the flexibility
|
||||
to share services that have been created previously for other consumers.
|
||||
|
||||
When you write tests for `Car` you're at the mercy of its hidden dependencies.
|
||||
Is it even possible to create a new `Engine` in a test environment?
|
||||
What does `Engine` depend upon? What does that dependency depend on?
|
||||
Will a new instance of `Engine` make an asynchronous call to the server?
|
||||
You certainly don't want that going on during tests.
|
||||
|
||||
What if the `Car` should flash a warning signal when tire pressure is low?
|
||||
How do you confirm that it actually does flash a warning
|
||||
if you can't swap in low-pressure tires during the test?
|
||||
|
||||
You have no control over the car's hidden dependencies.
|
||||
When you can't control the dependencies, a class becomes difficult to test.
|
||||
|
||||
How can you make `Car` more robust, flexible, and testable?
|
||||
|
||||
{@a ctor-injection}
|
||||
That's super easy. Change the `Car` constructor to a version with DI:
|
||||
|
||||
<code-tabs>
|
||||
|
||||
<code-pane title="src/app/car/car.ts (excerpt with DI)" path="dependency-injection/src/app/car/car.ts" region="car-ctor">
|
||||
</code-pane>
|
||||
|
||||
<code-pane title="src/app/car/car.ts (excerpt without DI)" path="dependency-injection/src/app/car/car-no-di.ts" region="car-ctor">
|
||||
</code-pane>
|
||||
|
||||
</code-tabs>
|
||||
|
||||
See what happened? The definition of the dependencies are
|
||||
now in the constructor.
|
||||
The `Car` class no longer creates an `engine` or `tires`.
|
||||
It just consumes them.
|
||||
|
||||
<div class="l-sub-section">
|
||||
|
||||
This example leverages TypeScript's constructor syntax for declaring
|
||||
parameters and properties simultaneously.
|
||||
|
||||
</div>
|
||||
|
||||
Now you can create a car by passing the engine and tires to the constructor.
|
||||
|
||||
<code-example path="dependency-injection/src/app/car/car-creations.ts" region="car-ctor-instantiation" linenums="false">
|
||||
</code-example>
|
||||
|
||||
How cool is that?
|
||||
The definition of the `engine` and `tire` dependencies are
|
||||
decoupled from the `Car` class.
|
||||
You can pass in any kind of `engine` or `tires` you like, as long as they
|
||||
conform to the general API requirements of an `engine` or `tires`.
|
||||
|
||||
Now, if someone extends the `Engine` class, that is not `Car`'s problem.
|
||||
|
||||
<div class="l-sub-section">
|
||||
|
||||
The _consumer_ of `Car` has the problem. The consumer must update the car creation code to
|
||||
something like this:
|
||||
|
||||
<code-example path="dependency-injection/src/app/car/car-creations.ts" region="car-ctor-instantiation-with-param" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
The critical point is this: the `Car` class did not have to change.
|
||||
You'll take care of the consumer's problem shortly.
|
||||
|
||||
</div>
|
||||
|
||||
The `Car` class is much easier to test now because you are in complete control
|
||||
of its dependencies.
|
||||
You can pass mocks to the constructor that do exactly what you want them to do
|
||||
during each test:
|
||||
|
||||
<code-example path="dependency-injection/src/app/car/car-creations.ts" region="car-ctor-instantiation-with-mocks" linenums="false">
|
||||
</code-example>
|
||||
|
||||
**You just learned what dependency injection is**.
|
||||
|
||||
It's a coding pattern in which a class receives its dependencies from external
|
||||
sources rather than creating them itself.
|
||||
|
||||
Cool! But what about that poor consumer?
|
||||
Anyone who wants a `Car` must now
|
||||
create all three parts: the `Car`, `Engine`, and `Tires`.
|
||||
The `Car` class shed its problems at the consumer's expense.
|
||||
You need something that takes care of assembling these parts.
|
||||
|
||||
You _could_ write a giant class to do that:
|
||||
|
||||
<code-example path="dependency-injection/src/app/car/car-factory.ts" title="src/app/car/car-factory.ts">
|
||||
</code-example>
|
||||
|
||||
It's not so bad now with only three creation methods.
|
||||
But maintaining it will be hairy as the application grows.
|
||||
This factory is going to become a huge spiderweb of
|
||||
interdependent factory methods!
|
||||
|
||||
Wouldn't it be nice if you could simply list the things you want to build without
|
||||
having to define which dependency gets injected into what?
|
||||
|
||||
This is where the dependency injection framework comes into play.
|
||||
Imagine the framework had something called an _injector_.
|
||||
You register some classes with this injector, and it figures out how to create them.
|
||||
|
||||
When you need a `Car`, you simply ask the injector to get it for you and you're good to go.
|
||||
|
||||
<code-example path="dependency-injection/src/app/car/car-injector.ts" region="injector-call" title="src/app/car/car-injector.ts" linenums="false">
|
||||
</code-example>
|
||||
|
||||
Everyone wins. The `Car` knows nothing about creating an `Engine` or `Tires`.
|
||||
The consumer knows nothing about creating a `Car`.
|
||||
You don't have a gigantic factory class to maintain.
|
||||
Both `Car` and consumer simply ask for what they need and the injector delivers.
|
||||
|
||||
This is what a **dependency injection framework** is all about.
|
||||
|
||||
Now that you know what dependency injection is and appreciate its benefits,
|
||||
turn to the [Angular Dependency Injection](guide/dependency-injection) guide to see how it is implemented in Angular.
|
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user