Creating a Reusable and Injectable Service in Angular
In our previous tutorial, we saw how we can make API calls using HTTP in Angular. But the problem with our approach was that we are making HTTP requests within the component, though this works it’s not recommended to do too much within the components. That’s why we have injectable services.
Using Services we can move our API calls and common operations into services. This makes your code more organized and testable. Services can also be reused, so you define once and inject them in whichever component you want to use it.
So in this tutorial, we will move our HTTP request into an Injectable service.
Throughout the article the word Angular refers to Angular 2 and above unless mentioned specifically otherwise. This tutorial uses Angular 4.
Recap
It’s great if you are following along from our previous tutorial. If not then no problem. You can either checkout the repo just like below to get the updated code.
$ git checkout http-request
Or you can just treat this tutorial as an independent one. In either case, you will be learning how to create reusable services in Angular 2+ applications.
This is how our the-quote component looks like right now.
import { Component, OnInit } from '@angular/core';
/** New imports below */
import { Http, Response } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';
import 'rxjs/add/observable/throw';
@Component({
selector: 'app-the-quote',
templateUrl: './the-quote.component.html',
styleUrls: ['./the-quote.component.css']
})
export class TheQuoteComponent implements OnInit {
myQuote : any = {
id: 1,
quote: String,
by: String
}
constructor( private http: Http) {
}
ngOnInit() {
this.http.get('http://quotes.rest/qod').map((res : Response)=>{
return res.json();
})
.catch((err: Response|any)=>{
return Observable.throw(err.json());
}).subscribe((data)=>{
this.myQuote.quote = data.contents.quotes[0].quote; //set our myQuote Object
this.myQuote.by = data.contents.quotes[0].author; //set our myQuote Object
}, (err)=>{
console.log(err);
this.myQuote.quote = "Error in fetching data"; //set our myQuote Object
this.myQuote.by = "Error in fetching data"; //set our myQuote Object
});
}
}
If you see we are making a server request to fetch our quote upon component initialization. Somethings not right, HTTP calls aren’t supposed to be within component classes. So we should move this API call into a reusable service.
Creating a service
To create a new service using the Angular CLI we run the following command.
This will create two files for us; thequote.service.ts and the-quote.service.spec.ts.
Now let’s add a getQuote method to our service, this method would be responsible for calling our API and fetching the data.
import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';
import 'rxjs/add/observable/throw';
@Injectable() //DOn't forget to add this
export class TheQuoteService {
constructor(private http: Http) { }
getQuote(): Observable <any>{
return this.http.get('http://quotes.rest/qod').map(response => response.json().contents)
.catch((err: Response|any)=>{
return Observable.throw(err.statusText);
});
}
}
If you see above we have added Injectable() to our service, which is required because our service is injecting other services as a dependency of its own. However, it’s best to add @Injectable() for every service you create as a practice.
We initialize http in our constructor and then we define our function getQuote. One thing to note here is that we are mapping our response to contents. So our getQuote service will directly return response.contents to our service.
Now we have our service ready, but we can’t use it yet. That is because we haven’t added out service as providers in out NgModule. Open up app.module.ts
...
..
//services
import { TheQuoteService } from './the-quote/the-quote.service';
..
..
..
@NgModule({
...
....
providers: [TheQuoteService],
..
....
})
export class AppModule { }
Now we are ready to use our service into our component.
import { Component, OnInit } from '@angular/core';
/** New imports below */
import { TheQuoteService } from './the-quote.service'; // import our service
@Component({
selector: 'app-the-quote',
templateUrl: './the-quote.component.html',
styleUrls: ['./the-quote.component.css']
})
export class TheQuoteComponent implements OnInit {
myQuote : any = {
id: 1,
quote: String,
by: String
}
constructor( private thequoteservice: TheQuoteService) { //initialize
}
ngOnInit() {
this.thequoteservice.getQuote().subscribe((data)=>{ //use methods in our service
this.myQuote.quote = data.quotes[0].quote;
this.myQuote.by = data.quotes[0].author;
}, (err)=>{
this.myQuote.quote = err;
this.myQuote.by = err;
});
}
}
This is what our component look like now. Much cleaner and organized. Now run the app using ng serve.
We are displaying our quote, but this time our service is used to communicate to the server.
Conclusion
Service are the right place to put common business logic, server communication and any other code that should be reused. Using services makes our application more organized and testable. In this tutorial, we learned how we can create reusable services in Angular applications. Hope this will help you build better and beautiful Angular applications.
Stay tuned as we will look into more awesome things in Angular 2+ in out further tutorials.
Further Links
- Learn Angular 2 From our Free Video Course on YouTube
- Learn Angular 2 by building 12 apps
Leave a Comment