CRUD Operation With Fake HTTP Service In Angular - Part Nine

We’ve already learned a lot of fundamental concepts in Angular. Here, we’ll see the concept of consuming HTTP Services. So, by the end of this article,

  • We’ll be able to connect our Angular application to our backend services or APIs.
  • And specifically, we’ll learn how to perform CRUD operations.
  • We’ll learn how to extract a reusable data service for working with various endpoints.
  • How to properly handle different kinds of errors.
  • How to build applications with proper separation of concerns.

If you’re a beginner and you want to start your journey with Angular, here is the roadmap for you.

JSONPlaceHolder

We already know that Angular is a front-end framework, we use it to build client applications. In most of the real-world applications, we need a bunch of HTTP Services on the server and we use these services to get and save data. Now, as an Angular or front-end developer, we don’t necessarily need to know how to build HTTP services unless we want to be a full stack developer. So here, we’ll learn about fake HTTP services which we use at the back-end for our application. 

Here is the link for fake online REST API for developers. If we scroll down the home screen, we’ll see various endpoints.

CRUD Operation with Fake Http Service In Angular

If we click on an anchor tag, we’ll navigate to the page where we’ll see the JSON object containing the data of posts or comments or albums or photos and so on.

CRUD Operation with Fake Http Service In Angular

When we hit this above image URL in our Angular app, we’ll get this huge amount of data. We can send various kinds of HTTP requests to create, update, and delete posts as well. But as I told you, this is a fake service, when we create a new post, the API responds as if the post was actually created on the server but if we refresh the page, we’ll not see the newly created post there; because there is no database behind this HTTP service. In this article, we’ll learn to use this service as the back-end of our application.
 
CRUD Operation with Fake Http Service In Angular

Getting Data

Now, we know what a JSONPlaceHolder is. Let us take a look at how to write code to get all these post objects from JSONPlaceHolder and display them on a page. So here, we’ve created a new component called Posts component.

PS C:\Users\Ami Jan\HelloWorld\MyFirstAngularProject> ng g c Posts

Now, to enable an HTTP Service in the module, we define it in app.module.ts

  1. imports: [  
  2.   BrowserModule,  
  3.   FormsModule,  
  4.   ReactiveFormsModule,  
  5.   HttpModule  
  6. ]  

And in the header, use this import statement for HttpModule.

  1. import { HttpModule } from "@angular/http";  

Now, let's come back to the component and here, we’ll define the constructor. And in this constructor, we’ll add the parameter HTTP of Http. Whenever we want to get the data or save the data, we use this Http class to send an HTTP request to the backend. Now, let’s send the Get request to the server, which returns an Observable of response. And we know that we use Promises and Observables for asynchronous and non-blocking operations. Here, the request goes over the network, calls the server, but the data is not available immediately. So, there is some delay. During this delay, we don’t want the main thread to be executed because this code would get blocked.

We’ve already discussed what Promise is. Now, let me tell you what an Observable is, with an example.

What is Observable?

Assume, you’re working with an application containing a source code in which a few lines are written for the server interaction (request, response). Now, when you run the application, your script executes line by line and when it comes to the server interaction of line of code, the execution is paused because now, the request is interacting with the server and waiting for the response. Until we get the response back, next lines of code will never execute. And the response might depend on the internet connection and many other factors. Let’s suppose the response comes back after 3 seconds. Only after these three seconds, the further code is executed. Well, this is an issue. We have to wait until the response comes back.

CRUD Operation with Fake Http Service In Angular
So in Angular, when we’re using HTTP request, the HTTP service sets the Observable object during the interaction of the request with the server. So, Observable is responsible for server interaction. It sends the request to the server and receives the response as well. In simple words, Observable observes the response on the server. So when we’re using Observable, during execution, we make Observable object responsible for handling the response and we subscribe that Observable object in some point in our code. In this way, our script remains in execution state and doesn’t wait for a response. And then, when the response comes back, the Observable object handles that response and implements that response in all the places where we subscribed it. So this is why we subscribe to the Observable.
CRUD Operation with Fake Http Service In Angular
When the HTTP Requests execute, they interact with the server. Angular sets the response in Observable and then if we want to use that response in our component, we need to subscribe it.

