Saturday 2 February 2019

rxjs operators in angular services

rxjs operators in angular services


Angular 6 uses rxjs 6. You can verify this by looking at package.json file in your Angular 6 project. In this topic we will discuss using rxjs 6 operators in Angular 6 services. 


In the project that we have been working with so far in this topic tutorial, we need to do some ground work before we we can create an angular service.  

Creating a fake online REST API 

First let's create a fake online REST API. For this we are going to use JSON-Server. We discussed what REST API is and using JSON-server in Part 63 of Angular CRUD tutorial

The following is the JSON Server Github page 
https://github.com/typicode/json-server 

Execute the following NPM command to install JSON server 

npm install -g json-server 

Create db.json file in the root project folder. Copy and paste the following JSON data in the file. 

{
    "employees": [
        {
            "id"1,
            "fullName""Mark",
            "contactPreference""email",
            "email""mark@email.com",
            "phone""5641238971",
            "skills": [
                {
                    "skillName""C#",
                    "experienceInYears"1,
                    "proficiency""beginner"
                },
                {
                    "skillName""Java",
                    "experienceInYears"2,
                    "proficiency""intermediate"
                }
            ]
        },
        {
            "id"2,
            "fullName""John",
            "contactPreference""phone",
            "email""john@email.com",
            "phone""3242138971",
            "skills": [
                {
                    "skillName""Angular",
                    "experienceInYears"2,
                    "proficiency""beginner"
                },
                {
                    "skillName""HTML",
                    "experienceInYears"2,
                    "proficiency""intermediate"
                },
                {
                    "skillName""LINQ",
                    "experienceInYears"3,
                    "proficiency""advanced"
                }
            ]
        }
    ]
}

Execute the following command to start the server 

json-server --watch db.json

At this point, fire up the browser and navigate to http://localhost:3000/employees/ to see the list of all employees along with their skills. You can test this REST API using a tool like fiddler. 

Creating the required interfaces to represent Employee and Skill types

Add a file in the employee folder with name ISkill.ts. Copy and paste the following code. 

export interface ISkill {
    skillName: string;
    experienceInYears: number;
    proficiency: string;
}

Add a file in the employee folder with name IEmployee.ts. Copy and paste the following code. 

import { ISkill } from './ISkill';

export interface IEmployee {
    id: number;
    fullName: string;
    email: string;
    phone?: number;
    contactPreference: string;
    skills: ISkill[];
}

Creating Angular Service 

Add a file in the employee folder with name employee.service.ts. Copy and paste the following code. 

import { Injectable } from '@angular/core';
import { IEmployee } from './IEmployee';
import { HttpClient, HttpErrorResponse, HttpHeaders } from '@angular/common/http';

import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';

@Injectable()
export class EmployeeService {
    baseUrl = 'http://localhost:3000/employees';
    constructor(private httpClient: HttpClient) {
    }

    getEmployees(): Observable<IEmployee[]> {
        return this.httpClient.get<IEmployee[]>(this.baseUrl)
            .pipe(catchError(this.handleError));
    }

    private handleError(errorResponse: HttpErrorResponse) {
        if (errorResponse.error instanceof ErrorEvent) {
            console.error('Client Side Error :', errorResponse.error.message);
        } else {
            console.error('Server Side Error :', errorResponse);
        }
        return throwError('There is a problem with the service. We are notified & working on it. Please try again later.');
    }

    getEmployee(id: number): Observable<IEmployee> {
        return this.httpClient.get<IEmployee>(`${this.baseUrl}/${id}`)
            .pipe(catchError(this.handleError));
    }

    addEmployee(employee: IEmployee): Observable<IEmployee> {
        return this.httpClient.post<IEmployee>(this.baseUrl, employee, {
            headers: new HttpHeaders({
                'Content-Type''application/json'
            })
        })
        .pipe(catchError(this.handleError));
    }

    updateEmployee(employee: IEmployee): Observable<void> {
        return this.httpClient.put<void>(`${this.baseUrl}/${employee.id}`, employee, {
            headers: new HttpHeaders({
                'Content-Type''application/json'
            })
        })
            .pipe(catchError(this.handleError));
    }

