I\'m upgrading to Angular to version 5, I was using @angular/http
before and now I need to update to @angular/common/http
and use HttpClient
I a
It all depends on the version of RxJs. Angular 6 shipped with RxJs 6 - which means that the map()/catch() approach is no longer valid.
Instead, you have to use pipe + map()/catchError() as shown below:
Before Angular 6 / RxJs 6 - classic Http use:
return this.http.get(url, {headers: this.headers}).map(
(response: Response) => {
const data : SomeType = response.json() as SomeType;
// Does something on data.data
// return the modified data:
return data.data; // assuming SomeType has a data properties. Following OP post
}
).catch(
(error: Response) => {
throwError(error); // From 'rxjs'
}
);
Should be changed to this:
After Angular 6 / RxJs 6 - HttpClient migration:
return this.http.get<SomeType>(url, {headers: this.headers})
.pipe(
map( response => { // NOTE: response is of type SomeType
// Does something on response.data
// modify the response.data as you see fit.
// return the modified data:
return response; // kind of useless
}),
catchError( error => {
return throwError(error); // From 'rxjs'
})
); // end of pipe
In the pipe, map() will pick up the response object (already parsed from JSON) and catchError() will pick up the first error if the HTTP fails.
Also, note that your Headers need to be HttpHeaders object too.
Read on pipe, map and catchError in RxJs 6
As per my consideration you can call function of that service in your success responce and process your data in function and return it to back it.
return this.http.get(url, {headers: this.headers}).map(
(response: Response) => {
const data = response.json();
return this.processData(data);
}
).catch(
(error: Response) => {
return Observable.throw(error);
}
);
public processData(data : any){
//process your data
return data;//this is processed data
}
In the "new" Httpclient, get return yet a json response by default. Then you only write
import {HttpClient} from '@angular/common/http'; //<--HttpClient
import 'rxjs/add/operator/map'; //import the operator "map"
import 'rxjs/add/operator/catch'; //and the operator "catch"
....
constructor(private http: HttpClient) {} //<--sure HttpClient
...
return this.http.get(url).map( //yet get "data"
(data:any) => {
return this.processData(data);
}
).catch(
(error: Response) => {
return Observable.throw(error);
}
);
public processData(data : any){
//process your data
return data;//this is processed data
}
My service
import {HttpClient} from '@angular/common/http';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
@Injectable()
export class DataService {
constructor(private http: HttpClient) {}
getData()
{
return this.http.get('../assets/data.json').map(data=>{
return this.process(data);
}).catch(
(error: Response) => {
return Observable.throw(error);
});
}
process(data:any)
{
let dataTransform:any[]=[];
let i:number=0;
for (let item of data)
{
dataTransform.push({"id":i,"key":item.key});
i++;
}
return dataTransform;
}
}
//My component
export class AppComponent implements OnInit {
constructor(private dataService:DataService){}
ngOnInit(){
this.dataService.getData().subscribe((data:any)=> {
console.log(data);
});
}
}
//the asset/data.json
[
{"key":"uno"},
{"key":"dos"},
{"key":"tres"},
{"key":"cuatro"},
{"key":"cinco"}
]
You can create your own Observable that wraps the http.get and return your manipulated response, in this example it is the manipulatedAccountsResponse object:
getAll(): Observable<AccountsResponse> {
return Observable.create(observer => {
this.http.get<AccountsResponse>('/accounts')
.subscribe((result) => {
const manipulatedAccountsResponse = result;
// do something with result.
manipulatedAccountsResponse.setTotal(100);
observer.next(manipulatedAccountsResponse);
// call complete if you want to close this stream (like a promise)
observer.complete();
});
});
}
If you want to handling errors more efficiently, i writting below codes and error classes, please notice to the each part:
Make a class app-error.ts like below codes:
export class AppError {
constructor(public originalError?: any) { }
}
Another error classes extends from app-error.ts class:
// not-found-error.ts class
import {AppError} from './app-error';
export class NotFoundError extends AppError { }
// conflict-error.ts class
import {AppError} from './app-error';
export class ConflictError extends AppError { }
// internal-server-error.ts class
import {AppError} from './app-error';
export class InternalServerError extends AppError { }
// bad-request-error.ts class
import {AppError} from './app-error';
export class BadRequestError extends AppError {
constructor(public originalError?: any) {
super(originalError);
}
get errors(): string[] {
if (this.originalError)
return this.originalError;
return null;
}
}
If you want to access to base error or you can modify error, i do it inside latest class bad-request-error.ts
Then you can use these classes inisde service:
import {Injectable} from '@angular/core';
import {HttpClient} from '@angular/common/http';
import {HttpHeaders} from '@angular/common/http';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
import {AppError} from '../errors/app-error';
import {BadRequestError} from '../errors/bad-request-error';
import {NotFoundError} from '../errors/not-found-error';
import {InternalServerError} from '../errors/internal-server-error';
import {ConflictError} from '../errors/conflict-error';
@Injectable()
export class DataService {
public headers = new HttpHeaders().set('Content-Type', 'application/json');
constructor(public http: HttpClient, public url: string) { }
get(id: number) {
return this.http.get(`${this.url}/${id}`, {headers: this.headers})
.map((response) => response.json())
.catch(DataService.handleError);
}
create(resource) {
return this.http.post(this.url, JSON.stringify(resource), {headers: this.headers})
.map((response) => response.json())
.catch(DataService.handleError);
}
update(id: number, resource) {
return this.http.put(`${this.url}/${id}`, JSON.stringify(resource), {headers: this.headers})
.map((response) => response.json())
.catch(DataService.handleError);
}
remove(id: number) {
return this.http.delete(`${this.url}/${id}`, {headers: this.headers})
.map((response) => response.json())
.catch(DataService.handleError);
}
public static handleError(error: Response) {
switch (error.status) {
case 400:
return Observable.throw(new BadRequestError(error));
case 404:
return Observable.throw(new NotFoundError());
case 409:
return Observable.throw(new ConflictError());
case 500:
return Observable.throw(new InternalServerError());
default:
return Observable.throw(new AppError(error));
}
}
}
Above code is the best code for error handling and using map operator for manipulating response in success manner.
And the latest pace is using service inside component like below code:
import {Component} from '@angular/core';
import {OnInit} from '@angular/core';
import {HttpParams} from '@angular/common/http';
import {DataService} from '../../services/data.service';
import {AppError} from '../errors/app-error';
import {BadRequestError} from '../errors/bad-request-error';
import {NotFoundError} from '../errors/not-found-error';
import {InternalServerError} from '../errors/internal-server-error';
import {ConflictError} from '../errors/conflict-error';
@Component({
selector: 'app-data',
templateUrl: './data.component.html',
styleUrls: ['./data.component.css']
})
export class DataComponent implements OnInit {
constructor(private dataService: DataService) {
}
ngOnInit() {
this.dataService.get(123).subscribe(
(response: DataModel) => {
// ...
},
(error: AppError) => {
if (error instanceof NotFoundError) {
// ...
} else if (error instanceof BadRequestError) {
// ...
} else if (error instanceof ConflictError) {
// ...
} else {
// ...
}
}
);
}
}