CRUD Operation with Fake Http Service In Angular

So now, let’s implement this Observable concept in our application. Let us go back to the Posts component.
  1. export class PostsComponent {  
  2.   constructor(http: Http) {  
  3.     http.get('https://jsonplaceholder.typicode.com/posts')  
  4.       .subscribe()  
  5.   }  
  6. }  

We’ve 2 overloaded functions of subscribe and we’ll implement the 2nd one.

CRUD Operation with Fake Http Service In Angular

So here, we can pass an arrow function.

  1. export class PostsComponent {  
  2.   constructor(http: Http) {  
  3.     http.get('https://jsonplaceholder.typicode.com/posts')  
  4.       .subscribe(response => {  
  5.         console.log(response);  
  6.       });  
  7.   }  
  8. }  

This is what we call a subscription function. And here, we can access the response. Now, use the Posts HTML Selector in app.component.html. Let’s run the application with ng serve and look into browser console.

CRUD Operation with Fake Http Service In Angular

Most of the times, we don’t need to use this response object in the raw form. We want to convert it into JSON object because we use JSON objects to display the data in our Views.
  1. constructor(http: Http) {  
  2.   http.get('https://jsonplaceholder.typicode.com/posts')  
  3.     .subscribe(response => {  
  4.       console.log(response.json());  
  5.     });  
  6. }  

Now, we’ll get a long array of posts.

CRUD Operation with Fake Http Service In Angular

Instead of showing the data on the console, let’s save the data to a container and use it in the template.
  1. export class PostsComponent {  
  2.   posts: any[];  
  3.   
  4.   constructor(http: Http) {  
  5.     http.get('https://jsonplaceholder.typicode.com/posts')  
  6.       .subscribe(response => {  
  7.         this.posts = response.json();  
  8.       });  
  9.   }  
  10. }  

Now, let’s go to posts.component.html

  1. <ul class="list-group">  
  2.   <li  
  3.     *ngFor="let post of posts"   
  4.     class="list-group-item">  
  5.     {{ post.title }}  
  6.   </li>  
  7. </ul>  

Here is the result in the browser.

CRUD Operation with Fake Http Service In Angular

So, this is how we consume the service in our application.

Creating Data

Now, let’s add the functionality to add a new post. We already know that we use HTTP Post request to create the new resource. Now, let’s make an input textbox for entering the text and when you hit Enter, it posts your service. So, come in the posts.component.html.

  1. <input   
  2.   (keyup.enter)="createPost()"  
  3.   type="text" class="form-control">  
  4.   
  5. <ul class="list-group">  
  6.   <li  
  7.     *ngFor="let post of posts"   
  8.     class="list-group-item">  
  9.     {{ post.title }}  
  10.   </li>  
  11. </ul>  

So here is the HTML. Now, we’re calling just the function here on keyup.enter. But, how do we get the text of the textbox to enter into the service? So, let’s add the template variable here.

  1. <input   
  2.   #title  
  3.   (keyup.enter)="createPost(title)"  
  4.   type="text" class="form-control">  

Now, come again into the component and define the function.

  1. createPost(title: HTMLInputElement) {      
  2. }  

As you can see, to avoid any confusion, we define the type annotation as HTMLInputElement instead of string or any object. And the title is really actually the reference of HTML element. Now here, we need to call the server once again. So, we’ll use an HTTP object again in this function.

But the issue is that the HTTP object is just available to constructor only. So we make this HTTP variable as the class property here.

  1. export class PostsComponent {  
  2.   posts: any[];  
  3.   
  4.   constructor(private http: Http) {  
  5.     http.get('https://jsonplaceholder.typicode.com/posts')  
  6.       .subscribe(response => {  
  7.         this.posts = response.json();  
  8.       });  
  9.   }  
  10.   
  11.   createPost(title: HTMLInputElement) {  
  12.   
  13.   }  
  14. }  

Let me tell you the purpose of requests with specific operations.

Http Request

  • GET - Get Data (Read)
  • POST - Create Data (Create)
  • PUT - Update Data (Update)
  • DELETE - Delete Data (Delete)

