Creating services – Angular Services and the Singleton Pattern
Services in Angular are TypeScript classes that aim to implement business logic for our interfaces. Business logic in a frontend project can seem like a controversial issue because ideally, all logic and processing should take place on the backend, which is correct.
Here we are using business rules; these rules are generic behaviors that do not depend on a visual component and can be reused in other components.
Examples of frontend business rules could be as follows:
• Application state control
• Communication with the backend
• Information validations with a fixed rule, such as the number of digits in a telephone number
We are going to put this concept into practice, and in our gym diary application, we are going to create the first service. In the command line we will use the Angular CLI:
ng generate service diary/services/ExerciseSets
Unlike the component, we can see that the element created by the Angular CLI is composed only of a TypeScript file (and its corresponding unit test file).
In this file, we will see the boilerplate that the Angular CLI generated:
import { Injectable } from ‘@angular/core’;
@Injectable({
providedIn: ‘root’
})
export class ExerciseSetsService {
constructor() { }
}
Here we have a TypeScript class called ExerciseSetsService with a decorator called @Injectable. It is this decorator that characterizes a service in Angular; we will see more details about it later in this chapter.
Let’s refactor our project and place the initial series of sets for our diary in this service.
First, we’ll create the methods that will get the initial list and refresh it in the backend:
private setList?: ExerciseSetList;
getInitialList(): ExerciseSetList {
this.setList = [
{ id: 1, date: new Date(), exercise: ‘Deadlift’, reps: 15, sets: 3 },
{ id: 2, date: new Date(), exercise: ‘Squat’, reps: 15, sets: 3 },
{ id: 3, date: new Date(), exercise: ‘Barbell row’, reps: 15, sets: 3 },
];
return this.setList;
}
refreshList(): ExerciseSetList {
this.setList = [
{ id: 1, date: new Date(), exercise: ‘Deadlift’, reps: 15, sets: 3 },
{ id: 2, date: new Date(), exercise: ‘Squat’, reps: 15, sets: 3 },
{ id: 3, date: new Date(), exercise: ‘Barbell row’, reps: 15, sets: 3 },
{ id: 4, date: new Date(), exercise: ‘Leg Press’, reps: 15, sets: 3 },
];
return this.setList;
}
In the service, we move the initialization and refresh of the journal component into the service, using the getInitialList and refreshList methods.
These methods will be improved when we see the communication with the backend, but here, we are already decoupling the exercise list management business rule from the component that renders the user interface, creating a specific service.
Let’s now consider the method that adds an item to the exercise list:
addNewItem(item: ExerciseSet): ExerciseSetList {
if (this.setList) {
this.setList = […this.setList, item];
} else {
this.setList = [item];
}
return this.setList;
}
The setList attribute of the service can be null, so here we use the TypeScript type guard concept (more details in Chapter 3, TypeScript Patterns for Angular) to manipulate the array. Here, we also use the concept of immutability by returning a new array after adding the new element.
In the DiaryComponent component, we will use the service we created:
export class DiaryComponent {
constructor(private exerciseSetsService: ExerciseSetsService) {}
exerciseList = this.exerciseSetsService.getInitialList();
newList() {
this.exerciseList = this.exerciseSetsService.refreshList();
}
addExercise(newSet: ExerciseSet) {
this.exerciseList = this.exerciseSetsService.addNewItem(newSet);
}
}
In the component, the first thing we can observe is the use of the class constructor, declaring an exerciseSetsService private attribute of type ExerciseSetsService. With this declaration, we have an object instantiated and we refactor our component, replacing the initialization of the list and the refresh action with service methods.
From now on, it is no longer a concern of the component how the exercise list is obtained and managed; this is the responsibility of the service, and we can now use this service in other components if necessary. In this piece of code, you may be wondering why we are using the ExerciseSetsService service if we did not instantiate an object of that class.
Here, we have a great feature of Angular, which is the dependency injection mechanism, and we will delve into this topic next.