Support us .Net Basics C# SQL ASP.NET Aarvi MVC Slides C# Programs Subscribe Download

Angular singleton service

Suggested Videos
Part 31 - Angular 2 route parameters | Text | Slides
Part 32 - Angular dependency injection | Text | Slides
Part 33 - Why dependency injection | Text | Slides

In our previous video we discussed why should we use dependency injection and the benefits it provides. One of the benefits of dependency injection is that it allows us to share data and functionality easily as the angular injector provides a Singleton i.e a single instance of the service.


So in this videos let us see how we can use Angular services and dependency injection to create a Singleton i.e a single instance of the service which enables us to share data and functionality across multiple components in our application. To better understand this video, please watch our previous video - Why dependency injection.


Create a simple UserPreferencesService. Add a new file to the "employee" folder. Name it "userPreferences.service.ts". Copy and paste the following code

import { Injectable } from '@angular/core';

@Injectable()
export class UserPreferencesService {
    colourPreference: string = 'orange';
}

Notice this service has a single property "colourPreference" which is defaulted to "orange". We want to retrieve and use this colour in both "HomeComponent" and "EmployeeListComponent". Notice the textbox displays the default colour (orange) and background is also set to orange. Also both these components should have the ability to set the "colourPreference" property of the service to a different value using the textbox.

angular 2 service to store data

angular 2 shared service

Modify the code in home.component.ts file as shown below. The code is commented and self-explanatory.

import { Component } from '@angular/core';
import { UserPreferencesService } from '../employee/userPreferences.service';

// Notice the colour property is bound to the textbox using angular two-way
// databinding. We are also using style binding to set the background colour
// of the textbox
@Component({
    template: `
            <h1>This is the home page</h1>
            <div>
                Colour Preference :
                <input type='text' [(ngModel)]='colour' [style.background]="colour"/>
            </div>`
})
export class HomeComponent {

    // Create a private variable to hold an instance of the UserPreferencesService
    private _userPreferencesService: UserPreferencesService;

    // In the constructor we are creating an instance of the UserPreferencesService
    // using the new keyword. So this instance is local to this component and we
    // cannot use it share data with other components. Later we will modify this
    // code to use dependency injection, which creates a Singleton so the colour
    // data can be shared with other components.
    constructor() {
        this._userPreferencesService = new UserPreferencesService();
    }

    // Implement a getter to retrieve the colourPreference value
    // from the service
    get colour(): string {
        return this._userPreferencesService.colourPreference;
    }

    // Implement a setter to change the colourPreference value
    // of the service
    set colour(value: string) {
        this._userPreferencesService.colourPreference = value;
    }
}

Now we need to make similar changes in "employeeList.component.html" and "employeeList.component.ts" files. First make the following change in "employeeList.component.html"

Include a <div> element after the table in the file. These are the similar changes we made in the inline view template of HomeComponent.

<div>
    Colour Preference :
    <input type="text" [(ngModel)]="colour" [style.background]="colour" />
</div>

Now make the following changes in "employeeList.component.ts"
  1. Introduce a private field to hold an instance of UserPreferencesService
  2. In the constructor create a new instance of UserPreferencesService. Again this instance is local to the EmployeeListComponent and cannot be used to share colour data with the other components. We will discuss how to solve this issue shortly using dependency injection.
  3. Finally, introduce a getter and a setter to get and set "colourPreference" property of the UserPreferencesService
import { UserPreferencesService } from './userPreferences.service';

private _userPreferencesService: UserPreferencesService; 
  
constructor(private _employeeService: EmployeeService) {
    this._userPreferencesService = new UserPreferencesService();
}

get colour(): string {
    return this._userPreferencesService.colourPreference;
}

set colour(value: string) {
    this._userPreferencesService.colourPreference = value;
}

Now run the application and notice that on both "HomeComponent" and "EmployeeListComponent" you will see the service colourPreference property default value "orange" displayed in the textbox. The background colour of the textbox is also set to "orange" as expected.

