Services In Angular2 [Updated]

This repo contains a small example that discusses use cases of services and also compares Angular2 services with Angular1 services.

Well, whenever we think about services, two common use cases come into our minds:

To demonstrate above use cases, let us take a following example:

Suppose, there is a cinema named as ABC. To keep our example simple, let us assume that cinema has 10 seats only and it sells tickets either through a ticket window or through a ticket booking site named bookshow.com.

So let's break our application into small parts:

Now, let's add some code to these components in order to join these parts and make them work.

Here is the app.component.ts file:

import {Component} from '@angular/core';
import {WindowComponent} from "./window.component";
import {BookShowComponent} from "./book-show.component";
@Component({
    selector: 'my-app',
    template: `
    <cinema-window></cinema-window>
    <book-show></book-show>
    `,
    directives: [WindowComponent, BookShowComponent],
})

export class AppComponent {
}

In the above code, we have simply added two child components i.e. WindowComponent and BookShowComponent.

Now, let's have a look at these two components:

window.component.ts

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

@Component({
    selector: 'cinema-window',
    template: `
    <div>
        <h1>ABC Cinemas</h1>
        <span>Hello Admin</span>
        <p>Currently, Number of Tickets available are: {{ticketCount}}</p>
        <button (click)="bookTicket()">Book Ticket</button>
        <button (click)="showTicket()">Show Ticket</button>
    </div>
    `
})

export class WindowComponent {
    ticketCount = '';
    bookTicket = () => {
    };
    showTicket = () => {
    };
}

We have two functions : bookTicket and showTicket in the WindowComponent. As the name suggests bookTicket component will be used to book tickets while showTicket component will be used to display the ticket details.

We also have a variable ticketCount which is empty so far but will be displaying the number of tickets available.

Before moving ahead, let's have a look at the BookShowComponent too:

book-show.component.ts

import {Component} from 'angular2/core';

@Component({
    selector: 'book-show',
    template: `
    <div>
        <h1>Welcome to bookshow.com</h1>
        <span>Welcome User</span>
        <p>Currently, Number of Tickets available are: {{ticketCount}}</p>
        <button (click)="bookShow()">Book Ticket</button>
        <button (click)="showMyTicket()">Show Ticket</button>
    </div>
    `
})

export class BookShowComponent {
    ticketCount = ";
    bookShow = () => {
    };
    showMyTicket = () => {
    }
}
import {Component} from '@angular/core';

@Component({
    selector: 'book-show',
    template: `
    <div>
        <h1>Welcome to bookshow.com</h1>
        <span>Welcome User</span>
        <p>Currently, Number of Tickets available are: {{ticketCount}}</p>
        <button (click)="bookShow()">Book Ticket</button>
        <button (click)="showMyTicket()">Show Ticket</button>
    </div>
    `
})

export class BookShowComponent {
    ticketCount ';
    bookShow = () => {
    };
    showMyTicket = () => {
    };
}

Well, BookShowComponent also looks pretty much the same.

So now its time to get into some more action. The first use case that we discussed for services was data sharing amongst the components.

Hence, we are making a booking service here, which will give the count of tickets available. Here is the service:

booking-service.ts

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

@Injectable()
export class BookingService {
    totalTicketCount:number = 10;
}

We have hardcoded the ticket count in this service to 10. We have named the above file as booking-service.ts. It is a common practice to name the service files with -service suffix.

Now we want this service to be exposed to our BookShowComponent and the WindowComponent. To achieve let's add the following lines to our app.component.ts:

import {BookingService} from "./booking-service";

Above statement is an import statement while below code needs to be added to the @Component decorator.

providers: [BookingService]

Now, our app.component.ts would look like:

import {Component} from '@angular/core';
import {BookingService} from "./booking-service";
import {WindowComponent} from "./window.component";
import {BookShowComponent} from "./book-show.component";
@Component({
    selector: 'my-app',
    template: `
    <cinema-window></cinema-window>
    <book-show></book-show>
    `,
    directives: [WindowComponent, BookShowComponent],
    providers: [BookingService]
})

export class AppComponent {
}

Any service that we want to use, needs to be injected in providers. Now let's see how to use this service in BookShowComponent and WindowComponent.

import {Component} from '@angular/core';
import {BookingService} from "./booking-service";

