Handling Errors In Angular - Part Ten

This is actually the second part of my previous article. And here is our Angular Novice to Ninja Roadmap.

So far in the implementation, we assume that the call to the server always succeeds but in reality that’s not the case.

So here in ngOnInit(), we’re calling getPosts() method of our services. Let me simulate the scenario where the call to the server fails. So back in posts service let’s change the url to invalid url. Now let’s see what happens in the browser. Now back in the browser, the list of posts is not appearing.

Handling Errors In Angular

But the user looking at this page? They have no clue what is going on under the hood. So we need to change our implementation and handle errors and give proper error messages to our user.

When it comes to handling errors, we have 2 types of errors.

  • Unexpected

    • Server is offline
      Client sends the request to the server but server is not up and running to respond.

    • Network is down
      So the server is online but the client cannot reach it. So as we’ve changed the url and make it invalid, we simulated this scenario. We could not reach the destination.

    • Unhandled Exceptions
      When we have an unhandled exception in our API,  the server is up and running, the network is fine, we call the server but because of a bug the request fails and results in unhandled exception.
  • Expected
    Generally speaking there are 2 types of expected errors that we need to handle in our implementation.
    • Not Found (404)
      What are these errors? Imagine there are 2 users looking at the same page at the same time, User A deletes a post but that post is still visible on the User B screen. So if User B tries to delete that post, the server responds with the Not Found message. And in http protocol, it responds with the status code 404. Whenever we want to get the object from the server or update or delete it is possible that API endpoint will respond with 404.

    • Bad Request (400)
      Imagine we’re building a signup form. Here we need to put the username, password and a few other fields as well. When we call the server to create the new account, it is possible that we have an account with the same username. So in that case, the server may respond with an error message which we call the bad request. And the status code in http protocol is 400.

So the server should tell the user that -- hey there is an account with the same username.

Handling Unexpected Errors

Now let’s see how to handle unexpected errors. So here in posts component in ngOnInit(), look at the subscribe method. Here we’ve passed the function working with response

  1. ngOnInit() {  
  2.   this.service.getPosts()  
  3.     .subscribe(response => {  
  4.       this.posts = response.json();  
  5.     });  
  6. }  

Now this subscribe() has another optional parameter.

Handling Errors In Angular 

Look here we have error parameter which is optional and here we need to pass the function that takes the error parameter and returns void. So here we pass once again an arrow function.

  1. ngOnInit() {  
  2.   this.service.getPosts()  
  3.     .subscribe(response => {  
  4.       this.posts = response.json();  
  5.     }, error => {  
  6.       alert('An Unexpected Error Occured.');  
  7.     });  
  8. }  

Now in a real world application, it is better to use Toast Notification instead of native alert function in javascript. These toast notifications are more user friendly. In case you don’t know what we mean by toast notification, think of the notification we get on facebook which is just the popup on the top right corner of the screen. It doesn’t freeze the browser, and automatically disappears after 5 seconds. And also the user can immediately close that. Also in the real world application, when the unexpected error occurred we want to log the error.

So now don’t worry about how to log the errors but to simulate this scenario, let’s write the errors on the console. And it is just for demonstration.

  1. ngOnInit() {  
  2.   this.service.getPosts()  
  3.     .subscribe(response => {  
  4.       this.posts = response.json();  
  5.     }, error => {  
  6.       alert('An Unexpected Error Occured.');  
  7.       console.log(error);  
  8.     });  
  9. }  

So instead of logging the error on the console, we need to store it somewhere in a database on the server. One more thing, always use the best approach to write the code which increases the readability and understandability of your code. So here is our final code.

  1. export class PostsComponent implements OnInit {  
  2.   posts: any[];  
  3.   
  4.   constructor(private service:PostService) {  
  5.   }  
  6.   
  7.   ngOnInit() {  
  8.     this.service.getPosts()  
  9.       .subscribe(  
  10.         response => {  
  11.           this.posts = response.json();  
  12.         },   
  13.         error => {  
  14.           alert('An Unexpected Error Occured.');  
  15.           console.log(error);  
  16.         });  
  17.   }  
  18.   
  19.   createPost(input: HTMLInputElement) {  
  20.     let post = { title: input.value };  
  21.     input.value = '';  
  22.   
  23.     this.service.createPost(post)  
  24.       .subscribe(  
  25.           response => {  
  26.             post['id'] = response.json().id;  
  27.             this.posts.splice(0, 0, post);  
  28.             console.log(response.json());  
  29.           },   
  30.           error => {  
  31.             alert('An Unexpected Error Occured.');  
  32.             console.log(error);  
  33.           });  
  34.   }  
  35.   
  36.   updatePost(post) {  
  37.     this.service.updatePost(post)  
  38.       .subscribe(  
  39.         response => {  
  40.           console.log(response.json());  
  41.         },   
  42.         error => {  
  43.           alert('An Unexpected Error Occured.');  
  44.           console.log(error);  
  45.         });  
  46.   }  
  47.   
  48.   deletePost(post) {  
  49.     this.service.deletePost(post.id)  
  50.       .subscribe(  
  51.         response => {  
  52.           let index = this.posts.indexOf(post);  
  53.           this.posts.splice(index, 1);  
  54.         },   
  55.         error => {  
  56.           alert('An Unexpected Error Occured.');  
  57.           console.log(error);  
  58.         });  
  59.   }  
  60. }  

Now let’s implement this error alert in all the cases. And run the application and see the results with invalid url.

  1. private url = 'https://avjsonplaceholder.typicode.com/posts';  

And if you run the application, you’ll see the alert in output.

Handling Expected Errors

Now let’s see how to handle expected errors. So here in deletePost(), we know that it is possible that the post with given id doesn’t exist on the server. So our API endpoint is going to return 404 message. If that’s the case we want to tell the user that this post has already been deleted.

So here (deletePost()) in error handler, we need to check the status of the response. As we already know that error parameter is of any type. So we need to use type innotations to work them perfectly. And to simulate the error, let’s provide the invalid id.

  1. deletePost(post) {  
  2.   // this.service.deletePost(post.id)  
  3.   
  4.   // invalid id  
  5.   this.service.deletePost(345)  
  6.     .subscribe(  
  7.       response => {  
  8.         let index = this.posts.indexOf(post);  
  9.         this.posts.splice(index, 1);  
  10.       },  
  11.       // When we're working with arrow functions we always need to put  
  12.       // parameters in brackets if we more than 1 parameter or when we're using   
  13.       // type annotation inside.   
  14.       (error: Response) => {  
  15.         if(error.status === 404)  
  16.           alert('This Post Is Already Been Deleted');  
  17.         else {  
  18.           // We wanna display generic error message and log the error  
  19.           alert('An Unexpected Error Occured.');  
  20.           console.log(error);  
  21.         }  
  22.       });  
  23. }  

Because this fake service (JsonPlaceHolder) has only 100 posts and the id of last posts is 100, so run the application and see the result.

Handling Errors In Angular 

