Advertisement
  1. Code
  2. JavaScript
  3. Angular

Beginner's Guide to Angular: Services

Scroll to top
9 min read
This post is part of a series called Beginner's Guide to Angular 4.
Beginner's Guide to Angular: Components
Beginner's Guide to Angular: Routing

Hello there! I hope you've followed along with our tutorials on Angular components and routing. In this post, we'll go on to another interesting concept in Angular: services.

If Angular components are the presentation layer of our application, what will be responsible for actually fetching real data and executing business logic? This is exactly where Angular services come in. The role of an Angular service is to fetch, organise, and eventually share data, models, and business logic across components.

Before we dive into the technical details of an Angular service, let's understand more about its features. This will help you understand which part of the code needs to be placed inside a component, and which part needs to be inside an Angular service.

Here are some important facts about services:

A service is defined with the @Injectable decorator. This tells Angular that the service can be injected into components or other services. We'll talk more about injecting services later.

Services are a place for holding all your business logic and sharing it across components. This makes your application more scalable and maintainable. Often, services are the right spot for interacting with the back-end as well. For example, if you need to make AJAX calls, methods for completing the call can be made inside a service.

Services are singleton classes. You will only have a single instance of a specific service running in your Angular application.

What Is a Service?

Services in Angular are objects that are instantiated only once in the application's lifetime. Data received and maintained by a service can be used across the application. This means that components can fetch data from a service at any time. Dependency injection is used to introduce services inside components.

Let's try to understand how to create a service and use it in an Angular component. You can find the complete source code for this project in our GitHub repo.

Once you have the source code, navigate to the project directory and install the required dependencies using npm install. After the dependencies have been installed, start the application by typing the following command:

1
ng serve

You should have the application running on https://localhost:4200/.

The overall folder structure of our project will be as follows.

1
src
2
--app
3
----components
4
------employee.component.css
5
------employee.component.html
6
------employee.component.ts
7
----services
8
------employee.service.spec.ts
9
------employee.service.ts
10
------employeeDetails.service.ts
11
--app.routing.module.ts
12
--app.component.css
13
--app.component.html
14
--app.component.spec.ts
15
--app.component.ts
16
--app.module.ts
17
--assets
18
--index.html
19
--tsconfig.json

1. Building the Skeleton of the Service

There are two ways of creating a service in Angular:

  1. Manually create folders and files inside the project.
  2. Use the ng g service <path/service_name> command to create a service automatically. When you use this approach, you will automatically get a .service.ts and a .service.spec.ts file in the chosen directory.
1
ng g service components/employee 

2. Creating the Service

Now that the .service.ts file has been created in your project structure, it's time to fill the contents of the service. To do this, you must decide on what the service needs to do. Remember, you can have multiple services, each to perform a specific business operation. In our case, we are going to use employee.service.ts to return a static list of roles to any component that uses it.

Enter the following code in employee.service.ts.

1
import { Injectable } from '@angular/core';
2
3
@Injectable({
4
  providedIn: 'root',
5
})
6
7
export class EmployeeService {
8
  role = [
9
    {'id':'1', 'type':'admin'},
10
    {'id':'2', 'type':'engineer'},
11
    {'id':'3', 'type':'sales'},
12
    {'id':'4', 'type':'human_resources'}
13
  ]
14
    getRole(){
15
    return this.role;
16
  }
17
}

This service just returns a static list of roles to the application. Let's decode the service, line by line.

  1. We import Injectable from the @angular/core library. This is crucial because our services will be used or injected into components. The @Injectable directive allows us to identify services.
  2. Next, we apply the @Injectable decorator. The providedIn property of @Injectable specifies where the injectors will be available. Most of the time, root is assigned as its value. This means the service can be injected at the application level. The other options are any, platform, null, or Type<any>.
  3. We create a class component, with the name EmployeeService. This class has a method getRole, which returns a static array of objects.

3. Create a Component

As mentioned before, services in Angular are used to hold the business logic of the application. In order to show data to the viewer, we need a presentation layer. That's where the traditional class-based Angular components come in, created using the decorator @Component.

You can learn more about Angular components in my previous post in this series. It will help you understand Angular components and create your own component. Create the file employee.component.ts and add the following code to it:

1
import { Component, OnInit } from '@angular/core';
2
import { EmployeeService } from '../services/employee.service';
3
4
@Component({
5
  selector: 'employee',
6
  templateUrl: './employee.component.html'
7
})
8
9
export class EmployeeComponent implements OnInit {
10
11
    role: any;
12
    
13
    constructor(private employeeService: EmployeeService) {		
14
	}
15
    
16
    ngOnInit(): void {
17
        this.role = this.employeeService.getRole()
18
    }
19
 
20
}

Let's break it down:

  1. Import the @Component decorator and invoke it. We specify 'employee' as the selector, and we provide a template URL pointing to the HTML describing the view of the component.
  2. Declare the component class and specify that it implements OnInit. As a result, we can define an ngOnInit event handler which will be called when the component gets created.
  3. In order to use our service, it has to be declared inside the constructor. In our case, you will see private employeeService: EmployeeService in the constructor. With this step, we will make the service accessible across the component.
  4. Since our goal is to load the roles when the employee component is created, we fetch the data inside ngOnInit.

Can this get any simpler? Since the service is a singleton class, it can be reused across multiple components without any performance penalty.

4. Creating a View

Now that we have data in our component, let's build a simple employee.component.html file to iterate through the roles and display them. Below, we use *ngFor to iterate through roles, and display only the type to the user.

1
<h3>Data from employee.service</h3>
2
<ul>
3
    <li *ngFor = "let role of roles">{{role.type}}</li>
4
</ul>

5. Running the Project

We only have one more step before the project gets up and running. We need to make sure that the employee.component.ts file is included in our list of declarations, inside the @NgModule directive.

