Angular2: HTTP PATCH with complex object Using Observable in Angular 2 Application

Standard

The Angular Http client communicates with the server using a familiar HTTP request/response protocol. The Http client is one of a family of services in the Angular HTTP library. When importing from the @angular/http module, SystemJS knows how to load services from the Angular HTTP library because the systemjs.config.js file maps to that module name. The HttpModule is necessary for making HTTP calls.

Observable

Think of an Observable as a stream of events published by some source. To listen for events in this stream, subscribe to the Observable. These subscriptions specify the actions to take when the web request produces a success event or a fail event (with the error in the payload).

  • The observable’s map callback moves to the success parameter and its catch callback to the fail parameter in this pattern.
  • The errorHandler forwards an error message as a failed promise instead of a failed observable.

Observable vs Promises

The less obvious but critical difference is that these two methods return very different results.

  • The promise-based then returns another promise. You can keep chaining more then and catch calls, getting a new promise each time.
  • The subscribe method returns a Subscription. A Subscription is not another Observable. It’s the end of the line for observables. You can’t call map on it or call subscribe again. The Subscription object has a different purpose, signified by its primary method, unsubscribe.

RxJS library

  • RxJS (“Reactive Extensions”) is a 3rd party library, endorsed by Angular, that implements the asynchronous observable pattern.
  • RxJS npm package loaded via system.js because observables are used widely in Angular applications.
  • The app needs it when working with the HTTP client. Additionally, you must take a critical extra step to make RxJS observables usable.
  • The RxJS library is large. Size matters when building a production application and deploying it to mobile devices. You should include only necessary features.
  • Accordingly, Angular exposes a stripped down version of Observable in the rxjs/Observable module that lacks most of the operators such as the map method.
  • You could add every RxJS operator with a single import statement. While that is the easiest thing to do, you’d pay a penalty in extended launch time and application size because the full library is so big.
  • Since this app only uses a few operators, it’s better to import each Observable operator and static class method, one-by-one, for a custom Observable implementation tuned precisely to the app’s requirements. Put the import statements in one app/rxjs-operators.ts file.

The Http.patch method takes an object that implements RequestOptionsArgs as a second parameter.

The search field of that object can be used to set a string or a URLSearchParams object.

HTTP PATCH using Observable with body object as parameter

See extended DEMO on Github

import { Injectable } from '@angular/core';
import { Http, Response, Headers, RequestOptions, URLSearchParams } 
from '@angular/http';
import { Observable } from 'rxjs/Observable';

// Observable class extensions
import 'rxjs/add/observable/of';
import 'rxjs/add/observable/throw';

// Observable operators
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/distinctUntilChanged';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/filter';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/switchMap';

@Injectable()
export class WebApiObservableService {
    headers: Headers;
    options: RequestOptions;

    constructor(private http: Http) {
        this.headers = new Headers({ 'Content-Type': 'application/json', 
                                     'Accept': 'q=0.8;application/json;q=0.9' });
        this.options = new RequestOptions({ headers: this.headers });
    }

    patchService(url: string, param: any): Observable<any> {
    let body = JSON.stringify(param);
    return this.http
        .patch(url, body, this.options)
        .map(this.extractData)
        .catch(this.handleError);
    }     

    private extractData(res: Response) {
        let body = res.json();
        return body || {};
    }

    private handleError(error: any) {
        let errMsg = (error.message) ? error.message :
            error.status ? `${error.status} - ${error.statusText}` : 'Server error';
        console.error(errMsg);
        return Observable.throw(errMsg);
    }
}

Usage Code:

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

import { SearchMovieModel } from './search-movie.model';
import { WebApiObservableService } from './web-api-observable.service';

@Component({
    selector: 'search-movie-list',
    templateUrl: '../../Scripts/app/search-list.component.html'
})

export class SearchMovieListComponent implements OnInit {
    searchMovieModel: SearchMovieModel;
    
    constructor(
        private movieObservableService: WebApiObservableService) {
       
        this.searchMovieModel = {id: '12' , name: 'abc'};
    }

    ngOnInit() {
        this.movieObservableService
            .patchService('api/Movie/TestPost', this.searchMovieModel)
            .subscribe(
                result => console.log(result),
                error => this.errorMessage = <any>error
        );  
    }
}

Leave a comment