Now let’s take a look at another example. So here we go on createPost() method.

  1. createPost(input: HTMLInputElement) {  
  2.   let post = { title: input.value };  
  3.   input.value = '';  
  4.   
  5.   this.service.createPost(post)  
  6.     .subscribe(  
  7.         response => {  
  8.           post['id'] = response.json().id;  
  9.           this.posts.splice(0, 0, post);  
  10.           console.log(response.json());  
  11.         },   
  12.         (error: Response) => {  
  13.           if(error.status === 400) {  
  14.           // now let's imagine the general alert message. Let's image we've a form  
  15.           // And in Reactive form article, we see how form object works in applicaton.  
  16.           // if it exists.  
  17.             this.form.setErrors(error.json());    // We know that it will not compile here.  
  18.           }  
  19.           else {  
  20.             alert('An Unexpected Error Occured.');  
  21.             console.log(error);  
  22.           }  
  23.         });  
  24. }  
  25.   
  26. deletePost(post) {  
  27.   this.service.deletePost(post.id)  
  28.     .subscribe(  
  29.       response => {  
  30.         let index = this.posts.indexOf(post);  
  31.         this.posts.splice(index, 1);  
  32.       },  
  33.       (error: Response) => {  
  34.         if(error.status === 404)  
  35.           alert('This Post Is Already Been Deleted');  
  36.         else {  
  37.           alert('An Unexpected Error Occured.');  
  38.           console.log(error);  
  39.         }  
  40.       });  
  41. }  

So this is how we handle expected and non expected errors in the application.

Throwing Application Specific Errors

Still here is the problem in code (deletePost() in component). It is violating the principal of SoC.

  1. (error: Response) => {  
  2.    if(error.status === 404)  

Earlier we created a service to encapsulate all the details about consuming http services on our backend. We introduced the service to hide all the complexity of our component. But in the above code, we’re looking at the response object on the server and we also checking the status of the response. This is not the language of our component, this is the language of our service. So we should change this implementation and move this logic inside our service. And here in the component, instead of working with Response object we need to work with an object that is part of our application domain, something that is not specific to http protocol. So let’s see how can we change the implementation.

So let’s go back to our posts service, here in deletePost() method  there is an error or some kind of exception and I want to catch the error. And instead of sending the response object to our component, I want to send a different kind of object. An object that is part of our application domain. So first let’s see how can we catch the error.

  1. deletePost(id) {  
  2.   return this.http.delete(this.url + '/' + id);  
  3. }  

As we know that http methods return observable. Observable is a type that is part of a 3rd party library called Reactive extensions. And this type has a bunch of useful methods that we call operators. But these operators are not there by default,

Handling Errors In Angular 

Look here we’ve some methods in Observable. But here we need catch/catchError method or I would say catch/catchError operator. Actually these operators are the part of rxjs library of Angular.

Now you might think what is Rxjs?

It is an API or library for asynchronous programming where we use observable objects.

In every front end application, we make the connection through API to communicate the front end with backend. And in this communication we use http client. In Angular, we’ve built-in http protocol. And we just simply import it and use it in our application. HttpClient and many other modules use the library RxJS. RxJS library has the speciality that it has Observable. And we already discussed Observable in our previous article in detail. RxJS itself a simple library like jquery and RxJS helps a lot in asynchronous operations. Asynchronous means multiple tasks at a time. So with the help of asynchronous, our code executes without any blockage.

Asynchronous Programming is a very special thing in Javascript and initially we deal it with simple callbacks like we mostly do in jquery.

  1. $("button").click(function(){  
  2.     $("p").hide("slow"function(){  
  3.         alert("The paragraph is now hidden");  
  4.     });  
  5. });  
  6. $("button").click(function(){  
  7.     $("p").hide(1000);  
  8.     alert("The paragraph is now hidden");  
  9. });  

Look at this code, when the control executes the first statement which is the click event of button containing callback function. Until the callback function executes successfully, our next jquery statement will never be executed. This is how we’re implementing asynchronous programming here. But the problem with this approach is when we’re working with jquery, our single html element can contain many callback functions on different events and effects. This is how it contains a lot of nesting in the code. This is what we say callback hell.

Then Promise comes into the picture to solve this problem. It works the same as we’re working with callbacks. When we call the function it returns the promise object and it becomes resolved when function successfully complete otherwise it rejects.

  • getPromise()
  • .then(getPosts)
  • .then(getCourses);

Here we use then() function to call the function. And it makes our code a lot simpler and we implement functional programming in which we just make the functions of different tasks and call it in then(). This is how we write the code in just one single statement instead of nesting our code.

We’ve already discussed the story of Observable. But you might wonder what the difference between Promise and Observable is? Where we use Promise and where we use Observable, you can get the idea from here.

RxJS Operator

Actually RxJS code of implementation varies. At this time, Angular 6 has been released. Normally, we install everything through npm and don’t specify its version to install. So everytime we install the latest version of different packages.

Open the command prompt and run this statement.

ng --version

Handling Errors In Angular 

Here we can see Angular 6 and RxJS 6.1.0 is installed in our machine.

RxJs contains different operators. Be aware, the way of implementation of these operators vary from version to version. Operators are nothing just like functions. Mostly operators operate on Observable object and return the Observable object. This is how we can chain the different operators with http request. So here we want to use the catch operator to handle the exception. The syntax of RxJS 6.1.0 is,

  1. import { nameOfOperatorsCommaSeparatedIfMultiple } from "rxjs/operators";  

So this is the syntax of utilizing the operators in any typescript file. And if we want to use the Observable object in the file:

  1. import { Observable } from "rxjs";  

So obviously when we’re working with Operators we definitely work with Observable. So both these import statements are essential. In RxJS 6.1.0, catch operator replaces with catchError. And when we use operators, we’ll use pipe in 6.1 version. So we were working with deletePost() method,

  1. deletePost(id) {  
  2.   return this.http.delete(this.url + '/' + id)  
  3.     .pipe(  
  4.       map(res => res),  
  5.       catchError((error: HttpErrorResponse) => {  
  6.         return Observable.throw();  
  7.       }));  
  8. }  

Look pipe is nothing but when we combine the functions. That’s why map and catchError operators have commas inside. We’re not making a chain like we do in jquery, we’re combining them here in pipe. And this pipe returns the Observable object of Response. And if you want to get an idea of map operator then you should take a look on this stackoverflow question. Map operator just transforms the result of one form to another form. Here in RxJS 6.1.0 version, catchError takes the argument of HttpErrorResponse rather than simple Response type. So we need the import statement here as well.

  1. import { HttpClient, HttpErrorResponse } from '@angular/common/http';  

One more thing  and let me be clear -- repeating each operator returns Observable object. So in the body of catchError, we’re returning Observable instead of void or any other type. Here we need to return an Observable that has an error so the consumer of this service (which in this case is component) will get that error and do something with that error. And to return the Observable, obviously we need to define the Observable in import statement.

  1. import { Observable, throwError } from "rxjs";  

Here is our catchError method,

  1. catchError((error: HttpErrorResponse) => {  
  2.   return throwError();  
  3. }))  

