Check out our Angular Book Series.

Component Specific Providers - Dependency Injection and Angular - Part 4

I was chatting with a colleague recently, and he had some confusion about how dependency injection works with Angular. There are a bunch of ways to set up Dependency Injection with Angular, but as always the documentation seems lacking. I'm writing this series of posts to discuss the ways and to explain what works and what won't work.

Check out the previous entries:

  1. The one where we set up a project and create a default Angular Service
  2. The one where we remove the providedIn configuration object
  3. The one where we remove all the injectable metadata.

Component specific providers

Everything we've built in the previous examples has shown how providers work when created at the root level. You can also create providers at the component level. This can be common if you have split up your app into sections which are lazy loaded. One section may need to know nothing about the other section, and thus you don't need a global service, just one specific to that section. In other cases, you may just need to share some data, or functionality, across all the components that make up a specific screen. That is what I'm going to demo now.

Let's create a brand new service:


ng generate service services/service4

You'll see this:

Open up the service class:


import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class Service4Service {
constructor() { }
}

First, we're going to remove the providedIn metadata. Second, we're going to add a shared value in this service:


import { Injectable } from '@angular/core';
@Injectable()
export class Service4Service {
myValue = 'default 4';
constructor() { }
}

Since we no longer specify providedIn value to be root, we must set up this component as a provider. In past examples we did this with a module, however, we can also use the same metadata in a component.

Open up view1.component.ts and find the Component metadata. It'll look like this:


@Component({
selector: 'app-view1',
templateUrl: './view1.component.html',
styleUrls: ['./view1.component.css']
})

Add a providers array to this component:


@Component({
selector: 'app-view1',
templateUrl: './view1.component.html',
styleUrls: ['./view1.component.css'],
providers: [Service4Service]
})

Components support the same providers array syntax that our app module is. The main difference is where the service instance is maintained. In this case the provider will be specific to this component. Be sure to import the Service4Service class:


import {Service4Service} from '../services/service4.service';

And inject the new service into the constructor:


constructor(public service1: Service1Service,
public service2: Service2Service,
public service3: Service3Service,
public service4: Service4Service) { }

Switch over to the view1.component.html file and add this at the end:


<h1>View 2: Service 4</h1>
<input [(ngModel)]="service4.myValue">

This uses the ngModel to bind the input value to the value inside the services. In past examples we used this approach to share services across multiple components. Let's see how it goes here.

Onto the view2.component.ts. We're going to mirror our changes from the view1.component.ts. First, add Service4 as a provider:


@Component({
selector: 'app-view2',
templateUrl: './view2.component.html',
styleUrls: ['./view2.component.css'],
providers: [Service4Service]
})

Then inject the provider into the constructor, along with all the services from our previous samples:


constructor(public service1: Service1Service,
public service2: Service2Service,
public service3: Service3Service,
public service4: Service4Service) { }

Be sure to properly import the Service4Services class:


import {Service4Service} from '../services/service4.service';

Switch over to view2.component.html and output the value at the bottom:


<h1>View 2: Service 4</h1>
{{service4.myValue}}

Now run the app:

Notice that updating the service4 value in view1 has no affect on the output in view2. This is because each component is creating its own instance of the service because the provider is set up at the component level, something that both of these components do not share.

What Next?

You can play with the code here.

In the next article of this series, coming out in a few weeks, I'm going to talk about how to create multiple Angular service providers using the same exact class. I ran into this in a real world sample, and the approach was not as trivial as I expected it to be.

Comments (Comment Moderation is enabled. Your comment will not appear until approved.)
Comments are not allowed for this entry.
All Content Copyright 2005, 2006, 2007, 2008, 2009 Jeffry Houser. May not be reused without permission
BlogCFC was created by Raymond Camden. This blog is running version 5.9.2.002.