3
\$\begingroup\$

I am beginning to develop a common data service that can be used by other services. Specifically, this service will be injected into a generic abstract application service. I am trying to figure out the best way to return the results from these services.

I chose to setup a common wrapper for the HTTP service to consume a custom IResult from my API application. It allows for system messages (error, warnings, successes), typical post/get commands, and so it can be used throughout the application with ease. I am using blockUI to show a loading indicator

The common data service:

@Injectable()
export class DataService {
    private reqData = {};
    private url: string;
    @BlockUI() blockUI: NgBlockUI;
    constructor(private http: Http) {

    }

    post(url: string, data: any, blockUi: boolean, blockUiMessage?: string, options?: RequestOptionsArgs): Observable<IResult> {
        // some logic

        return this.http.post(url, data, options)
            .map((response: Response) => {
                if (blockUi)
                    this.blockUI.stop();

                var result = <IResult>response.json();
                if(result.Message && result.Message.DisplayMessage)
                    this.showMessage(result.Message);
                return result;
            })
            .catch((error: Response) => {
                if (blockUi)
                    this.blockUI.stop();
                console.log(error);
               return Observable.throw('Server error');

            });

    }


    public get(url: string, blockUi: boolean, blockUiMessage?: string, options?: RequestOptionsArgs):
        Observable<IResult> {

        if (!blockUiMessage || blockUiMessage.length <= 0)
            blockUiMessage = 'Please Wait...';

        if (blockUi)
            this.blockUI.start(blockUiMessage);

        return this.http.get(url, options)
            .map((response: Response) => {
                if (blockUi)
                    this.blockUI.stop();
                var result = <IResult>response.json();
                if (result.Message && result.Message.DisplayMessage)
                    this.showMessage(result.Message);
                return result;


            })
            .catch((error: Response) => {
                if (blockUi)
                    this.blockUI.stop();
                console.log(error);
                var result = <IResult>error.json();
                this.showMessage(result.Message);
                return Observable.throw(error.json() || 'Server error');

            });
    }

    public showMessage(message: any) {

        ...code to display growl type messages
    }
}

My back-end API exposes commands for GetById<T>, GetAll<IEnumberable<T>>, etc. I then wanted to create an abstract; generic base service class that would be used by specific application types. It also requires the user be authenticated. I add an Authorization header for each request in the constructor to ensure this.

The BaseClientDto is a simple class to expose properties required by the BaseHttpAuthorizationService. This is best illustrated by the getById method.

@Injectable()
export abstract class BaseHttpAuthorizationService<T extends BaseClientDto> {

    public header: Headers;
    public model: BehaviorSubject<T>;
    public httpResult: any;
    public modelCollection: T[];

    constructor(public authService: AuthenticationService, public dataService: DataService) {

        this.model = BehaviorSubject.create();
        if (this.authService.isAuthenticated())
            this.header = new Headers({ 'Authorization': 'Bearer ' + this.authService.getUserFromLocalStorage().token });

    }


    public getDataService(): DataService {
        return this.dataService;
    }

    public getById(dto: T) {

        var url = this.buildUri(dto.constructor.name) + dto.Id;
        this.dataService.get(url, true, 'Loading...', new RequestOptions({ headers: this.header }))
            .subscribe(result => {
                console.log(result.Model);
                this.model.next(<T>result.Model);
            });

    }


    public getAll(dto: T) {

        var url = this.buildUri(dto.constructor.name);
        this.dataService.post(url, dto, true, 'Loading...', new RequestOptions({ headers: this.header }))
            .subscribe(result => {
                this.modelCollection = <T[]>result.model;
            });

    }



    public deleteHard(dto: T) {
        var url = this.buildUri(dto.constructor.name) + 'delete/hard/' + dto.Id;
        this.dataService.post(url, dto, true, 'Loading...', new RequestOptions({ headers: this.header }))
            .subscribe(result => {
                this.httpResult = result;
            });

    }

    public buildUri(typeName: string) {

        if (typeName.toLowerCase().indexOf("dto") !== -1)
            typeName = typeName.toLowerCase().replace('dto', '');
       return globalContansts.ApiUrl typeName + "/";
    }
}

I'd like my methods to return whatever T is, rather than a type of observable. s you can see in the generic base class, the model property is setup as a BehaviorSubject<T>. I did this so my implemented classes could subscribe to the property and get the value, but this seems clunky to me. I end up having to do something like this in the implemented class to get the result:

export class SomeObjectService extends BaseHttpAuthorizationService<SomeObject>{

someObject = SomeObject;

   constructor(){
        this.someObject = Observable.create((observer: any) => {
            this.implmentedService.model.asObservable()
                   .subscribe((result: SomeObject) => {
                       observer.next(result);

                   });
           });
   }
}

I am sure there is a better way to set this up than how I currently have it.

\$\endgroup\$
0

1 Answer 1

2
\$\begingroup\$

First, you could switch over to HttpClient (from '@angular/common/http') instead of Http in your DataService. Then, you could use (import 'rxjs/add/operator/toPromise';) return a Promise instead of an Observable:

return this.http.get(url, options)
        .toPromise()
        .then((response: Response) => {
            if (blockUi)
                this.blockUI.stop();
            var result = <IResult>response;
            if (result.Message && result.Message.DisplayMessage)
                this.showMessage(result.Message);
            return result;
        })
        .catch(this.handleError);

Now, in your implementation, you could do this:

    public getById(dto: T) {

    var url = this.buildUri(dto.constructor.name) + dto.Id;
    this.dataService.get(url, true, 'Loading...', new RequestOptions({ headers: this.header }))
        .then(result => {
            console.log(result);
            this.SomeObject = result; // need a deserializer / loader to not kill other methods of SomeObject if that class is more than a pure DTO
        });
    }

However, I'm having a similar problem. I'm losing the properties / methods of my returned Type because the object is assigned to the returned JSON string. I'm working on a de-serialization method to preserve the object.

\$\endgroup\$
1
  • \$\begingroup\$ My only concern is not having the ability to cancel the request or inspect requests for duplicates. I need to ask myself how often I really need to perform those options. \$\endgroup\$
    – DDiVita
    Aug 9, 2017 at 19:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.