This throwError() method returns a new Observable that has an error. Now what is the type of that error? The type of that error should be something specific to our application, not the Response object. So we need to create a new class to represent application specific errors.

To define application specific errors, define the typescript class in app/common folder with (app-error.ts) and here we define the class AppError.

  1. export class AppError {      
  2. }  

With the help of this class, we’ll represent the application error. Now here is the catchError() method, we define AppError() object inside throw() function

  1. catchError((error: HttpErrorResponse) => {  
  2.   return throwError(new AppError());  
  3. }));  

Now the good practice to include original error inside this AppError because somewhere we're going to get that error and log it on the server. So,

  1. export class AppError {  
  2.     constructor(public error: any) {}  
  3. }  

And here is the component deletePost() method.

  1. deletePost(id) {  
  2.   return this.http.delete(this.url + '/' + id)  
  3.     .pipe(  
  4.       map(res => res),  
  5.       catchError((error: HttpErrorResponse) => {  
  6.         return throwError(new AppError(error));  
  7.       }));  
  8. }  

So what we’re doing here, we’re catching the error object which is an instance of HttpErrorResponse class and then we’re returning a different kind of error that is specific to our application. Now we need to change this implementation and check for the status of the error if it is 404 or 400, we want to return a different kind of error because in our component we need to know if that post exists or not. And we don’t want to check this status of the response object. So go back in the common folder, and create a new class (not-found-error.ts)

  1. export class NotFoundError {  
  2. }  

Now we want to derive this class from AppError because this is kind of an application error but it is more specific. So,

  1. import { AppError } from './app-error';  
  2. export class NotFoundError extends AppError {  
  3. }  

Now back in our service, we can check the status of the error.

  1. deletePost(id) {  
  2.   return this.http.delete(this.url + '/' + id)  
  3.     .pipe(  
  4.       map(res => res),  
  5.       catchError((error: HttpErrorResponse) => {  
  6.         if (error.status === 404)   
  7.           return throwError(new NotFoundError(error.json()));  
  8.             
  9.         return throwError(new AppError(error.json()));  
  10.       }));  
  11. }  

Now in this case, we don’t want to put the original error here because we don’t want to log this error on the server. And as the argument of the constructor of NotFoundError is, I want to include an error object came from the server because the error object includes data about the invalid fields. So,

  1. return throwError(new NotFoundError(error.json()));  

One last thing is, back in the component instead of working with Response object here in subscribe method,

  1. (error: Response) => {}  

We’re going to work with AppError or one of its derivatives because in posts service, we are throwing AppError or NotFoundError. And since NotFoundError is a kind of AppError, we can say that essentially here we’re catching the response object and throwing an AppError object.

Here is our deletePost() component method,

  1. deletePost(post) {  
  2.   this.service.deletePost(post.id)  
  3.     .subscribe(  
  4.       response => {  
  5.         let index = this.posts.indexOf(post);  
  6.         this.posts.splice(index, 1);  
  7.       },  
  8.       (error: AppError) => {  
  9.         if(error instanceof NotFoundError)  
  10.           alert('This Post Is Already Been Deleted');  
  11.         else {  
  12.           alert('An Unexpected Error Occured.');  
  13.           console.log(error);  
  14.         }  
  15.       });  
  16. }  

Now let’s implement BadRequest error message for createPost() as well. So here is the BadRequest class.

  1. import { AppError } from './app-error';  
  2. export class BadRequest extends AppError {  
  3. }  

Now come on to the Post Service.

  1. import { BadRequest } from './../common/bad-request';  
  2. import { NotFoundError } from './../common/not-found-error';  
  3. import { AppError } from './../common/app-error';  
  4. import { Http } from '@angular/http';  
  5. import { Injectable } from '@angular/core';  
  6. import { Observable, throwError } from "rxjs";  
  7. import { catchError, map } from "rxjs/operators";  
  8. import { HttpClient, HttpErrorResponse } from '@angular/common/http';  
  9.   
  10. @Injectable({  
  11.   providedIn: 'root'  
  12. })  
  13. export class PostService {  
  14.   
  15.   private url = 'https://jsonplaceholder.typicode.com/posts';  
  16.     
  17.   constructor(private http: Http) { }  
  18.   
  19.   deletePost(id) {  
  20.     return this.http.delete(this.url + '/' + id)  
  21.       .pipe(  
  22.         map(res => res),  
  23.         catchError((error: HttpErrorResponse) => {  
  24.           if (error.status === 404)   
  25.             return throwError(new NotFoundError(error));  
  26.               
  27.           return throwError(new AppError(error));  
  28.         }));  
  29.   }  
  30.   
  31.   getPosts() {  
  32.     return this.http.get(this.url);  
  33.   }  
  34.   
  35.   createPost(post) {  
  36.     return this.http.post(this.url, JSON.stringify(post))  
  37.       .pipe(  
  38.         map(res => res),  
  39.         catchError((error: HttpErrorResponse) => {  
  40.           if(error.status === 400)  
  41.             return throwError(new BadRequest(error));  
  42.             
  43.           return throwError(new AppError(error));  
  44.         })  
  45.       )  
  46.   }  
  47.   
  48.   updatePost(post){  
  49.     return this.http.patch(this.url + '/' + post.id, JSON.stringify({ isRead: true }))  
  50.   }  
  51. }  

And now come back again to post component.

  1. import { BadRequest } from './../common/bad-request';  
  2. import { NotFoundError } from './../common/not-found-error';  
  3. import { AppError } from './../common/app-error';  
  4. import { PostService } from './../services/post.service';  
  5. import { Component, OnInit } from '@angular/core';  
  6.   
  7. @Component({  
  8.   selector: 'app-posts',  
  9.   templateUrl: './posts.component.html',  
  10.   styleUrls: ['./posts.component.css']  
  11. })  
  12. export class PostsComponent implements OnInit {  
  13.   posts: any[];  
  14.   
  15.   constructor(private service:PostService) {  
  16.   }  
  17.   
  18.   ngOnInit() {  
  19.     this.service.getPosts()  
  20.       .subscribe(  
  21.         response => {  
  22.           this.posts = response.json();  
  23.         },   
  24.         error => {  
  25.           alert('An Unexpected Error Occured.');  
  26.           console.log(error);  
  27.         });  
  28.   }  
  29.   
  30.   createPost(input: HTMLInputElement) {  
  31.     let post = { title: input.value };  
  32.     input.value = '';  
  33.   
  34.     this.service.createPost(post)  
  35.       .subscribe(  
  36.           response => {  
  37.             post['id'] = response.json().id;  
  38.             this.posts.splice(0, 0, post);  
  39.             console.log(response.json());  
  40.           },   
  41.           (error: AppError) => {  
  42.             if(error instanceof BadRequest) {  
  43.               // this.form.setErrors(error.originalError);  
  44.             }  
  45.             else {  
  46.               alert('An Unexpected Error Occured.');  
  47.               console.log(error);  
  48.             }  
  49.           });  
  50.   }  
  51.   
  52.   updatePost(post) {  
  53.     this.service.updatePost(post)  
  54.       .subscribe(  
  55.         response => {  
  56.           console.log(response.json());  
  57.         },   
  58.         error => {  
  59.           alert('An Unexpected Error Occured.');  
  60.           console.log(error);  
  61.         });  
  62.   }  
  63.   
  64.   deletePost(post) {  
  65.     this.service.deletePost(post.id)  
  66.       .subscribe(  
  67.         response => {  
  68.           let index = this.posts.indexOf(post);  
  69.           this.posts.splice(index, 1);  
  70.         },  
  71.         (error: AppError) => {  
  72.           if(error instanceof NotFoundError)  
  73.             alert('This Post Is Already Been Deleted');  
  74.           else {  
  75.             alert('An Unexpected Error Occured.');  
  76.             console.log(error);  
  77.           }  
  78.         });  
  79.   }  
  80. }  