@Component({
    selector: 'cinema-window',
    template: `
    <div>
        <h1>ABC Cinemas</h1>
        <span>Hello Admin</span>
        <p>Currently, Number of Tickets available are: {{ticketCount}}</p>
        <button (click)="bookTicket()">Book Ticket</button>
        <button (click)="showTicket()">Show Ticket</button>
    </div>
    `
})
export class WindowComponent {
    constructor(public bookingService:BookingService) {
    }
    ticketCount = this.bookingService.totalTicketCount;
    bookTicket = () => {
        this.bookingService.totalTicketCount = this.bookingService.totalTicketCount - 1;
        this.ticketCount = this.bookingService.totalTicketCount;
    };
    showTicket = () =>{
    }
}

After making similar changes to the BookShowComponent, it will look like this:

import {Component} from '@angular/core';
import {BookingService} from "./booking-service";

@Component({
    selector: 'book-show',
    template: `
    <div>
        <h1>Welcome to bookshow.com</h1>
        <span>Welcome User</span>
        <p>Currently, Number of Tickets available are: {{ticketCount}}</p>
        <button (click)="bookShow()">Book Ticket</button>
        <button (click)="showMyTicket()">Show Ticket</button>
    </div>
    `
})

export class BookShowComponent {
    constructor(public bookingService:BookingService) {
    }

    ticketCount = this.bookingService.totalTicketCount;
    bookShow = () => {
        this.bookingService.totalTicketCount = this.bookingService.totalTicketCount - 1;
        this.ticketCount = this.bookingService.totalTicketCount;
    };
    showMyTicket = () => {
    }
}

Now, let's discuss the changes that we have made:

constructor(public bookingService:BookingService) {
}

In the ticketCount variable we have assigned the totalTicketCount which is given by the service BookingService.

So and once user clicks on the Book Ticket button in the WindowComponent, bookShow() function is called, where totalTicketCount shared by the BookingService is decremented by 1 and the new bookingService.totalTicketCount is then assigned to ticketCount to update on the view.

Supposing that bookShow() function has been called once in the WindowComponent, now the totalTicketCount would be 9. Now, let's move to BookShowComponent, and click on the Book Ticket button here and you will notice that, ticketCount would now become 9-1 i.e. 8 here.

Let's book another ticket through WindowComponent and see that available count would now change to 7.

Note: Currently, there is one flaw in the application i.e. once we change the totalTicketCount from one component, it should get updated on the view of the second component, but this part is currently out of the scope of this post.

Now, let's move on to our second use case, i.e. making http requests. To start with, here is your hardcoded json from which we would be fetching the ticket details.

ticketData.json

{
  "cinemaName" : "ABC Cinemas",
  "showTime" : "9:30PM",
  "date": "25-04-2016",
  "seatNumber": "A1",
  "ticketNumber": 1362196405309
}

Now, let's make myTicket-service which will make http request. Here we go:

myTicket-service.ts

import {Injectable} from "@angular2/core";
import {Http} from '@angular/http';

@Injectable()
export class MyTicketService {
    constructor(public http:Http) {
    }

    getTicketData() {
        return this.http.get("./ticketData.json")
            .map(function (response) {
                return response.json()
            });
    }
}

Here we are importing Http and have a look at the getTicketData() function where we are making our http request. Response from the request is then fed into a map, where the response is being converted into JSON.

Well.. the story doesn't ends here. Now, let's go back to WindowComponent. We had made a showTicket() function here, which unfortunately as of now is not doing anything. So its time to make it work:

import {Component} from '@angular/core';
import {BookingService} from "./booking-service";
import {MyTicketService} from "./myTicket-service";

@Component({
    selector: 'cinema-window',
    template: `
    <div>
        <h1>ABC Cinemas</h1>
        <span>Hello Admin</span>
        <p>Currently, Number of Tickets available are: {{ticketCount}}</p>
        <button (click)="bookTicket()">Book Ticket</button>
        <button (click)="showTicket()">Show Ticket</button>
        <div class="box" [hidden]="!dataAvailable">
            <span>Your Ticket Details:</span>
            <ul class="li-style">
                <li>{{ticketData.cinemaName}}</li>
                <li>{{ticketData.showTime}}</li>
                <li>{{ticketData.date}}</li>
                <li>{{ticketData.seatNumber}}</li>
                <li>{{ticketData.ticketNumber}}</li>
            </ul>
        </div>
    </div>
    `
})

export class WindowComponent {
    constructor(public bookingService:BookingService, public myTicketService:MyTicketService) {
    }

