Key Takeaways
- Angular 2 components have the ability to use providers, which are a set of injectable objects available to the component. Providers are a fundamental aspect of Angular 2’s Dependency Injection (DI) system.
- Providers can be classified into three types: class providers, factory providers, and value providers. A class provider generates an instance of the class, a factory provider generates the return value of a specified function, and a value provider simply returns its value.
- Angular 2’s DI system allows for registering a class, function, or value (known as providers), resolving dependencies between providers, making the provider’s result available in code, and maintaining a hierarchy of injectors.
- Angular’s injector creates an instance of a class provider once, caches it, and each subsequent injection receives the same instance as long as the same provider is used. This feature gives flexibility over the results generated by any one provider and whether we work with a single instance or multiple instances.
- Angular 2 allows for the registration of providers using a key (called a “token”) associated with the actual provider. This feature is useful for unit testing, where it is possible to substitute a mock class that doesn’t make the server call without changing the component code.
In a previous article, we looked at how to get data into and out of components using the @Input
and @Output
annotations. In this article, we’ll look at another fundamental aspect of Angular 2 components — their ability to use providers.
You may have seen “providers” in a list of properties you can use to configure components, and you might have realized that they allow you to define a set of injectable objects that will be available to the component. That’s nice, but it of course begs the question, “what is a provider?”
Answering that question gets us into an involved discussion of Angular 2’s Dependency Injection (DI) system. We may specifically cover DI in a future blog post, but it’s well covered in a series of articles by Pascal Precht, beginning with: http://blog.thoughtram.io/angular/2015/05/18/dependency-injection-in-angular-2.html. We’ll assume you are familiar with DI and Angular 2’s DI system in general, as covered in Pascal’s article, but in brief the DI system is responsible for:
- Registering a class, function or value. These items, in the context of dependency injection, are called “providers” because they result in something. For example, a class is used to provide or result in an instance. (See below for more details on provider types.)
- Resolving dependencies between providers — for example, if one provider requires another provider.
- Making the provider’s result available in code when we ask for it. This process of making the provider result available to a block of code is called “injecting it.” The code that injects the provider results is, logically enough, called an “injector.”
- Maintaining a hierarchy of injectors so that if a component asks for a provider result from a provider not available in its injector, DI searches up the hierarchy of injectors.
In the previous article, we included a diagram showing that components form a hierarchy beginning with a root component. Let’s add to that diagram to include the injectors and the resources (providers) they register:
Figure 1: Each component has its own injector that registers providers. Injectors create child injectors and a request for a provider starts with the local injector and searches up the injector hierarchy.
We can see from the above that while components form a downwards directed graph, their associated injectors have a two-way relationship: parent injectors create children (downwards) and when a provider is requested, Angular 2 searches the parent injector (upwards) if it can’t find the requested provider in the component’s own injector. This means that a provider with the same identifier at a lower level will shadow (hide) the same-named provider at a higher level.
What are Providers?
So, what are these “providers” that the injectors are registering at each level? Actually, it’s simple: a provider is a resource or JavaScript “thing” that Angular uses to provide (result in, generate) something we want to use:
- A class provider generates/provides an instance of the class.
- A factory provider generates/provides whatever returns when you run a specified function.
- A value provider doesn’t need to take an action to provide the result like the previous two, it just returns its value.
Unfortunately, the term “provider” is sometimes used to mean both the class, function or value and the thing that results from the provider — a class instance, the function’s return value or the returned value.
Let’s see how we can add a provider to a component by creating a class provider using MyClass
, a simple class that will generate the instance we want to use in our application.
Figure 2: A simple class with four properties. (Code screenshots are from Visual Studio Code)
Okay, that’s the class. Now let’s instruct Angular to use it to register a class provider so we can ask the dependency injection system to give us an instance to use in our code. We’ll create a component, ProvDemo_01.ts
, that will serve as the root component for our application. We load this component and kick-off our application in the bootstrap.ts
:
Figure 3: Our application’s bootstrap.ts file that instantiates the root component.
If the above doesn’t make sense, then take a look at our earlier post that walks through building a simple Angular 2 application. Our root component is called ProvDemo
, and the repository contains several numbers versions of it. You can change the version that’s displayed by updating the line that imports ProvDemo above. Our first version of the root component looks like this:
Figure 4: CompDemo with MyClass imported, added to the providers array and used as a Type in the constructor arguments.
Adding the MyClass
provider to this component is straightforward:
- Import MyClass
- Add it to the @Component providers property
- Add an argument of type “MyClass” to the constructor.
Under the covers, when Angular instantiates the component, the DI system creates an injector for the component which registers the MyClass
provider. Angular then sees the MyClass
type specified in the constructor’s argument list and looks up the newly registered MyClass
provider and uses it to generate an instance which it assigns to “myClass” (initial small “m”).
The process of looking up the MyClass
provider and generating an instance to assign to “myClass” is all Angular. It takes advantage of the TypeScript syntax to know what type to search for but Angular’s injector does the work of looking up and returning the MyClass
instance.
Given the above, you might conclude that Angular takes the list of classes in the “providers” array and creates a simple registry used to retrieve the class. But there’s a slight twist to make things more flexible. A key reason why a “twist” is needed is to help us write unit tests for our components that have providers we don’t want to use in the testing environment. In the case of MyClass
, there isn’t much reason not to use the real thing, but if MyClass
made a call to a server to retrieve data, we might not want to or be able to do that in the test environment. To get around this, we need to be able to substitute within ProvDemo
a mock MyClass
that doesn’t make the server call.
How do we make the substitution? Do we go through all our code and change every MyClass
reference to MyClassMock
? That’s not efficient and is a poor pattern for writing tests.
We need to swap out the provider implementation without changing our ProvDemo
component code. To make this possible, when Angular registers a provider it sets up a map to associate a key (called a “token”) with the actual provider. In our example above, the token and the provider are the same thing: MyClass
. Adding MyClass
to the providers property in the @Component decorator is shorthand for:
providers: [ provide(MyClass, {useClass: MyClass} ]
This says “register a provider using ‘MyClass’ as the token (key) to find the provider and set the provider to MyClass
so when we request the provider, the dependency injection system returns a MyClass
instance.” Most of us are used to thinking of keys as being either numbers or strings. But in this case the token (key) is the class itself. We could have also registered the provider using a string for the token as follows:
providers: [ provide(“aStringNameForMyClass”, {useClass: MyClass} ]
So, how does this help us with testing? It means in the test environment we can override the provider registration, effectively doing:
provide(MyClass, {useClass: MyClassMock})
This associates the token (key) MyClass
with the class provider MyClassMock
. When our code asked the DI system to inject MyClass
in testing, we get an instance of MyClassMock
which can fake the data call. The net effect is that all our code remains the same and we don’t have to worry about whether the unit test will make a call to a server that might not exist in the test environment.
Injecting Non-Class Providers
In the above, we injected our class provider instance into the constructor by writing:
constructor( myClass: MyClass ) {...}
TypeScript lets us specify that the myClass argument needs to be of type MyClass and the DI system does the work to give us the MyClass instance.
But how do we tell Angular to inject our provider result if we use a string token instead of a class? Let’s edit our bootstrap.ts
file to add a new value provider and register it using a string token. Remember value providers are a type of provider that returns the value associated with the token. In the example above we told Angular to register a provider by adding to the @Component providers property but we can also register providers by passing them into the bootstrap function as follows (the same thing could be added to the providers property):
Figure 5: bootstrap.ts with a value provider added.
Here we’ve added a provider by invoking the provide function and passed in a string token ( “SECURITY_KEY”) and an object which specifies we want to create a value provider and the provider itself — in this case a simple value. Now, we’d like to inject the value generated by the value provider into our constructor, but this isn’t going to work …
constructor( SECKEY: “SECURITY_KEY”) {...}
This is because “SECURITY_KEY” is not a type. To make it possible to inject providers with non-class tokens, Angular gives us the @Inject parameter decorator. As with all other decorators, we need to import it and then we use it to tell Angular to inject a provider associated with our string token. To do this we adjust create ProvDemo\_02.ts
:
Figure 6: Importing the “Inject” decorator and using it to inject a value provider identified using a string token.
We could use the same syntax to inject the MyClass
provider:
constructor( @Inject(MyClass) myClass, @Inject('SECURITY_KEY') SECKEY ) {...}
Okay, we’ve seen how to register and use providers, but let’s learn a little bit more about what providers return.
Providers and Singletons
As we saw above, providers are responsible for generating the thing that gets injected. A class provider generates an instance and the instance gets injected. But it’s important to understand that you don’t get a new instance each time the class provider result is injected. Instead, the DI system generates the instance once, caches it and each subsequent injection receives the same instance as long as you use the same provider.
The last is important because each component gets its own injector with its own registered providers. MyClass
has a time property set to the current time in milliseconds and a random number to help us see if we’re getting the same instance each time. We’re going to add a ChildComp
component to our application.
Figure 7: ChildComp with MyClass injected into the constructor.
Notice we import MyClass
and use it to set the type in the constructor’s argument list. Important: The only purpose the imported MyClass
serves in ChildComp
is as a token the DI system uses, to look for a registered provider. Because ChildComp
does not have its own provider registered using that token, Angular looks up the injector hierarchy to find one. To make this work, we need to add ChildComp
to the ProvDemo
component:
Figure 8: ProvDemo with ChildComp added to the template.
We import ChildComp
, add a directives property to @Component to tell ProvDemo
we’re going to use the ChildComp
component and add the ChildComp
element to the template. When the application runs, the console output shows that both ProvDemo
and ChildComp
receive the same instance of MyClass
:
ProvDemomyClass 1453033148406 390
ChildCompmyClass 1453033148406 390
Now let’s change ChildComp
to add a MyClass
provider to its injector:
Figure 9: ParentComp with its own MyClass provider defined.
All we’ve changed is to add the providers property to the @Component annotation. And, sure enough, we can see that two different MyClass
instances are created:
ProvDemomyClass 1453033681877 263
ChildCompmyClass 1453033681881 761
This feature of Angular gives a lot of flexibility over the results generated by any one provider and whether we are going to work with a single instance or multiple instances. For example, you might put a component inside a repeater so the component is generated multiple times. If this repeated component registers its own provider, each one gets unique providers. But, if you only register provider in the parent component, each repeated instance shares the parent’s provider.
Wrapup
In this article, we defined what a provider is and covered the three different types of providers. We then looked at how you can register a provider for a component and inject the result generated by the provider into the component. We also took a look at how the hierarchy of injectors is used by Angular to find a requested provider. Angular gives you additional control over how the dependency injection system works and where it looks for providers but the above should get you started creating and working with providers in your Angular 2 applications.
Frequently Asked Questions (FAQs) about Angular 2 Components, Providers, Classes, Factories, and Values
What is the difference between a class and a factory in Angular 2?
In Angular 2, a class is a blueprint for creating objects. It encapsulates data and functions that manipulate that data. On the other hand, a factory is a design pattern used to create objects. In Angular 2, factories are used to create and configure services or values that don’t have a clear class to represent them. Factories provide a way to generate different instances of a class, depending on the context or configuration.
How do components and providers interact in Angular 2?
In Angular 2, components and providers work together to create a dynamic and interactive user interface. Components are the building blocks of an Angular application, while providers are used to create services that components can use. Providers allow components to share data and functionality, making it easier to maintain and update the application.
What is the role of values in Angular 2?
Values in Angular 2 are used to provide configuration information to other parts of the application. They can be injected into controllers, services, and factories, allowing these parts of the application to be configured at runtime. This makes the application more flexible and easier to test.
How do I bind a class in Angular 2?
Class binding in Angular 2 is a way to add and remove CSS classes dynamically from an element. You can bind a class to an expression, and when that expression evaluates to true, the class is added to the element. If the expression evaluates to false, the class is removed. This allows you to create dynamic and interactive user interfaces.
What is an API in the context of Angular 2?
An API, or Application Programming Interface, in the context of Angular 2, is a set of rules and protocols for building and interacting with software applications. Angular 2 provides a rich API that allows developers to create complex applications with less code and effort. The Angular 2 API includes features for creating components, services, directives, pipes, and more.
How do I use a factory in Angular 2?
To use a factory in Angular 2, you first need to define it. This is done by creating a function that returns the object you want the factory to produce. You can then register this factory with an Angular module using the .factory method. Once the factory is registered, you can inject it into other parts of your application, such as controllers and services.
How do I create a component in Angular 2?
Creating a component in Angular 2 involves defining a class and decorating it with the @Component decorator. The @Component decorator tells Angular that the class is a component and provides metadata that determines how the component should be processed, instantiated, and used at runtime.
How do I use a provider in Angular 2?
Providers in Angular 2 are used to create and deliver services to parts of an application. To use a provider, you first need to register it with an Angular module. Once registered, the provider can be injected into components, other services, and even other providers.
How do I create a service in Angular 2?
Creating a service in Angular 2 involves defining a class that encapsulates the data and functions that the service provides. This class is then decorated with the @Injectable decorator, which tells Angular that the class is a service and can be injected into other parts of the application.
How do I use values in Angular 2?
Values in Angular 2 are used to provide configuration information to other parts of the application. To use a value, you first need to register it with an Angular module. Once registered, the value can be injected into controllers, services, and factories.
David is a consultant with DevelopIntelligence and Senior Developer at Salesforce’s Desk.com working with Angular technologies on Desk’s NextGen support application. He is currently working on a team that is developing an entirely new set of features for Desk built on Angular components with initial release planned for 2016. He spends his free time doing what his wife tells him he will enjoy doing which often includes spending time with the family (which he does in fact enjoy). He is also one of the authors of Angular 2 in Action.