Now let’s test the application up to this point. So here in our post component in deletePost() send the invalid post id

this.service.deletePost(345)

And now we’re expecting to see the NotFoundError alert. So let’s try this,

Handling Errors In Angular 

And it is working successfully.

  1. import { Observable, throwError } from "rxjs";  

This is the important statement, we define throwError operator here in rxjs with Observable.

Global Error Handling

Now let’s come back again to posts component, and if we see on the different methods of this component we’re repeating these 2 lines again and again in each method. These 2 lines are especially for handling unexpected errors. And now here we’re working with 1 component. In real world applications, we’ll work with 100s of components. And we don’t want to repeat these lines again and again.

  1. error => {  
  2.   alert('An Unexpected Error Occured.');  
  3.   console.log(error);  
  4. });  

When you watch some code that is repeating again and again in different places. It means that something is not right here. We need to refactor the code and make it better. So let’s see how to handle unexpected errors globally.

Once again add a new file in common folder (app-error-handler.ts). And here we want to export the class that is responsible for handling all the unexpected exceptions in our application. And now come to the angular website and search for errorhandler. This is one of the built-in classes in Angular. Now if you look at the implementation of this class, we can see here we have the method called handleError()

Handling Errors In Angular 

This method takes an error parameter of any type and returns void. And in the default implementation of this class in Angular, Angular simply logs this error message in the console. Now we want to provide a different implementation of this class. And in our implementation we’ll display an error message to the user and potentially log this error on the server. And here is an example of how to implement this class in our code.

Handling Errors In Angular 

So now let’s write the code in our application. And cut these 2 repetitive lines and put them into our global error handler.

  1. import { ErrorHandler } from "@angular/core";  
  2.   
  3. export class AppErrorHandler implements ErrorHandler {  
  4.     handleError(error) {  
  5.         alert('An Unexpected Error Occured.');  
  6.         console.log(error);  
  7.     }  
  8. }  

But it is not so simple, we’ll make this alert statement as toast notification and instead of printing the error message on the console, we’ll display it into the server. So it is not so simple. Now let’s register this global error handler (AppErrorHandler) class into our app module.

But let’s implement a different approach, instead of registering the class as an array like this.

  1. providers: [  
  2.   PostService,  
  3.   AppErrorHandler  
  4. ]  

We want to tell Angular where we internally using AppErrorHandler instead of using ErrorHandler. So internally in Angular, there are multiple places where ErrorHandler class I showed you is used. So here we replace this above implementation with this new implementation.

  1. providers: [  
  2.   PostService,  
  3.   {  
  4.     provide: ErrorHandler,  
  5.     useClass: AppErrorHandler  
  6.   }  
  7. ]  

Provide  the name of class which we want to replace and useClass is the property where we add replacement. These two properties are essential here. With the help of this object, we’re telling Angular that where we’re using ErrorHandler, we’ll use this new class AppErrorHandler.

Now let’s go back to our Posts.component and cleanup all the repetitive code. So first in ngOnInit(), we’ll completely remove this error handling function.

  1. ngOnInit() {  
  2.   this.service.getPosts()  
  3.     .subscribe(  
  4.       response => {  
  5.         this.posts = response.json();  
  6.       },   
  7.       error => {  
  8.         alert('An Unexpected Error Occured.');  
  9.         console.log(error);  
  10.       });  
  11. }  

So instead of handling the error here, I want the error to propogate in our application and then eventually it will hit the global error handler. So,

  1. ngOnInit() {  
  2.   this.service.getPosts()  
  3.     .subscribe(  
  4.       response => {  
  5.         this.posts = response.json();  
  6.       });  
  7. }  

Now in the createPost(),

  1.   this.service.createPost(post)  
  2.     .subscribe(  
  3.         response => {  
  4.           post['id'] = response.json().id;  
  5.           this.posts.splice(0, 0, post);  
  6.           console.log(response.json());  
  7.         },   
  8.         (error: AppError) => {  
  9.           if(error instanceof BadRequest) {  
  10.             // this.form.setErrors(error.originalError);  
  11.           }  
  12.           else {  
  13.             alert('An Unexpected Error Occured.');  
  14.             console.log(error);  
  15.           }  
  16.         });  
  17. }  

We still want to keep errorhandler because here we’re checking if error is an instance of BadRequest class but here we’ll slightly change the else block.

  1. (error: AppError) => {  
  2.   if(error instanceof BadRequest) {  
  3.     // this.form.setErrors(error.originalError);  
  4.   }  
  5.   else throw error;  
  6. });  

If we don’t re throw this error, the error which is handled here will never hit our global error handler. So we need to re-throw it.

Now let’s take a look on updatePost(),

  1. updatePost(post) {  
  2.   this.service.updatePost(post)  
  3.     .subscribe(  
  4.       response => {  
  5.         console.log(response.json());  
  6.       });  
  7. }  

And finally in the deletePost, similar to createPost()

  1. deletePost(post) {  
  2.   this.service.deletePost(345)  
  3.     .subscribe(  
  4.       response => {  
  5.         let index = this.posts.indexOf(post);  
  6.         this.posts.splice(index, 1);  
  7.       },  
  8.       (error: AppError) => {  
  9.         if(error instanceof NotFoundError)  
  10.           alert('This Post Is Already Been Deleted');  
  11.         else throw error;  
  12.       });  
  13. }  

Now let’s test the application. So once again test the delete function with invalid Post id 345.

Handling Errors In Angular 

And we get the proper error message. Now let’s simulate an unhandled exception. So let’s change in the url in post service to an invalid url.

  1. private url = 'https://abcjsonplaceholder.typicode.com/posts';  

Now we're back in the browser once again. And here we get an unexpected error alert.

Handling Errors In Angular 

So to handle errors globally, we create a new class that implements ErrorHandler and then register it in AppModule.

Extracting a Reusable Error Handling Method