Here is also another Http Verb (Patch) -- we’ll look into it later on.

  1. export class PostsComponent {  
  2.   posts: any[];  
  3.   
  4.   // it is private because we don't want to access it outside of the class.   
  5.   // It is just for this class  
  6.   private url = 'https://jsonplaceholder.typicode.com/posts';  
  7.   
  8.   constructor(private http: Http) {  
  9.     http.get(this.url)  
  10.       .subscribe(response => {  
  11.         this.posts = response.json();  
  12.       });  
  13.   }  
  14.   
  15.   createPost(input: HTMLInputElement) {  
  16.   
  17.     this.http.post(this.url,);  
  18.   }  
  19. }  

In the post method, the 2nd argument is the body which we input into a JSON object. When calling the HTTP services for creating data, the body of the request should be a JSON object that’s the object we want to send to the server. So here, we need to create the post object containing title as the body of post request.

  1. createPost(input: HTMLInputElement) {  
  2.   let post = { title: input.value };  
  3.   this.http.post(this.url, );  
  4. }  

So we’ve set only 1 property here because this is a simple application. In most complex applications, we need to set multiple properties or multiple input fields. But for setting multiple input fields in the JSON object, we need to create the HTML form in the template to get the value from it. Now as the second argument of post method, we need to convert this post JSON object to a string. So we use JSON.stringify(). JSON is the native object in JavaScript.

  1. createPost(input: HTMLInputElement) {  
  2.   let post = { title: input.value };  
  3.   this.http.post(this.url, JSON.stringify(post));  
  4. }  

Now, this post method also has the returntype of Observable<Response>.

CRUD Operation with Fake Http Service In Angular

So all the methods in the HTTP class return Observable which means here, we need to subscribe to this Observable and we’ll get the response in the subscription function. And now, let’s print the response on the console.

  1. createPost(input: HTMLInputElement) {  
  2.   let post = { title: input.value };  
  3.   this.http.post(this.url, JSON.stringify(post))  
  4.     .subscribe(response => {  
  5.       console.log(response.json());  
  6.     });  
  7. }  

Let’s test this in the browser. Enter the name in the input field and hit Enter; check what you have in the console.

CRUD Operation with Fake Http Service In Angular

So this is the object which we get from the server in response. And this object has only 1 property called id of the new post. In the real world application, when we send HTTP post request to the server the server should respond with the complete representation of the newly created object. So as we're working with fake service. This is purely for demonstration purpose, not a real API.

Now back in the component, the id we get from the server adds into the post object. So,

  1. createPost(input: HTMLInputElement) {  
  2.   let post = { title: input.value };  
  3.   this.http.post(this.url, JSON.stringify(post))  
  4.     .subscribe(response => {  
  5.       post.id = response.json().id;  
  6.       console.log(response.json());  
  7.     });  
  8. }  

We’ve the compilation error on post.id because post object which we have created in the first line of the function doesn’t have id property. There are 2 workarounds here.

One way is,

  1. createPost(input: HTMLInputElement) {  
  2.   let post: any = { title: input.value };  
  3.   this.http.post(this.url, JSON.stringify(post))  
  4.     .subscribe(response => {  
  5.       post.id = response.json().id;  
  6.       console.log(response.json());  
  7.     });  
  8. }  

Use any type with post object.

The second way is,

  1. createPost(input: HTMLInputElement) {  
  2.   let post = { title: input.value };  
  3.   this.http.post(this.url, JSON.stringify(post))  
  4.     .subscribe(response => {  
  5.       post['id'] = response.json().id;  
  6.       console.log(response.json());  
  7.     });  
  8. }  

Use square bracket notation.

So, we can use any approach. So now, we have the post. Finally, we need to add it to our post array. So it can make it visible in the browser.

  1. createPost(input: HTMLInputElement) {  
  2.   let post = { title: input.value };  
  3.   
  4.   this.http.post(this.url, JSON.stringify(post))  
  5.     .subscribe(response => {  
  6.       post['id'] = response.json().id;  
  7.       this.posts.splice(0, 0, post);  
  8.       console.log(response.json());  
  9.     });  
  10. }  

Here, the splice method adds an item at the first place in the array. The first argument is the starting position number, the 2nd argument is the deleteCount to delete the elements from start and the 3rd argument is the object which we want to add into the array. And if we want to add this object into the end of the array, we’ll use push() method.

  1. this.posts.push(post);  

