In the previous section, we talked about injectors and how they are organized like a tree. Lookups follow the injector tree upwards until they found the requested thing to inject. But when do we actually want to provide bindings on the root injector and when do we want to provide them on a child injector?
In the previous section, we talked about injectors and how they are organized like a tree. Lookups follow the injector tree upwards until they found the requested thing to inject. But when do we actually want to provide providers on the root injector and when do we want to provide them on a child injector?
Consider you are building a component to show a list of super heroes that displays each super hero in a card with it’s name and superpower. There should also be an edit button that opens up an editor to change the name and superpower of our hero.
@ -92,64 +99,68 @@ figure.image-display
Let’s take a look at the `HeroesListComponent` which is the root component for this example.
Notice that it imports the `HeroService` that we’ve used before so we can skip its declaration. The only difference is that we’ve used a more formal approach for our `Hero`model and defined it upfront as such.
Our `HeroesListComponent` defines a template that creates a list of `HeroCardComponents` and `HeroEditorComponents`, each bound to an instance of hero that is returned from the `HeroService`. Ok, that’s not entirely true. It actually binds to an `EditItem<Hero>` which is a simple generic datatype that can wrap any type and indicate if the item being wrapped is currently being edited or not.
Now here it’s getting interesting. The `HeroEditorComponent`defines a template with an input to change the name of the hero and a `cancel` and a `save` button. Remember that we said we want to have the flexibility to cancel our editing and restore the old value? This means we need to maintain two copies of our `Hero` that we want to edit. Thinking ahead this is a perfect use case to abstract it into it’s own generic service since we have probably more cases like this in our app.
And this is where the `RestoreService` enters the stage.
All this tiny service does is define an API to set a value of any type which can be altered, retrieved or set back to it’s initial value. That’s exactly what we need to implement the desired functionality.
Our `HeroEditComponent` uses this services under the hood for it’s `hero` property. It intercepts the `get` and `set` method to delegate the actual work to our `RestoreService` which in turn makes sure that we won’t work on the original item but on a copy instead.
At this point we may be scratching our heads asking what this has to do with component injectors? If we look closely at our `HeroEditComponent` we’ll notice this piece of code
Technically we could, but our component wouldn’t quite behave the way it is supposed to. Remember that each injector treats the services that it provides as singletons. However, in order to be able to have multiple instances of `HeroEditComponent` edit multiple heroes at the same time we need to have multiple instances of the `RestoreService`. More specifically each instance of `HeroEditComponent` needs to be bound to it’s own instance of the `RestoreService`.
By configuring a binding for the `RestoreService` on the `HeroEditComponent`, we get exactly one instance of the `RestoreService`per `HeroEditComponent`.
By configuring a provider for the `RestoreService` on the `HeroEditComponent`, we get exactly one new instance of the `RestoreService`per `HeroEditComponent`.
Does that mean that services aren’t singletons anymore in Angular 2? Yes and no.
While there’s only one instance per binding per injector there may be multiple instances of the same type across
the entire application due to the fact that we can create multiple bindings for the same type on different components.
There can be only one instance of a service type in a particular injector.
But we've learned that we can have multiple injectors operating at different levels of the application's component tree.
Any of those injectors could have its own instance of the service.
If we had only defined a binding for `RestoreService` on the root component,
we would have exactly one instance of the across the entire applicatoin. That’s clearly not what we want in this scenario.
We don’t want to share an instance. We want each component to have its own instance of the `RestoreService`.
If we defined a `RestoreService` provider only on the root component,
we would have exactly one instance of that service and it would be shared across the entire application.
That’s clearly not what we want in this scenario. We want each component to have its own instance of the `RestoreService`.
Defining (or re-defining) a provider at the component level creates a new instance of the service for each new instance
of that component. We've made the `RestoreService` a kind of "private" singleton for each `HeroEditComponent`,
scoped to that component instance and its child components.
<!--
## Advanced Dependency Injection in Angular 2
@ -162,5 +173,5 @@ figure.image-display
:marked
## Dependency Visibility
[TODO] (bindings vs viewBindings) This has been postponed for now until come up with a decent use case
[TODO] (providers vs viewProviders) This has been postponed for now until come up with a decent use case