So now in Post service, we have another room for refactoring.

  1. export class PostService {  
  2.   
  3.   private url = 'https://jsonplaceholder.typicode.com/posts';  
  4.     
  5.   constructor(private http: Http) { }  
  6.   
  7.   deletePost(id) {  
  8.     return this.http.delete(this.url + '/' + id)  
  9.       .pipe(  
  10.         map(res => res),  
  11.         catchError((error: HttpErrorResponse) => {  
  12.           if (error.status === 404)   
  13.             return throwError(new NotFoundError(error));  
  14.               
  15.           return throwError(new AppError(error));  
  16.         }));  
  17.   }  
  18.   
  19.   getPosts() {  
  20.     return this.http.get(this.url);  
  21.   }  
  22.   
  23.   createPost(post) {  
  24.     return this.http.post(this.url, JSON.stringify(post))  
  25.       .pipe(  
  26.         map(res => res),  
  27.         catchError((error: HttpErrorResponse) => {  
  28.           if(error.status === 400)  
  29.             return throwError(new BadRequest(error));  
  30.             
  31.           return throwError(new AppError(error));  
  32.         })  
  33.       )  
  34.   }  
  35.   
  36.   updatePost(post){  
  37.     return this.http.patch(this.url + '/' + post.id, JSON.stringify({ isRead: true }))  
  38.   }  
  39. }  

Look in createPost() and deletePost() both functions have mostly the same code. The only difference is the status number. And also in updatePost() we don’t have error handling because we’re assuming that this API endpoint doesn’t return any predefined error messages. But in real world applications, this API endpoint returns certain kinds of errors. So then we’ll repeat the same code for error handling in updatePost() method as well.

So how can we improve this implementation?

We’ll extract a separate private method called HandleError() and instead of repeating these few lines in every method. We’ll simply delegate that error handling to that new method. So,

  1. private handleError(error: HttpErrorResponse) {  
  2.   if (error.status === 404)   
  3.     return throwError(new NotFoundError(error));  
  4.   
  5.   if(error.status === 400)  
  6.     return throwError(new BadRequest(error));  
  7.   
  8.   return throwError(new AppError(error));  
  9. }  

We’ve defined this method as private because it is completely about the implementation details of this post service. We don’t want the consumer of this service to know about this method. And the consumer of this service is Post component.

Here is our final code after refactoring in post service.

  1. import { BadRequest } from './../common/bad-request';  
  2. import { NotFoundError } from './../common/not-found-error';  
  3. import { AppError } from './../common/app-error';  
  4. import { Http } from '@angular/http';  
  5. import { Injectable } from '@angular/core';  
  6. import { Observable, throwError } from "rxjs";  
  7. import { catchError, map } from "rxjs/operators";  
  8. import { HttpClient, HttpErrorResponse } from '@angular/common/http';  
  9.   
  10. @Injectable({  
  11.   providedIn: 'root'  
  12. })  
  13. export class PostService {  
  14.   
  15.   private url = 'https://jsonplaceholder.typicode.com/posts';  
  16.     
  17.   constructor(private http: Http) { }  
  18.   
  19.   deletePost(id) {  
  20.     return this.http.delete(this.url + '/' + id)  
  21.       .pipe(  
  22.         map(res => res),  
  23.         catchError(this.handleError));  
  24.   }  
  25.   
  26.   getPosts() {  
  27.     return this.http.get(this.url)  
  28.       .pipe(  
  29.         map(res => res),  
  30.         catchError(this.handleError));  
  31.   }  
  32.   
  33.   createPost(post) {  
  34.     return this.http.post(this.url, JSON.stringify(post))  
  35.       .pipe(  
  36.         map(res => res),  
  37.         catchError(this.handleError));  
  38.   }  
  39.   
  40.   updatePost(post){  
  41.     return this.http.patch(this.url + '/' + post.id, JSON.stringify({ isRead: true }))  
  42.       .pipe(  
  43.         map(res => res),  
  44.         catchError(this.handleError));  
  45.   }  
  46.   
  47.   private handleError(error: HttpErrorResponse) {  
  48.     if (error.status === 404)   
  49.       return throwError(new NotFoundError(error));  
  50.   
  51.     if(error.status === 400)  
  52.       return throwError(new BadRequest(error));  
  53.     
  54.     return throwError(new AppError(error));  
  55.   }  
  56. }  

Extracting a Reusable Data Service

Now our post service is in pretty good shape, small methods are very clean. However in a real world application, we don’t have one service. We often have tens of services. So imagine we want to create the service for working with the course's endpoint. With this service, we can create update and delete the course. We can imagine, in that new service our code is going to look almost identical. So we are going to have a method called getCourses() where we call get() method of http request. And this all means that we repeat the same implementation with every other service. So let’s see how can we do it,

We want to extract reusable service working with http endpoint. So in the services folder, we’ll add a new file (data.service.ts) and we copy all the code from post service and paste it into the data.service.ts file. Change the name of the class called DataService. And we rename the things something more generic.

  1. import { BadRequest } from './../common/bad-request';  
  2. import { NotFoundError } from './../common/not-found-error';  
  3. import { AppError } from './../common/app-error';  
  4. import { Http } from '@angular/http';  
  5. import { Injectable } from '@angular/core';  
  6. import { Observable, throwError } from "rxjs";  
  7. import { catchError, map } from "rxjs/operators";  
  8. import { HttpClient, HttpErrorResponse } from '@angular/common/http';  
  9.   
  10. @Injectable({  
  11.   providedIn: 'root'  
  12. })  
  13. export class DataService {  
  14.   
  15. // This url is specific to the post. And we want to make this service class generic  
  16. //   private url = 'https://jsonplaceholder.typicode.com/posts';  
  17.   private url;  
  18.     
  19.   constructor(private http: Http) { }  
  20.   
  21.   delete(id) {  
  22.     return this.http.delete(this.url + '/' + id)  
  23.       .pipe(  
  24.         map(res => res),  
  25.         catchError(this.handleError));  
  26.   }  
  27.   
  28.   getAll() {  
  29.     return this.http.get(this.url)  
  30.       .pipe(  
  31.         map(res => res),  
  32.         catchError(this.handleError));  
  33.   }  
  34.   
  35.   create(resource) {  
  36.     return this.http.post(this.url, JSON.stringify(resource))  
  37.       .pipe(  
  38.         map(res => res),  
  39.         catchError(this.handleError));  
  40.   }  
  41.   
  42.   update(resource){  
  43.     return this.http.patch(this.url + '/' + post.id, JSON.stringify({ isRead: true }))  
  44.       .pipe(  
  45.         map(res => res),  
  46.         catchError(this.handleError));  
  47.   }  
  48.   
  49.   private handleError(error: HttpErrorResponse) {  
  50.     if (error.status === 404)   
  51.       return throwError(new NotFoundError(error));  
  52.   
  53.     if(error.status === 400)  
  54.       return throwError(new BadRequest(error));  
  55.     
  56.     return throwError(new AppError(error));  
  57.   }  
  58. }  