But here, we’ll use splice() method.

Now, let’s test the application in the browser.

CRUD Operation with Fake Http Service In Angular

Look it is adding our text into the array. But our textbox should be clear. So
  1. createPost(input: HTMLInputElement) {  
  2.   let post = { title: input.value };  
  3.   input.value = '';  
  4.   
  5.   this.http.post(this.url, JSON.stringify(post))  
  6.     .subscribe(response => {  
  7.       post['id'] = response.json().id;  
  8.       this.posts.splice(0, 0, post);  
  9.       console.log(response.json());  
  10.     });  
  11. }  

And now, it is working perfectly.

CRUD Operation with Fake Http Service In Angular

It takes time to add the things because we’re using a fake service on the back-end. So, don’t confuse.

Updating Data

Now, let’s see how to implement the ability to update data. So, back in our template, let’s add a button inside list li so that if someone presses this button, we’ll update this record.

  1. <ul class="list-group">  
  2.   <li  
  3.     *ngFor="let post of posts"   
  4.     class="list-group-item">  
  5.     <button   
  6.       (click)="updatePost(post)"  
  7.       class="btn btn-default btn-sm">Update</button>  
  8.     {{ post.title }}  
  9.   </li>  
  10. </ul>  

Now back in our component, let’s define the updatePost() method.

  1. updatePost(post) {  
  2.   this.http.patch()  
  3. }  

So here we’ve 2 options (PUT or PATCH). We use Patch method to update only a few properties in an object. So instead of sending a complete representation of that object to the server, we send only the properties that should be modified. So here we’re using a patch. We use PUT when we need to modify the complete object. It is easier to show you in code. So here let’s say each post has a property called isRead, when we click a post we wanna set that property to true to indicate that this post has been read.

  1. updatePost(post) {  
  2.   // We are sending only the properties that should be modified, instead of  
  3.   // sending complete object.  
  4.   this.http.patch(this.url, JSON.stringify({ isRead: true }));  
  5. }  

So this is how we use patch method. And if you want to use the put method.

  1. updatePost(post) {  
  2.   this.http.put(this.url, JSON.stringify(post));  
  3. }  

Now you might be thinking which approach should you use, and that is really dependent upon what you’re building. First of all, you need to make sure that is your API really supports both the PUT and PATCH. Most people build APIs that respond to PUT request, so PATCH is not widely supported. But if you have an API that supports both the Patch and Put method, then if you’re modifying few properties then it would be better to use Patch method because with the help of Patch we don’t need to send the entire object to the server. And it leads to a slight performance benefit.

And here we use the patch method and just like other HTTP methods, here we use subscribe() method as well.

  1. updatePost(post) {  
  2.   this.http.patch(this.url, JSON.stringify({ isRead: true }))  
  3.     .subscribe(response => {  
  4.       console.log(response.json());  
  5.     });  
  6. }  

Now, let’s see the results in the browser. Look when I click on the Update button, we got Patch 404 error.

CRUD Operation with Fake Http Service In Angular

