i have an Ionic application, this application contains set of pages and services, now since i'm working with Ionic & Angular, i decided to build reusable code for different projects.
for example, Http service: in this service i want to centralize the code which manage the communication with server side to achieve the reusable concept and the change will be in one place.
my Http.service code is:
export interface RequestOptions {
observable?: any;
url?:string;
method?: string;
successCallBack?: any;
notSuccessCallBack?: any;
useDefaultNotSuccessCallBack?: boolean;
errorCallBack?: any;
useDefaultErrorCallBack?: boolean;
completeCallBack? : any;
sendToken?: boolean;
data?: any;
refresher?:Refresher;
infinitScroller?: InfiniteScroll;
loader?: Loading;
}
export interface ForkOptions {
requests: RequestOptions[];
useDefaultNotSuccessCallBack?: boolean;
errorCallBack?: any;
useDefaultErrorCallBack?: boolean;
completeCallBack? : any;
refresher?:Refresher;
infinitScroller?: InfiniteScroll;
loader?: Loading;
}
import {Injectable} from "@angular/core";
import {AuthenticationService} from "./authentication.service";
import {Observable} from "rxjs/Observable";
import {HttpClient, HttpHeaders} from "@angular/common/http";
import {AlertController, InfiniteScroll, Loading, Refresher} from "ionic-angular";
import 'rxjs/add/observable/forkJoin';
@Injectable()
export class HttpConnectionService {
constructor(
private http: HttpClient,
private authService: AuthenticationService,
private alertCtrl: AlertController
) {}
/**
* @param {ForkOptions} options: an options which contains the list of requests to be forked together.
* this method will construct an array of observables and handle the response using @responseHandler and @errorHandler methods
* in this service
*/
httpRequests( options: ForkOptions) {
// build the array of observables
let observables = [
...options.requests.map((request) => request.observable)
];
// subscribe to these observables
Observable.forkJoin(observables).subscribe(
(results) => {
// handle the response for each of the requests
for(let i = 0; i < results.length; i++) {
this.responseHandler(results[i], options.requests[i]);
}
}, (error) => {
// handle the errors
this.errorHandler(error, options);
}, () => {
// execute the complete handler
this.completeHandler(options);
}
);
}
/**
* @param {RequestOptions} requestOptions: contains the options and attributes of the request to be constructed
* @returns {any} return a ready to subscribe observable
*/
createObservable(requestOptions: RequestOptions) {
// switch statement to handle the different types of methods.
switch(requestOptions.method) {
// if the case is post or delete method, they would have the same parameters.
case 'post' : case 'delete' :
return this.postDelete(requestOptions);
// if the case is get method, it will be constructed differently
case 'get':
return this.get(requestOptions);
}
}
/**
*
* @param {RequestOptions} requestOptions: the options and attribute of the request (post or delete)
* @returns {any}: return the request observable.
*/
private postDelete(requestOptions: RequestOptions) {
return this.http[requestOptions.method](requestOptions.url, requestOptions.data);
}
/**
*
* @param {RequestOptions} requestOptions
* @returns {Observable<Object>}
*/
private get(requestOptions: RequestOptions) {
return this.http.get(requestOptions.url);
}
/**
*
* @param {RequestOptions} requestOptions identify different attributes of the request.
*/
httpRequest(requestOptions: RequestOptions) {
// send the http request and use the method attribute
// if there is observable sent with request
let observable = requestOptions.observable;
if(observable == undefined){
// if there is no observable, create one
observable = this.createObservable(requestOptions);
}
observable.subscribe(
(response: any) => {
// call the response handler
this.responseHandler(response, requestOptions);
}, (error) => {
// call the error handler
this.errorHandler(error, requestOptions);
}, () => {
// call the complete handler
this.completeHandler(requestOptions);
}
);
}
private responseHandler(response, requestOptions: RequestOptions) {
// if the response success, execute the success call back
if(response.isSuccess) {
requestOptions.successCallBack(response.result);
// check if there is infinit scroller and the response is empty, then disable the infinit scroller
if(requestOptions.infinitScroller && response.result.length == 0) {
requestOptions.infinitScroller.enable(false);
}
}else {
// if the response is not success, check if the notSuccessCallBack is defined,
// if notSuccessCallBack is defined, execute it, other wise, execute the default notSuccess callBacl
if(requestOptions.notSuccessCallBack) {
// execute the provided not success callBack
requestOptions.notSuccessCallBack(response);
}else if(requestOptions.useDefaultNotSuccessCallBack){
// execute the default not success callBack
this.defaultNotSuccessResponse(response);
}
}
}
private errorHandler(error, requestOptions: RequestOptions | ForkOptions) {
// check for the provided error callBack.
if(requestOptions.errorCallBack) {
// execute the provided callBack
requestOptions.errorCallBack(error);
}else if(requestOptions.useDefaultErrorCallBack){
// if u can default error handler
this.defaultErrorHandler(error, requestOptions);
}
}
/**
*
* @param {RequestOptions | ForkOptions} requestOptions: the requests options which contain completeCallBack.
*/
private completeHandler(requestOptions: RequestOptions | ForkOptions) {
// if there is complete callBack, execute it.
if(requestOptions.completeCallBack) {
requestOptions.completeCallBack();
}
// turn off the external components after the response is arrived
// for example: loader, infinit scroller, refreshing
this.turnOffExternalComponents(requestOptions);
}
/**
* contains the default behavioral for the not success response,
* it can be terminated if @useNotSuccessResponse = false in the request options.
* @param response: the response from the server side which contains the error message.
*/
private defaultNotSuccessResponse(response) {
// the default behavioral is to display an error message.
this.alertCtrl.create({
title: "Error!",
subTitle: response.message
}).present();
}
/**
*
* @param error: the error object
* @param {RequestOptions} requestOptions: contains attributes about the request
* used as params to access the external components when turning them off.
*/
private defaultErrorHandler(error, requestOptions: RequestOptions | RequestOptions | ForkOptions) {
// turn off the active components.
this.turnOffExternalComponents(requestOptions);
// create alert for the client.
this.alertCtrl.create({
title: "Error!",
subTitle: error.message
}).present();
}
/**
* terminates the view components which are related to the request,
* @param {RequestOptions | ForkOptions} requestOptions
*/
private turnOffExternalComponents(requestOptions: RequestOptions | ForkOptions) {
// set the refresher to complete
if(requestOptions.refresher) {
requestOptions.refresher.complete();
// after refreshing, enable the infinit scroller.
if (requestOptions.infinitScroller) {
requestOptions.infinitScroller.enable(true);
}
}
// set the infinit Scroller to complete.
// and turn on the infinit scroller.
if(requestOptions.infinitScroller) {
requestOptions.infinitScroller.complete();
}
// check if there is loader, and turn it off.
if(requestOptions.loader) {
requestOptions.loader.dismissAll();
}
}
}
this service will be used by other services in the application to provide http communication with server side.
for now, i don't now if this is a good practice or not.
any guidance or help will be appreciated :)
thanks.