Now register this service in app.module.ts otherwise it will not work.

  1. providers: [  
  2.   DataService,  
  3.   PostService,  
  4.   {  
  5.     provide: ErrorHandler,  
  6.     useClass: AppErrorHandler  
  7.   },  
  8. ]  

So here in this implementation, there is nothing about Posts. We’ve made everything generic. Now let’s go back to our posts service. And delete all the methods because we’ve defined them in the Data service.

  1. export class PostService {  
  2.   private url = 'https://jsonplaceholder.typicode.com/posts';  
  3.   constructor(private http: Http) { }  
  4. }  

So now we’ve just the url and constructor in the Post Service class. Now we want this class to inherit all the code that we defined in data service class. So we’ll use inheritance here.

  1. export class PostService extends DataService {  
  2. }  

But here we’ve lots of compilation errors. Our url property is defined in the base class. So we don’t need to define it again here. And in base class http is the base class member. So, we remove the access modifier here from the constructor parameter.

  1. export class PostService extends DataService {    
  2.   constructor(http: Http) { }  
  3. }  

Now still here we’ve compilation error on constructor.

Handling Errors In Angular 

Look the last line of error “Constructor for derived classes must contain a super call”. Look the base class constructor,

  1. constructor(private http: Http) { }  

This constructor requires an http request. So when creating the DataService object, we need to provide an http object. Now back in post service. So,

  1. constructor(http: Http) {  
  2.   super(http);  
  3. }  

Now the constructor compilation is gone. As we changed the names of DataService methods and make them more generic and we’re requesting the different service name of methods in our posts component, so we’re facing these above errors like “Property ‘deletePost’ does not exist on type ‘PostService’”. And one more thing, still we have an issue of url because we have not even provide the url to our base class. So add one more property in base class constructor.

  1. constructor(private url: string, private http: Http) { } 

Now pass the url and http object from derived class constructor.

  1. constructor(http: Http) {  
  2.   super('https://jsonplaceholder.typicode.com/posts', http);  
  3. }  

And now our PostService is much cleaner, we’ve  less import statements here with only 3 lines of code containing constructor.

  1. import { Http } from '@angular/http';  
  2. import { DataService } from "./data.service";  
  3. import { Injectable } from '@angular/core';  
  4.   
  5. @Injectable({  
  6.   providedIn: 'root'  
  7. })  
  8. export class PostService extends DataService {    
  9.   constructor(http: Http) {  
  10.     super('https://jsonplaceholder.typicode.com/posts', http);  
  11.   }  
  12. }  

Now the final step is, to resolve the errors of wrong naming function call in the component let’s change the name of the methods.

  1. import { BadRequest } from './../common/bad-request';  
  2. import { NotFoundError } from './../common/not-found-error';  
  3. import { AppError } from './../common/app-error';  
  4. import { PostService } from './../services/post.service';  
  5. import { Component, OnInit } from '@angular/core';  
  6. import { throwError } from 'rxjs';  
  7.   
  8. @Component({  
  9.   selector: 'app-posts',  
  10.   templateUrl: './posts.component.html',  
  11.   styleUrls: ['./posts.component.css']  
  12. })  
  13. export class PostsComponent implements OnInit {  
  14.   posts: any[];  
  15.   
  16.   constructor(private service:PostService) {  
  17.   }  
  18.   
  19.   ngOnInit() {  
  20.     this.service.getAll()  
  21.       .subscribe(  
  22.         response => {  
  23.           this.posts = response.json();  
  24.         });  
  25.   }  
  26.   
  27.   createPost(input: HTMLInputElement) {  
  28.     let post = { title: input.value };  
  29.     input.value = '';  
  30.   
  31.     this.service.create(post)  
  32.       .subscribe(  
  33.           response => {  
  34.             post['id'] = response.json().id;  
  35.             this.posts.splice(0, 0, post);  
  36.             console.log(response.json());  
  37.           },   
  38.           (error: AppError) => {  
  39.             if(error instanceof BadRequest) {  
  40.               // this.form.setErrors(error.originalError);  
  41.             }  
  42.             else throw error;  
  43.           });  
  44.   }  
  45.   
  46.   updatePost(post) {  
  47.     this.service.update(post)  
  48.       .subscribe(  
  49.         response => {  
  50.           console.log(response.json());  
  51.         });  
  52.   }  
  53.   
  54.   deletePost(post) {  
  55.     this.service.delete(post.id)  
  56.       .subscribe(  
  57.         response => {  
  58.           let index = this.posts.indexOf(post);  
  59.           this.posts.splice(index, 1);  
  60.         },  
  61.         (error: AppError) => {  
  62.           if(error instanceof NotFoundError)  
  63.             alert('This Post Is Already Been Deleted');  
  64.           else throw error;  
  65.         });  
  66.   }  
  67. }  

Now let’s test the application. And yes it is working fine as we expect.

Map Operator

Let’s understand the map operator with more detail. In all the methods of post component class where we subscribe to our observables, we’re working with the response object. And we don’t want to use the response object here in our component because we’ve added the service to encapsulate all the details about working with the http services on the backend. So ideally instead of using response object and calling the json method on it, I want to get an array of objects.

So let’s go back to our data service, here we’ve already used map operator in service methods.

  1. getAll() {  
  2.   return this.http.get(this.url)  
  3.     .pipe(  
  4.       map(res => res),  
  5.       catchError(this.handleError));  
  6. }  

With the map operator, we can transform the items in an observable. Observables have lots of operators and they are not available by default because they increase the size of application code. So we need to import only the one, we really need it. So we import the operator here.

  1. import { catchError, map } from "rxjs/operators";  

Now back in getAll() method and we write the map statement as,

  1. getAll() {  
  2.   return this.http.get(this.url)  
  3.     .pipe(  
  4.       map(res => res.json()),  
  5.       catchError(this.handleError));  
  6. }  

So we’re mapping and transforming this response object to an array of javascript objects (json). Now let’s go back to our component

  1. ngOnInit() {  
  2.   this.service.getAll()  
  3.     .subscribe(  
  4.       response => {  
  5.         this.posts = response.json();  
  6.       });  
  7. }  

Now instead of getting response object, we get an array of object. So we rename it and as it is one line of code in body, we exclude the curly brackets.

  1. ngOnInit() {  
  2.   this.service.getAll()  
  3.     .subscribe(posts => this.posts = posts);  
  4. }  

Now it is much cleaner than the code we previously had. Now let’s make changes in complete service and component files.

You can download the files attached with the article for a sample. One important thing during these changes on deletePost() method. So it is the code before any change.

  1. deletePost(post) {  
  2.   this.service.delete(post.id)  
  3.     .subscribe(  
  4.       response => {  
  5.         let index = this.posts.indexOf(post);  
  6.         this.posts.splice(index, 1);  
  7.       },  
  8.       (error: AppError) => {  
  9.         if(error instanceof NotFoundError)  
  10.           alert('This Post Is Already Been Deleted');  
  11.         else throw error;  
  12.       });  
  13. }  