As seen below, EmployeeComponent is added into the app.module.ts file.

1
//app.module.ts

2
import { NgModule } from '@angular/core';
3
import { BrowserModule } from '@angular/platform-browser';
4
5
import { AppRoutingModule } from './app-routing.module';
6
import { AppComponent } from './app.component';
7
import { EmployeeComponent } from './components/employee.component';
8
9
@NgModule({
10
  declarations: [
11
    AppComponent,
12
    EmployeeComponent
13
  ],
14
  imports: [
15
    BrowserModule,
16
    AppRoutingModule
17
  ],
18
  providers: [],
19
  bootstrap: [AppComponent]
20
})
21
export class AppModule { }

Interestingly, we have not added the service in our list of providers, yet we are able to use the service successfully. Why? Because we have specified that the service is to be provided at the application's root level (i.e. with the providedIn: 'root' parameter). However, keep reading to understand more about a scenario where we do need to mention a service in the providers array of @NgModule.

Also, we need to add the employee element into the app.component.html file.

1
<h1>
2
  Tutorial: Angular Services
3
</h1>
4
<employee></employee>
5
6
<router-outlet></router-outlet>

If we run our app so far, it will look like this:

screenshot of the completed appscreenshot of the completed appscreenshot of the completed app

6. Fetching Data Dynamically From a Service

Now, we are going to fetch data specific to our employee.component.ts.

Let's create a new service to fetch data from an API.

1
import { Injectable } from '@angular/core';
2
import { HttpClient } from '@angular/common/http';
3
@Injectable()
4
export class EmployeDetailsService {
5
    fetchEmployeeDetailsURL = 'https://reqres.in/api/users?page=2'
6
    constructor(private http: HttpClient) { }
7
    fetchEmployeeDetails = () => {
8
        return this.http.get(this.fetchEmployeeDetailsURL);
9
    }
10
}

Now, let's understand our code line by line.

  1. Since we want to fetch data through an AJAX call, it is important to import HttpClient. If you are new to HttpClient, you can learn more about it in another post in this series.
  2. In our EmployeeDetailsService, we are not specifying the provideIn parameter. This means we need to do an additional step to let the entire application know about our injectable service. You'll learn about this in the next step.
  3. HttpClient is itself an injectable service. Declare it in the constructor so it will be injected into the component. In the fetchEmployeeDetails method, we'll use the HttpClient.get method for fetching data from a URL.

7. Registering the Service in app.module

Unlike our first service, it is crucial for us to register the EmployeeDetailsService in app.module.ts since we have not declared the injectable at root level. Here's the updated app.module.ts file:

1
import { NgModule } from '@angular/core';
2
import { BrowserModule } from '@angular/platform-browser';
3
import { HttpClientModule } from '@angular/common/http';
4
import { AppRoutingModule } from './app-routing.module';
5
import { AppComponent } from './app.component';
6
import { EmployeeComponent } from './components/employee.component';
7
import { EmployeDetailsService } from './services/employeeDetails.service';
8
9
@NgModule({
10
  declarations: [
11
    AppComponent,
12
    EmployeeComponent
13
  ],
14
  imports: [
15
    BrowserModule,
16
    AppRoutingModule,
17
    HttpClientModule
18
  ],
19
  providers: [
20
    EmployeDetailsService],
21
  bootstrap: [AppComponent]
22
})
23
export class AppModule { }

If you're following closely, you might have noticed two important changes:

  1. In our app.module.ts file, we need to include EmployeDetailsService in the list of Providers.
  2. We need to import HttpClientModule from @angular/common/http. HttpClientModule has to be included in our list of imports.

That's it—we are now ready to make use of EmployeeDetailsService in our component.

8. Fetching Dynamic Data

In order to accommodate the new service, we are going to make a few changes in our component.

Add a Button to Load the Data

First, we'll add a button to our view. When we click this button, the data will be loaded via an AJAX call. Here's the updated employee.component.html file:

1
<h3>Data from employee.service</h3>

2
<ul>
3
    <li *ngFor = "let role of roles">{{role.type}}</li>

4
</ul>

5
<button (click)="loadEmployeeDetails()">Load Employee Details</button>

6
<ul>
7
    <li *ngFor = "let employee of employeeDetails">{{employee.email}}</li>

8
</ul>

Subscribe to the Getter Function

Next, subscribe to the getter function in the EmployeDetailsService. To achieve this, we will be adding EmployeDetailsService to our constructor in employee.component.ts:

1
import { Component, OnInit } from '@angular/core';
2
import { EmployeeService } from '../services/employee.service';
3
import { EmployeDetailsService } from '../services/employeeDetails.service';
4
5
@Component({
6
  selector: 'employee',
7
  templateUrl: './employee.component.html'
8
})
9
10
export class EmployeeComponent implements OnInit {
11
12
    roles: any;
13
    employeeDetails: any;
14
    constructor(private employeeService: EmployeeService,
15
        private employeeDetailsService: EmployeDetailsService) {		
16
	}
17
    ngOnInit(): void {
18
        this.roles = this.employeeService.getRole()
19
    }
20
21
    loadEmployeeDetails = () => {
22
        this.employeeDetailsService.fetchEmployeeDetails()
23
                                    .subscribe((response:any)=>{
24
                                        this.employeeDetails = response.data;
25
                                    })
26
    }
27
 
28
}

With this change, and on clicking the LoadEmployeeDetails button, we would see the following view.

screenshot of the completed appscreenshot of the completed appscreenshot of the completed app

Conclusion

There you go! We have gradually built an Angular service that can deal with static and dynamic data. Now, you should be able to build your very own Angular services and use them to fetch data through AJAX calls. And you can even implement your business logic in a more reusable fashion.

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.