In this lesson, we’re going to take a look at how adding the class provide to the providers property of a component to create an actual provider. We’ll learn what a provider specifically does and how we can provide different dependencies with the same token.
[00:00] When injecting services into Angular 2 components, there are typically three things we need to do. First, we need to import the type of what we want to inject, in this case our list component has a data service dependency, so we import dataservice, while datservice is a class in its own module. Now we have the type imported, we can use it in our component file.
[00:21] Second, we ask for dependency of that type in the constructor, where we want to inject that dependency. Now in this component this means we're adding a dataservice parameter to the constructor of type dataservice. Again, in order to actually use that type, it has to be imported first.
[00:41] Now we're basically Angular that we're interested in a dependency of type dataservice. But Angular actually doesn't know what a dataservice is. That's why we need to do the third thing, which is we need to tell Angular what a dataservice is, and also how to create it.
[00:58] We do that by creating providers, by using the providers property of the component metadata. What seems unclear at this point is why do we have to define such a provider when we've already imported dataservice in the first place? Also how come a dataservice is a provider at the same time?
[01:17] The answer is that importing a type doesn't give us an instance of that type, all we really do are just pulling in the class defined in another file so wecan use it in our own file. What we want to inject, however, is an instance and the provider takes care of creating that for us.
[01:35] It's like a recipe that describes how something is created when someone ask for a certain thing. That brings us to the second question. How come a dataservice is a provider at the same time?
[01:46] As we see here in our list component, we added dataservice to the list of providers, so Angular can actually create objects of that type when someone asks for it. It turns out that simply adding a class to the providers list is actually a shorthand syntax for a token and a strategy that describe how to create a certain object.
[02:06] The same code can be rewritten with a map literal syntax like this. Provide dataservice use class dataservice. The provide property gets a so-called token, and the token is the thing that we use in constructors to ask for dependency as we do here in our list component.
[02:30] Use class is a strategy that describes what and how something should be created. In other words, what we do here is we say when someone asks for a dependency with a token dataservice, use the class dataservice to create an object which then will be injected.
[02:49] There are other strategies to create objects, but the shorthand syntax only expands to use class, and only if the given class is the same as the token.
[02:58] What is really powerful is that we can now inject different objects, even though our application asks for a dataservice dependency. Here we have an otherdataservice class that has the same API as the dataservice, but the returned data in getItems is different.
[03:17] We can inject an instance of that class by importing it, and configure out provider that it uses that class instead of dataservice to create the dependency. At this point we can't use the shorthand syntax anymore, because the strategy class is no longer the same as the token.
[03:36] We save the file and see that we get the data from other dataservice while it's still asking for a dependency of the type dataservice.
[03:43] To recap importing a data really just makes the type available, but doesn't give us an instance. In order to inject objects, we need providers that know how to create these objects by a given token which is a type.
I am a bit surprised to see that the component is responsible for saying which implementation should be used for a given service type (provider in Anglular2). This configuration is typically done outside of the component itself when using DI container. e.g. the component would specify that a DataService is required but not which implementation to use.
- What's your opinion on that?
- How can this configuration outside of the component be achieved in Angular2?
Cheers, Michael
I think the answer is that you can create your own custom providers which make decisions about which service to provide. Then you can pass "DataServiceProvider" to the providers array of the component and have DataServiceProvider determine which service to provide. This moves the decision making out of the component
EDIT: Your answer is in the next video :)
It's still a little scary to me how with TypeScript, you can make 2 different classes and have them share the same public interface - then not define an interface for them to explicitly extend, but then still use the 2 classes interchangeably. I would've thought that the TypeScript compiler would complain about them not explicitly extending an interface if you've annotated the type explicitly like that.
I am a bit surprised to see that the component is responsible for saying which implementation should be used for a given service type (provider in Anglular2). This configuration is typically done outside of the component itself when using DI container. e.g. the component would specify that a DataService is required but not which implementation to use.
Cheers, Michael