When we delete an object with json placeholder, we don’t get anything in the response as usual. So the body of the response is an empty object. So we replace with empty parenthesis.

  1. deletePost(post) {  
  2.   this.service.delete(post.id)  
  3.     .subscribe(  
  4.       () => {  
  5.         let index = this.posts.indexOf(post);  
  6.         this.posts.splice(index, 1);  
  7.       },  
  8.       (error: AppError) => {  
  9.         if(error instanceof NotFoundError)  
  10.           alert('This Post Is Already Been Deleted');  
  11.         else throw error;  
  12.       });  
  13. }  

So this is the final result, we’ve clean components with good SoC and proper error handling.

Optimistic and Pessimistic Updates

 Let’s talk about Optimistic and Pessimistic Updates. If you’re not a Native English speaker, you may not know the meaning of these words.

Optimistic (Hopeful, Positive)

Pessimistic (Hopeless, Negative)

Look we’ve implemented the createPost method. When we enter a new record, it takes few seconds to add it into the list. It is called Pessimistic Update. We’re hopeless, we’re assuming that call to the server will probably fail. And we’ll only add items to the list if we get a successful response from the server.

In contrast, we’ve optimistic updates. So instead of waiting to get the result from the server, we update the user interface immediately. We assume most of the time, the call to the server is going to succeed. Now if it fails for any reason then we need to rollback our changes. A lot of modern applications use the optimistic update approach, these days. Because it makes the application appear faster and smoother. So let’s see how we can implement optimistic update here.

Back to our posts component, in the createPost method we can see that we’re updating our posts array upon receiving a successful response from the server.

  1. createPost(input: HTMLInputElement) {  
  2.   let post = { title: input.value };  
  3.   input.value = '';  
  4.   
  5.   this.service.create(post)  
  6.     .subscribe(  
  7.         newPost => {  
  8.           post['id'] = newPost.id;  
  9.           this.posts.splice(0, 0, post);  
  10.           console.log(newPost);  
  11.         },   
  12.         (error: AppError) => {  
  13.           if(error instanceof BadRequest) {  
  14.             // this.form.setErrors(error.originalError);  
  15.           }  
  16.           else throw error;  
  17.         });  
  18. }  

Now let’s implement optimistic update approach.

  1. createPost(input: HTMLInputElement) {  
  2.   let post = { title: input.value };  
  3.   
  4.   // Optimistic Approach  
  5.   this.posts.splice(0, 0, post);  
  6.     
  7.   input.value = '';  
  8.   
  9.   this.service.create(post)  
  10.     .subscribe(  
  11.         newPost => {  
  12.           post['id'] = newPost.id;  
  13.           // this.posts.splice(0, 0, post);  
  14.           console.log(newPost);  
  15.         },   
  16.         (error: AppError) => {  
  17.           if(error instanceof BadRequest) {  
  18.             // this.form.setErrors(error.originalError);  
  19.           }  
  20.           else throw error;  
  21.         });  
  22. }  

So as soon as we create this post object then we immediately update posts array and then input field clears and then the call goes to server as you can see in the code. If the call fails, I want to rollback the changes. So we need to remove this new post on the top of the array. So,

  1. (error: AppError) => {  
  2.   // Optimistic Rollback  
  3.   this.posts.splice(0, 1);  
  4.   
  5.   if(error instanceof BadRequest) {  
  6.     // this.form.setErrors(error.originalError);  
  7.   }  
  8.   else throw error;  
  9. });  

Now let’s test this in the browser.

Handling Errors In Angular 

And now it is working fine, adding and deleting post in few seconds. Now it is so much faster. Now let’s simulate the scenario where the call to the server fails. So open the data.service. So here in create() method (data.service.ts), I want to temporarily return an Observable that has an error.

  1. create(resource) {      
  2.   return Observable.throw(new AppError());  
  3. }  

Now let’s test the application. And here we’ll see an alert message in the browser.

Now let’s implement the Optimistic update for delete method in posts.component.ts

  1. deletePost(post) {  
  2.   // Optimistic Code  
  3.   let index = this.posts.indexOf(post);  
  4.   this.posts.splice(index, 1);  
  5.   
  6.   this.service.delete(post.id)  
  7.     .subscribe(  
  8.       // Now we're not interested to pass the success function.  
  9.       // () => {  
  10.       //   // let index = this.posts.indexOf(post);  
  11.       //   // this.posts.splice(index, 1);  
  12.       // },  
  13.       null,  
  14.       (error: AppError) => {  
  15.         // Optimistic Resolver  
  16.         this.posts.splice(index, 0, post);  
  17.   
  18.         if(error instanceof NotFoundError)  
  19.           alert('This Post Is Already Been Deleted');  
  20.         else throw error;  
  21.       });  
  22. }  

Now the items are deleting very fast.

Observable vs Promise

As you have seen in this complete article, when we’re working with backend services, we’re dealing with observable objects. Earlier in the article, I’ve told you that we use Promises and Observables to work with asynchronous operations. And if you have worked with other javascript libraries or frameworks, chances are you’ve worked a lot with promises. So you might wonder why the Angular team has used observables instead of promises. So now we’ll discuss the difference of Observable and Promises in details.

So to focus on one thing, I’m going to modify the code in the deletePost() method in our post component. Don’t worry about updating the UI or Error Handling. So delete all the code in deletePost() method and simply call the server.

  1. deletePost(post) {  
  2.   this.service.delete(post.id);  
  3. }  

Now let’s see what happens if we don’t subscribe to this Observable. Save the file and come into the browser. Now delete the post and open the network tab of the browser.

Handling Errors In Angular 

Look here we’ve no request to delete the post. We’ve posts request to show all the posts in the browser. So what I want to show you is, with Observable nothing happens until you subscribe to them. Now,

  1. deletePost(post) {  
  2.   this.service.delete(post.id)  
  3.     .subscribe();  
  4. }  

Now take a look in the network tab.

Handling Errors In Angular 

Now we’ve a request for deleting the post.

So this one of the key differences is,

Observables are lazy, Promises are Eager.

So as soon as you create the promise, the code is executed. Now let’s go back to our data service. I want to change the delete() and instead of returning Observable, I’ll return the Promise here. So we can always convert Observables to Promises, if we really want to but it is not recommended unless you have strong reason to do so. So I prefer to work with Observables. But let’s come back to the point and create the promise and as we’re passing the Observable from service to component, here we’ll also use another operator to convert the object from observable to promise to check the promise difference. We’ve a map operator to transform the object, we’ve another operator toPromise() to convert the object into Promise.

Remember I’m using RxJS 6.1, First of all import the toPromise statement here,

  1. import { toPromise } from "rxjs/operator/toPromise";  

And then change the observable object into Promise.

  1. delete(id) {     
  2.   return this.http.delete(this.url + '/' + id)  
  3.     .pipe(  
  4.       map(res => res.json()),  
  5.       catchError(this.handleError))  
  6.     .toPromise();  
  7. }  

Now back in our posts component,