Because when we use Patch or Put method, we need to reference it to a specific post. And if you see the URL here (https://jsonplaceholder.typicode.com/posts), there is not any specific record to update. We’re referencing the entire posts collection. But we need to reference a specific post. So,
  1. updatePost(post) {  
  2.   this.http.patch(this.url + '/' + post.id, JSON.stringify({ isRead: true }))  
  3.     .subscribe(response => {  
  4.       console.log(response.json());  
  5.     });  
  6. }  

And if we now press the Update button, now it is working fine without any error. Now as we’re using patch method, we’re just updating the part of the object not the complete object. So, here is our request header tab in Network.

CRUD Operation with Fake Http Service In Angular

Deleting Data

So we’ve implemented GET, POST, PUT/PATCH. Now let’s implement the delete functionality. So first of all add the delete button in a template.

  1. <ul class="list-group">  
  2.   <li  
  3.     *ngFor="let post of posts"   
  4.     class="list-group-item">  
  5.     <button   
  6.       (click)="updatePost(post)"  
  7.       class="btn btn-default btn-sm">Update</button>  
  8.     {{ post.title }}  
  9.     <button   
  10.       (click)="deletePost(post)"  
  11.       class="btn btn-danger btn-sm pull-right">Delete</button>  
  12.   </li>  
  13. </ul>  

Now let’s define the deletePost() method in the component.

  1. deletePost(post) {  
  2.   this.http.delete(this.url + '/' + post.id)  
  3.     .subscribe(response => {  
  4.       let index = this.posts.indexOf(post);  
  5.       this.posts.splice(index, 1);  
  6.     });  
  7. }  

Now let’s test the application in the browser. And yes it is working fine, records are deleting. Where we click the Delete button on any list item, it removes from the list.

So this is how we use fake http service to define the CRUD operations.

OnInit Interface

Now let’s take a look on the issue with this implementation. So here in the constructor of Posts component, we’re calling the get() method of an http request to get the posts from the server. As the best practice, the constructor should be very small and lightweight. So we should not perform the expensive operations like calling the server. Then you might ask, when and where can we get the posts from the server. Well, components in Angular have what we call lifecycle hooks. So there are special methods that we can add to our component and Angular will automatically call these components at a specific time during the lifecycle of the component.

For example,

  • We create the component
  • Render it in some template where we want
  • Create and render its children
  • Destroy a component.

These are examples of lifecycle events. At this point, Angular will call specific methods in our component if they are defined. One of this method is ngInit(). So when we create the component with Angular CLI all the components implements OnInit interface. Now we come to the angular website and search for OnInit interface.

CRUD Operation with Fake Http Service In Angular

So it is defined in @angular/core and here we can see the shape of OnInit interface. It has ngOnInit() method with void returntype. So OnInit is an interface that refer to lifecycle hook. There are multiple lifecycle hooks in Angular i.e.
  • OnInit
  • OnChanges
  • DoCheck
  • AfterContentInit and so on…

Each of these interfaces declare the same name method prefix with ng. So for OnInit we have ngOnInit. So let’s implement OnInit interface in the class.

  1. export class PostsComponent implements OnInit {  
  2. }  

Add the import statement for OnInit interface as well.

  1. import { Component, OnInit } from '@angular/core';  

It is not necessary to explicitly implement the interface OnInit on the class, because when we use ngOnInit() in the class it will automatically work. But for compile time checking, we explicitly mention implements OnInit with class.

  1. export class PostsComponent implements OnInit {  
  2.   posts: any[];  
  3.   private url = 'https://jsonplaceholder.typicode.com/posts';  
  4.   constructor(private http: Http) {  
  5.     http.get(this.url)  
  6.       .subscribe(response => {  
  7.         this.posts = response.json();  
  8.       });  
  9.   }  
  10.   
  11.   ngOnInit() {  
  12.       
  13.   }  
  14. }  

And now move all the constructor code in ngOnInit()

  1. export class PostsComponent implements OnInit {  
  2.   posts: any[];  
  3.   private url = 'https://jsonplaceholder.typicode.com/posts';  
  4.   constructor(private http: Http) {  
  5.   }  
  6.   
  7.   ngOnInit() {  
  8.     this.http.get(this.url)  
  9.       .subscribe(response => {  
  10.         this.posts = response.json();  
  11.       });  
  12.   }  
  13. }  

Now you might think, we are using this.http in ngOnInit() but it is not the class member here. So let me repeat once again, if we create the parameter of constructor with access modifier it means that it is the member of the class. So it is working inside ngOnInit().

So here is the lesson, don’t call http services in the constructor of your component. If you need initialization use ngOnInit()

Separation of Concern

So it is the complete implementation of our Posts component.

  1. export class PostsComponent implements OnInit {  
  2.   posts: any[];  
  3.   private url = 'https://jsonplaceholder.typicode.com/posts';  
  4.   constructor(private http: Http) {  
  5.   }  
  6.   
  7.   ngOnInit() {  
  8.     this.http.get(this.url)  
  9.       .subscribe(response => {  
  10.         this.posts = response.json();  
  11.       });  
  12.   }  
  13.   
  14.   createPost(input: HTMLInputElement) {  
  15.     let post = { title: input.value };  
  16.     input.value = '';  
  17.   
  18.     this.http.post(this.url, JSON.stringify(post))  
  19.       .subscribe(response => {  
  20.         post['id'] = response.json().id;  
  21.         this.posts.splice(0, 0, post);  
  22.         console.log(response.json());  
  23.       });  
  24.   }  
  25.   
  26.   updatePost(post) {  
  27.     this.http.patch(this.url + '/' + post.id, JSON.stringify({ isRead: true }))  
  28.       .subscribe(response => {  
  29.         console.log(response.json());  
  30.       });  
  31.   }  
  32.   
  33.   deletePost(post) {  
  34.     this.http.delete(this.url + '/' + post.id)  
  35.       .subscribe(response => {  
  36.         let index = this.posts.indexOf(post);  
  37.         this.posts.splice(index, 1);  
  38.       });  
  39.   }  
  40. }  

Now here is the little problem with this implementation. The problem is this implementation violates the principal of Separation of Concerns. So the issue is our classes should be responsible for only one thing. A class should have a single responsibility. A class that does too many things violates the principle of SoC. Such kind of classes are hard to maintain and hard to test.

Now in this implementation, our post's component is involved in too different concerns. One is the presentation logic behind this view i.e. what should happen when click the update or delete button.

CRUD Operation with Fake Http Service In Angular

And the other concern in this classes involve is getting the data. And here we need to know this endpoint.

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

We need to work with http class. In Update and Delete method, we need to construct the url. This is a different concern. In the larger and more complex applications, it is likely that we’re going to work with posts of different pages. So this is not the only page where we display the posts. So in those pages as well we need to know the url and the post endpoint. So if in the future, the url changes for any reason we have to update at multiple places in our code. And also from a testing point of view, this implementation is little bit harder to test. For example, we have to write the automated test for this method.

  1. updatePost(post) {  
  2.   this.http.patch(this.url + '/' + post.id, JSON.stringify({ isRead: true }))  
  3.     .subscribe(response => {  
  4.       console.log(response.json());  
  5.     });  
  6. }  

In our unit test, we don’t have the live server up and running. So we want to isolate this component and only focus on the logic implemented here. In other words, we don’t want to make http calls to the server because it has too many problems. The first problem is if the server is not up and running, our test will fail. And the second problem is, always http calls slow down the automated test. So we run hundreds and thousands of automated tests in just a few seconds. So we should not ever ever ever call the http requests in the automated test.

So what is the solution?

The solution is to make another class Service. And this class will purely be responsible for working with our backend. So if we want to get all the posts or create the posts, or update or delete the posts we’re going to delegate this responsibility to that class or service. And it will solve the 2 issues that we discussed earlier. First of all the details of working with this backend is encapsulated in 1 place and we can reuse this at multiple places. And also if in the future, this detail changes like url, we need to update it only 1 place in our code.

CRUD Operation with Fake Http Service In Angular
The second benefit is, when we want to unit test our component. We can create Fake implementation of that service that doesn’t make http calls to the server. And then we can easily run thousands of unit test in just one go.
CRUD Operation with Fake Http Service In Angular

Extracting the Service

Now we’ll see how to extract the service from the component. Now first of all create the new service using Angular CLI.

  • PS C:\Users\Ami Jan\HelloWorld\MyFirstAngularProject> ng g s post

And it will create the two files.

  • src/app/post.service.spec.ts (for unit testing)
  • src/app/post.service.ts (service file)

As you can see Angular CLI adds this service in app folder. In real world applications, we’ve tens and hundreds of services. We should avoid putting all the services in app folder. So create the folder ‘services’ and put all the services there.

  • PS C:\Users\Ami Jan\HelloWorld\MyFirstAngularProject> cd src/app
  • PS C:\Users\Ami Jan\HelloWorld\MyFirstAngularProject\src\app> mkdir services
  • PS C:\Users\Ami Jan\HelloWorld\MyFirstAngularProject\src\app> mv post.service.* services

This is how we make the services directory inside app folder and move all the post services to services folder.

Now register this service in providers (app.module.ts)

  1. providers: [  
  2.   PostService  
  3. ]  

And auto import automatically adds the reference of this PostService in app.module.ts.

Now let’s go to our posts component, the first thing I wanto to move to the service is url. This is the implementation details working with our backend

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

Now here in the constructor, we need to inject Http class. So,

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

Now let’s go back to post.component.ts and replace http parameter of constructor with our service. So in our component we should not inject http class, this is a different concern. Instead we should work with our services. So, let’s change this to our services.

  1. export class PostsComponent implements OnInit {  
  2.   posts: any[];  
  3.   constructor(private service:PostService) {  
  4.   }  
  5. }  

And remove the import statement of Http from post component. Now let’s modify ngOnInit() and move the http call to the service. So here is PostService

  1. export class PostService {  
  2.   private url = 'https://jsonplaceholder.typicode.com/posts';  
  3.   constructor(private http: Http) { }  
  4.   getPosts() {  
  5.     return this.http.get(this.url);  
  6.   }  
  7. }  

And here is the posts.component implementation

  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(response => {  
  10.         this.posts = response.json();  
  11.       });  
  12.   }  
  13. }  