    deleteEmployee(id: number): Observable<void> {
        return this.httpClient.delete<void>(`${this.baseUrl}/${id}`)
            .pipe(catchError(this.handleError));
    }
}

RxJS 5 vs 6 

An Angular 6 project, by default uses RxJS version 6. RxJS 6 has some breaking changes compared to RxJS 5.5 and older versions. 

The way we import some of the classes like Observable and Subject has changed in RxJS 6. 

In RxJS 5, we import Observable and Subject classes as shown below. 

import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';

In RxJS 6, this has changed to 

import { Observable, Subject } from 'rxjs';

Similarly, the way we import operators also changed in RxJS 6. To import catchError operator in RxJS 5, we use the following 

import { catchError } from 'rxjs/operators/catchError';

In RxJS 6, it has changed to the following 

import { catchError } from 'rxjs/operators';

In RxJS 6, we import all the operators from 'rxjs/operators' 

import { map, delay, catchError } from 'rxjs/operators';

Many classes like ArrayObservableEmptyObservableErrorObservable etc are also removed from v6, in favour of existing or new operators that perform the same operations. 

For example, in v5 to create an ErrorObservable we might use one of the following 

new ErrorObservable('Your error message');

OR

ErrorObservable.create('Your error message');

In v6, we use throwError() function to achieve this.  

return throwError('Your error message');

How do I know, I have to use throwError() function instead of ErrorObservable class. Well, the following GitHub article contains all the differences between RxJS v5.x and v6. A quick search (CTRL + F) on the page for ErrorObservable shows, it has been removed in favour of throwError() function.
https://github.com/ReactiveX/rxjs/blob/master/docs_app/content/guide/v6/migration.md

in v6, import throwError function from rxjs. Since ErrorObservable class is replaced by throwError function, we import it the same way we import other classes like Observable and Subject from rxjs. 

import { Observable, throwError } from 'rxjs';

Implementing ListEmployeesComponent : While we are here, let's implement ListEmployeesComponent

We want this component to display all employee details as shown below. 

angular list component example 

Copy and paste the following HTML in list-employees.component.html 

<div class="table-responsive">
  <table class="table table-bordered" *ngIf="employees && employees.length">
    <thead>
      <tr class="bg-primary">
        <th>Name</th>
        <th>Email</th>
        <th>Phone</th>
        <th>Contact Preference</th>
        <th>Action</th>
      </tr>
    </thead>
    <tbody>
      <tr *ngFor="let employee of employees">
        <td>{{ employee.fullName }}</td>
        <td>{{ employee.email }}</td>
        <td>{{ employee.phone }}</td>
        <td>{{ employee.contactPreference }}</td>
        <td> <button class="btn btn-primary">Edit</button> </td>
      </tr>
    </tbody>
  </table>
</div>

Copy and paste the following code in list-employees.component.ts 

import { Component, OnInit } from '@angular/core';
import { EmployeeService } from './employee.service';
import { IEmployee } from './IEmployee';

@Component({
  selector: 'app-list-employees',
  templateUrl: './list-employees.component.html',
  styleUrls: ['./list-employees.component.css']
})
export class ListEmployeesComponent implements OnInit {
  employees: IEmployee[];

  constructor(private _employeeService: EmployeeService) { }

  ngOnInit() {
    this._employeeService.getEmployees().subscribe(
      (employeeList) => this.employees = employeeList,
      (err) => console.log(err)
    );
  }

}

Changes in app.module.ts file 

To be able to use EmployeeService in ListEmployeesComponent we have to register it. Since we want EmployeeService to be available across the entire application, Let's register it in the application root module AppModule.

Import EmployeeService and include it in the providers array of @NgModule decorator of AppModule. Employee service uses angular's HttpClient service. To be able to use this we have to import HttpClientModule in the AppModule and include it in the imports array. 

import { EmployeeService } from './employee/employee.service';
import { HttpClientModule } from '@angular/common/http';

@NgModule({
  declarations: [
    AppComponent,
    CreateEmployeeComponent,
    ListEmployeesComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    HttpClientModule,
    ReactiveFormsModule
  ],
  providers: [EmployeeService],
  bootstrap: [AppComponent]
})
export class AppModule { }

No comments:

Post a Comment