It looks like the "colourPreference" property of the service is being shared by both the components. But that is not true. Since we are using the new keyword to create an instance of the UserPreferencesService, the instance created in each component is local to that component. Let's prove this.

Introduce a constructor in UserPreferencesService. Notice in the constructor we are logging a message to the console stating that a "New Instance of Service Created". Here is the code in "userPreferences.service.ts"

import { Injectable } from '@angular/core';

@Injectable()
export class UserPreferencesService {

    constructor() {
        console.log('New Instance of Service Created');
    }

    colourPreference: string = 'orange';
}

Run the application again and launch browser developer tools and click on the Console tab. Notice in the Console you will see the message - "New Instance of Service Created"

Now click on "Employees" menu. This will take you to EmployeeListComponent. Take another look in the "Console" tab, you will see "New Instance of Service Created" logged second time. So this proves each component is creating an instance that is local to that component and sharing data using these local instances is not possible. 

angular service sharing data

Now let us see what happens when each of the components change the "colourPreference" property value of the UserPreferencesService. Navigate to the "HomeComponent" and type "yellow" in the textbox. Notice the background colour of the textbox is changed to yellow as expected.

angular shared service between components

Now navigate to "EmployeeListcomponent" by clicking on the "Employees" tab. Notice the "colourPreference" property value is still orange. This is beacuse when we navigate to EmployeeListComponent it has created a new local instance of UserPreferencesService and the default value "orange" is being used.

angular 2 service share data between components

At this point navigate back to the HomeComponent, notice the "colourPreference" property value on the HomeComponent is also "orange" now. This is because when we navigated away from the HomeComponent the previous local instance of the UserPreferencesService it has created is destroyed and when came back to the HomeComponent it created another new local instance of the UserPreferencesService and we got it's default colourPreference property value which is orange.

angular service not singleton

So bottom line, because each component is creating a local instance of the UserPreferencesService we are not able to share data i.e we are not able to see the changes made by one component in the other component.

Now let us see how to create a Singleton i.e a single instance of the UserPreferencesService and use that single instance to share data (colourPreference property value) between the 2 components (HomeComponent & EmployeeListComponent) using dependency injection.

Remember the 2 steps to use dependency injection. We discussed these 2 steps in detail in Part 32 of Angular 2 tutorial.

Step 1 : Register the service using the Providers property of @NgModule() decorator in app.module.ts file

import { UserPreferencesService } from './employee/userPreferences.service';

@NgModule({
   providers: [UserPreferencesService]
})
export class AppModule { }

Step 2 : Specify the dependency on UserPreferencesService using the constructor of HomeComponent & EmployeeListComponent.

Modify the HomeComponent class constructor as shown below. Notice now we are using dependency injection, instead of creating a local instance of the UserPreferencesService using the new keyowrd.

export class HomeComponent {

    constructor(private _userPreferencesService: UserPreferencesService) {
    }
}

Along the samelines, modify EmployeeListComponent class as well.

export class EmployeeListComponent implements OnInit {
    // Other code

    constructor(private _employeeService: EmployeeService,
        private _userPreferencesService: UserPreferencesService)
    { }

    // Other code
}

With all these changes in place, run the application one more time and navigate to HomeComponent. Notice we have the default "colourPreference" property value orange. At this point change the colour in the textbox to yellow. Notice the background colour changes to yellow as expected.

angular service single instance

Now navigate to the EmployeeListComponent. Notice we see the yellow colour which the HomeComponent has set. Also notice in the "Console" table of the browser developer tools, this message (New Instance of Service Created) is logged only once when we navigate between HomeComponent and EmployeeListComponent which proves that only a single instance of the service (ie. a singleton) is created. This singleton enables data sharing between both the components. That's how we are able to see the changes made by one component in the other component.

angular service singleton example

So bottom line, with dependency injection it is easy to share data and functionality as the angular injector provides a Singleton i.e a single instance of the service.

Angular 2 tutorial for beginners

1 comment:

  1. Please provide a tutorial on debugging type script in VScode or chrome debugger.

    ReplyDelete

It would be great if you can help share these free resources