    ticketData = {};
    dataAvailable:boolean = false;
    ticketCount = this.bookingService.totalTicketCount;
    errorMessage = '';
    bookTicket = () => {
        this.bookingService.totalTicketCount = this.bookingService.totalTicketCount - 1;
        this.ticketCount = this.bookingService.totalTicketCount;
    };
    showTicket = () => {
        this.myTicketService.getTicketData()
            .subscribe(
            (data) => {
                this.ticketData = data,
                    this.dataAvailable = true
            },
            (error) => {
                this.errorMessage = error;
            }
        );
    }
}

To start from the top, changes that I have made in the WindowComponent are:

Let's make the similar changes to BookShowComponent. So, BookShowComponent would look something like:

import {Component} from '@angular/core';
import {BookingService} from "./booking-service";
import {MyTicketService} from "./myTicket-service";

@Component({
    selector: 'book-show',
    template: `
    <div>
        <h1>Welcome to bookshow.com</h1>
        <span>Welcome User</span>
        <p>Currently, Number of Tickets available are: {{ticketCount}}</p>
        <button (click)="bookShow()">Book Ticket</button>
        <button (click)="showMyTicket()">Show Ticket</button>
        <div class="box" [hidden]="!dataAvailable">
            <span>Your Ticket Details:</span>
            <ul class="li-style">
            <li>{{ticketData.cinemaName}}</li>
            <li>{{ticketData.showTime}}</li>
            <li>{{ticketData.date}}</li>
            <li>{{ticketData.seatNumber}}</li>
            <li>{{ticketData.ticketNumber}}</li>
            </ul>
        </div>
    </div>
    `
})

export class BookShowComponent {
    constructor(public bookingService:BookingService, public myTicketService:MyTicketService) {
    }

    ticketCount = this.bookingService.totalTicketCount;
    ticketData = {};
    dataAvailable:boolean = false;
    errorMessage = '';
    bookShow = () => {
        this.bookingService.totalTicketCount = this.bookingService.totalTicketCount - 1;
        this.ticketCount = this.bookingService.totalTicketCount;
    };
    showMyTicket = () => {
        this.myTicketService.getTicketData()
            .subscribe(
            (data) => {
                this.ticketData = data
                    this.dataAvailable = true
            }
            , (error) => {
                this.errorMessage = error;
            }
        );
    }
}

Now, we need to make the one last change in order to make everything work. Here it is :

In our main.ts, we need to import rxjs and http so that they can be used throughout the application:

main.ts

import {bootstrap}      from '@angular/platform-browser-dynamic';
import {AppComponent}   from './app.component';
import {HTTP_PROVIDERS} from '@angular/http';
import 'rxjs/Rx';

bootstrap(AppComponent, [HTTP_PROVIDERS]);

We are passing HTTP_PROVIDERS to bootstrap(). http module of Angular2 exposes HTTP_PROVIDERS which has the providers required for making http requests.

You can notice that we are also importing rxjsso now the question that is coming to our mind is what are rxjs. Well let's have a look at these in brief.

rxjs is a library by Microsoft which is being used in Angular 2 for making async calls. So when we make a call suppose http.get(), an Observable object is returned. Observables are though similar to Promises and help in managing async calls they still are different from Promises.

Also, we need to make one last change that is registering our MyTicketService in the app.component.ts:

import {Component} from '@angular/core';
import {BookingService} from "./booking-service";
import {MyTicketService} from "./myTicket-service";
import {WindowComponent} from "./window.component";
import {BookShowComponent} from "./book-show.component";
@Component({
    selector: 'my-app',
    template: `
    <cinema-window></cinema-window>
    <book-show></book-show>
    `,
    directives: [WindowComponent, BookShowComponent],
    providers: [BookingService, MyTicketService]
})

export class AppComponent {
}

Well, now if you run the code, you would be able to get the ticket details.

Before we end this blog it would be important for us to discuss the major difference between the Services in Angular 1.x and Services in Angular2:

Services in Angular 1.x are singleton i.e. you would get one object for the entire application but that is not the case in Angular2. You had seen that in our app.ts, we had done:

providers: [BookingService, MyTicketService]

In the above line we had registered our providers BookingService and MyTicketService. Since both of these providers are being used all throughout the application, also we had to share data between our components i.e. between both the child components, the ideal place to register our both the providers was in AppComponent. Had we registered our provider separately in each component, then we would have got the separate instance of that provider in each component.

So now suppose that original totalTicketCount is 10 and booking the ticket from WindowComponent would have decreased the count to 9 and then on making a booking from BookShowComponent, the count would change to 9. 9? But why 9? Because there would be different instances of BookingService in the WindowComponent and the BookShowComponent.

Hence, this is the major difference between the services in Angular 1.x and Angular2.

Follow Me

Github

Twitter

LinkedIn

More Blogs By Me