Handling Errors In Angular 

Because promise don’t have subscribe() method. So here we’ve only 2 methods.

then (for getting the result)

catch (for handling errors)

Let’s make our request simple.

  1. deletePost(post) {  
  2.   this.service.delete(post.id);  
  3. }  

Now go back to our browser. And delete the post, you’ll see the http request to delete the post. Let’s verify this,

Handling Errors In Angular 

So promises are eager. We don’t have to call then() whereas Observables are lazy, nothing happens until subscribe to them.

Now you might be wondering why Observables are lazy? Because there are many operators available on Observable for performing different operations. These operators are very powerful, they allow us to implement certain features with only 1 line of code. So, let’s see one more operator.

  1. import { catchError, map, retry } from "rxjs/operators";  

And here is the code for retry()

  1. delete(id) {     
  2.   return this.http.delete(this.url + '/' + id)  
  3.     .pipe(  
  4.       map(res => res.json()),  
  5.       catchError(this.handleError),  
  6.       retry(3)  
  7.     );  
  8. }  

So if the call to the server fails, this observable will retry 3 times.

And if you want to implement this feature manually in the code, you know you have to use the for loop which iterates 3 times minimum, you need some variable to keep track of failure. Whereas here with observable, we just need to use retry() operator.

There are lots of other operators. And we use these operators for certain features to implement certain functionality in 1 line of code. This is what we called Reactive programming. So reactive extensions or RxJS which is the library where we get observables and their operators, they allow us to write code in reactive style.

You don’t have to worry about all these operators, because as an angular developer we just use 2 operators mostly (map, catchError). Here is the key thing, I want you to take away, with observables we’ve powerful operators that don’t exists in promises and these operators only come into effect when we subscribe to an observable. That’s why we can chain all these operators like map, retry, catchError and nothing happens. So when you call the map() nothing happens, all you’re doing is programming to an Observable object.

  1. delete(id) {     
  2.   return this.http.delete(this.url + '/' + id)  
  3.     .pipe(  
  4.       map(res => res.json()),  
  5.       catchError(this.handleError),  
  6.       retry(3)  
  7.     );  
  8. }  

And later when we subscribe to this observable, all these operators come into effect.

  1. deletePost(post) {  
  2.   this.service.delete(post.id)  
  3.     .subscribe();  
  4. }  

What We Have Learned

Let’s summarize what we’ve learned. So let’s make a small application in which we use the github api to display the followers on the screen. We’ll show the follower avatar, its profile name, description. So let’s start our journey.

Let’s make a separate component ‘github-followers’

PS C:\Users\Ami Jan\HelloWorld\MyFirstAngularProject> ng g c github-followers

Now let’s create the service,

PS C:\Users\Ami Jan\HelloWorld\MyFirstAngularProject> ng g s github-followers

Now open the github-followers.service.ts

  1. import { Injectable } from '@angular/core';  
  2. @Injectable({  
  3.   providedIn: 'root'  
  4. })  
  5. export class GithubFollowersService {  
  6.   constructor() { }  
  7. }  

Now I want this class to extend reusable DataService.

  1. import { DataService } from './services/data.service';  
  2. import { Injectable } from '@angular/core';  
  3.   
  4. @Injectable({  
  5.   providedIn: 'root'  
  6. })  
  7. export class GithubFollowersService extends DataService {  
  8.   constructor() { }  
  9. }  

Now we resolve the constructor requirements in this component. As we’re inheriting the DataService class in this class so we define the constructor also to complete the requirement of base class.

  1. export class GithubFollowersService extends DataService {  
  2.   constructor(http: Http) {  
  3.     super('', http);  
  4.   }  
  5. }  

Now we make our requirements complete. But the url is missing, here we need to pass the url and to know the github followers of any profile. Here we’re using top ranked github profile. Here is the github helpful link to request for the followers.

  1. export class GithubFollowersService extends DataService {  
  2.   constructor(http: Http) {  
  3.     super('https://api.github.com/users/fabpot/followers', http);  
  4.   }  
  5. }  

We’re done with the service. Now let’s go back to our new component. First we need to inject this new service,

  1. export class GithubFollowersComponent implements OnInit {  
  2.   constructor(private service: GithubFollowersService) { }  
  3.   ngOnInit() {  
  4.   }  
  5. }  

Now we need to register our service in app.module.ts

  1. providers: [  
  2.   DataService,  
  3.   PostService,  
  4.   GithubFollowersService,  
  5.   {  
  6.     provide: ErrorHandler,  
  7.     useClass: AppErrorHandler  
  8.   },  
  9. ]  

Now back in the component. Let’s get all the followers in ngOnInit() function.

  1. export class GithubFollowersComponent implements OnInit {  
  2.   followers: any[];  
  3.   constructor(private service: GithubFollowersService) { }  
  4.   ngOnInit() {  
  5.     this.service.getAll()  
  6.       .subscribe(followers => this.followers = followers);  
  7.   }  
  8. }  

And now it is done. Because our purpose was just to show followers. We can’t update and delete them. We’re using someone’s github profile. Now come on to the github-followers.component.html.

And in html here we use bootstrap media object. With the help of this markup we can render the markup like this,

Handling Errors In Angular 

This is a very common layout that you see a lot of social networking websites. So let’s copy this markup and paste it into our html file.

  1. <div class="media">  
  2.   <a class="pull-left" href="#">  
  3.     <img class="media-object" src="..." alt="...">  
  4.   </a>  
  5.   <div class="media-body">  
  6.     <h4 class="media-heading">Media heading</h4>  
  7.     ...  
  8.   </div>  
  9. </div>  

Now let’s modify the html according to our requirement of angular.

  1. <div *ngFor="let follower of followers" class="media">  
  2.   <a class="pull-left" href="#">  
  3.     <img class="media-object" src="{{ follower.avatar_url }}" alt="...">  
  4.   </a>  
  5.   <div class="media-body">  
  6.     <h4 class="media-heading">{{ follower.login }}</h4>  
  7.     <a href="follower.html_url">{{ follower.html_url }}</a>  
  8.   </div>  
  9. </div>  

Now finally we need to use our this new component on app.html. Come back again to our component and modify its selector.

  1. @Component({  
  2.   selector: 'github-followers',  
  3.   templateUrl: './github-followers.component.html',  
  4.   styleUrls: ['./github-followers.component.css']  
  5. })  

Now in app.component.html,

  1. <github-followers></github-followers>  

Now come into the browser,

Handling Errors In Angular 

Look it is working now. And here avatars are in full size. We want to make avatar small and round using css. Now open github-followers.component.css

  1. .avatar {  
  2.     width: 80px;  
  3.     height: 80px;  
  4.     border-radius: 100%;  
  5. }  

Now let’s go back to the template. So here is the image,

  1. <a class="pull-left" href="#">  
  2.   <img class="avatar media-object" src="{{ follower.avatar_url }}" alt="...">  
  3. </a>  

And this is what we get.

Handling Errors In Angular 


Similar Articles