Angular Services: How to reuse you business logic throughout you Application

Did you ever want to reuse your business logic in other parts of your app?

Or did you want to have one central instance in your app, managing a certain task?

Then services are made for you. If you answered that questions with no... well, maybe you should continue reading anyways!

Services are a fundamental concept in Angular. They are great for centralizing and reusing business logic.

In this article, we will take a closer look at how they are set up. We will do so with some real-world examples.



What are Services?


Services solve one big problem: They prevent us from copying logic over and over. Instead, they centralize business logic.

Also, they are very useful everywhere in our application.

That is because they can be easily requested via Dependency Injection.

They are also very useful if you want to use the same instance of a class everywhere in your class.

Services are just classes


After all, services are just classes. Other than components, services may only contain logic. They should be completely separated from the view (anything visual). They also should only fulfill one purpose, following the single responsibility principle.

Use Cases


The most common use case is I/O (Input/Output). To get more specific: HTTP requests. Generally, all HTTP requests in Angular are wrapped by a service. Why? Because with the help of Dependency Injection, our code stays highly maintainable. Here is an example:

Imagine you changed the route of your REST-Endpoint. Imagine you called that route in a billion different places. Good luck finding and replacing them all!

By wrapping our Http calls in a service, we know exactly where the change has to be made. And it is only one line affected.


Caching Example


My favorite example for a service is a cached HTTP service. So let's build one together!

So let's say we want to create a wrapper service for a specific REST-Endpoint. Furthermore, we only want to hit the web once. Every additional response is served from a cache.

First, we define what kind of object our endpoint returns. In this case, we do so, by defining the Whatever interface.

interface Whatever{
id: string;
}
In this case, our response object only contains an id. Of course, this object can look however you want.

Next, we create our service. It is a simple injectable with the public getWhatever method. It also has a cache object, where we save the responses we got. Finally, we have a private getWhateverHttpRequest that does the actual Http request.

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { HttpClient } from '@angular/common/http';
// of-operator has to be imported separatly
import 'rxjs/add/observable/of';
import 'rxjs/add/operator/first';
interface Whatever{
id: string;
}
@Injectable()
export class CachedHttpService {
private cache = {};
// Note: I'm using the 4.3 Http client here.
constructor(private http: HttpClient) { }
public getWhatever(id: string):Observable<Whatever> {
if (this.cache[id] == null) {
// If we have no response in chache, reach out to the web
const observable = this.getWhateverHttpRequest(id);
// We need to subscribe to the result, to write the result to our cache
let subscription = observable.first().subscribe( response => {
// Wite the response to cache
this.cache[id] = response;
});
console.log("Cached Http: Read from server");
return observable;
}
else {
//If we have the response in our cache already, just serve that response
console.log("Cached Http: Read from cache")
return Observable.of(this.cache[id]);
}
}

private getWhateverHttpRequest(id: string): Observable<Whatever> {

// Only for test purposes
return Observable.of({id: "result"});
// return this.http.get<Whatever>("anyurl.com/api/any/" + id);
}
}
Every class (or service) served via Dependency Injection, has to be provided somewhere. We do so in our AppModule. We also need to import the HttpClientModule here, if we actually want to make any requests.

Note: I'm using the HttpClient of Angular version 4.3+. If you are on older versions, you can use the old client, as well. However, the syntax might look a little different.

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { HttpClientModule } from '@angular/common/http';
import { CachedHttpService } from './services/cached.http.service';
@NgModule({
declarations: [
AppComponent,
],
imports: [
BrowserModule,
HttpClientModule
],
providers: [ CachedHttpService], // provide our service here
bootstrap: [AppComponent]
})
export class AppModule { }
All that is left now, is to actually use our service somewhere. We do so in our AppComponent.

import { Component, OnInit } from '@angular/core';
import { CachedHttpService } from './services/cached.http.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {

title = 'app';
constructor( private cachedHttpService: CachedHttpService){}
ngOnInit(): void {
this.cachedHttpService.getWhatever("1").subscribe( result => {
console.log("Received Response: " + result.id);
});
this.cachedHttpService.getWhatever("1").subscribe( result => {
console.log("Received Response: " + result.id);
});
this.cachedHttpService.getWhatever("1").subscribe( result => {
console.log("Received Response: " + result.id);
});
}
}

Output



Cached Http: Read from server
Received Response: result
Cached Http: Read from cache
Received Response: result
Cached Http: Read from cache
Received Response: result
As you can see, only the first request is actually hitting the web. Exactly what we wanted to achieve.

Conclusion


I hope you found this article informative. If you did, please share it!
To get informed about new content, go ahead and follow me on twitter @malcoded.
 
Leave a comment