Now look this statement,

this.service.getPosts()

means that we want the posts here. We call the function and now it is the responsibility of this service function to get the data for us. We don’t know how this function works and get the posts, we just want to get the posts. And it is totally up to the service to figure out where to get these posts. Today it is getting the posts from JsonPlaceHolder; tomorrow it might get the data from FireBase or it might get the data from the local storage. So all these are the responsibilities of the services we’ve created. So when we change how to get the posts, our component will not be affected. All these changes are encapsulated at one place inside the service. And if we’ve 10 other components in our application that are using the service, those components will not be affected by these changes. So this is why we have Separation of Concerns.

So here is our final component code.

  1. import { PostService } from './../services/post.service';  
  2. import { Component, OnInit } from '@angular/core';  
  3.   
  4. @Component({  
  5.   selector: 'app-posts',  
  6.   templateUrl: './posts.component.html',  
  7.   styleUrls: ['./posts.component.css']  
  8. })  
  9. export class PostsComponent implements OnInit {  
  10.   posts: any[];  
  11.   
  12.   constructor(private service:PostService) {  
  13.   }  
  14.   
  15.   ngOnInit() {  
  16.     this.service.getPosts()  
  17.       .subscribe(response => {  
  18.         this.posts = response.json();  
  19.       });  
  20.   }  
  21.   
  22.   createPost(input: HTMLInputElement) {  
  23.     let post = { title: input.value };  
  24.     input.value = '';  
  25.   
  26.     this.service.createPost(post)  
  27.       .subscribe(response => {  
  28.         post['id'] = response.json().id;  
  29.         this.posts.splice(0, 0, post);  
  30.         console.log(response.json());  
  31.       });  
  32.   }  
  33.   
  34.   updatePost(post) {  
  35.     this.service.updatePost(post)  
  36.       .subscribe(response => {  
  37.         console.log(response.json());  
  38.       });  
  39.   }  
  40.   
  41.   deletePost(post) {  
  42.     this.service.deletePost(post.id)  
  43.       .subscribe(response => {  
  44.         let index = this.posts.indexOf(post);  
  45.         this.posts.splice(index, 1);  
  46.       });  
  47.   }  
  48. }  

And here is our service part,

  1. import { Http } from '@angular/http';  
  2. import { Injectable } from '@angular/core';  
  3.   
  4. @Injectable({  
  5.   providedIn: 'root'  
  6. })  
  7. export class PostService {  
  8.   
  9.   private url = 'https://jsonplaceholder.typicode.com/posts';  
  10.     
  11.   constructor(private http: Http) { }  
  12.   
  13.   getPosts() {  
  14.     return this.http.get(this.url);  
  15.   }  
  16.   
  17.   createPost(post) {  
  18.     return this.http.post(this.url, JSON.stringify(post))  
  19.   }  
  20.   
  21.   updatePost(post){  
  22.     return this.http.patch(this.url + '/' + post.id, JSON.stringify({ isRead: true }))  
  23.   }  
  24.   
  25.   deletePost(id) {  
  26.     return this.http.delete(this.url + '/' + id);  
  27.   }  
  28. }  

Now let’s test the application in the browser. And we’ll see it is working beautifully.


Similar Articles