Mike Sav
Mike Sav

Reputation: 15321

Move http functionaity into its own Service with Angular2 and TypeScript

I'm currently trying to teach myself Angular2 and TypeScript after happily working with Angular 1.* for the last 4 years! Anyway, I have a top level component that creates a property that is derived from a type I have created in another class. With my component when ngOnInit() is called I make a http call to a phoney REST service I wrote as a back end. Now when writing apps using AngularJS I would put my $http tasks into a service and inject them into my controllers... I would like to do the same with my component. Here's my component without the service code activated... notice the comments

import {Component, OnInit} from 'angular2/core';
import {RouteConfig, ROUTER_DIRECTIVES} from 'angular2/router';
import {Home} from './components/home/home';
import {UserStatus} from './types/types.ts'; // this is why my UserStatus Type is kept
import {UserData} from './services/user-data/UserData.ts'; // here is where I wish to write my sevice to perform the http tasks...
import {Http, Headers} from 'angular2/http';

@Component({
    selector: 'app', // <app></app>
    providers: [...FORM_PROVIDERS],
    directives: [...ROUTER_DIRECTIVES],
    pipes: [],
    styles: [require('./app.scss')],
    template: require('./app.html')
})

export class App {

    userStatus: UserStatus;

    constructor(public http: Http) {
        this.userStatus = new UserStatus();
    }

    ngOnInit() {

        // I really want to put this code into a seperate class and provide it as a service...
        var headers = new Headers();
        headers.append('Content-Type', 'application/json');
        this.http.get('/restservice/userstatus', {headers: headers})
            .map((data: any) => data.json())
            .subscribe(
            (data: any) => {
                this.userStatus = data;
            },
            err => console.log(err), // error
            () => console.log('getUserStatus Complete') // complete

        );

        /* I'd like to factor the above into a service kept in a diffent file like so*/
        /*

         UserData.getUserStatus().then((resp) => {
            this.userStatus = resp;
         })
         */

    }
}

Now here is my type... for my userStatus property...

export class UserStatus {

    constructor (
        public firstName?: string,
        public fullPersId?: number,
        public goldUser?: boolean,
        public hasProfileImage?: boolean,
        public hideMoblieNavigationAndFooter?: boolean,
        public persId?: string,
        public profileName?: string,
        public profilePicture?: string,
        public showAds?: boolean,
        public siteId?:  number,
        public url?: string,
        public verified?: boolean,
        public appOS?: any,
        public formerName?: any
    ) {

        this.firstName = firstName || '';
        this.fullPersId = fullPersId || 0;
        this.goldUser = goldUser || false;
        this.hasProfileImage = hasProfileImage || false;
        this.hideMoblieNavigationAndFooter = hideMoblieNavigationAndFooter || false;
        this.persId = persId || '';
        this.profileName = profileName || '';
        this.profilePicture = profilePicture || '';
        this.showAds = showAds || false;
        this.siteId =  siteId || 0;
        this.url = url || '';
        this.verified = verified || false;
        this.appOS = appOS || null;
        this.formerName = formerName || null;

    }
}

Now I wish to put the http functionality of my Component into a serperate service... I started to write the following code (PLEASE DON'T LAUGH, I really am new to Angular2)

import {Injectable} from 'angular2/core';
import {Http, Headers} from 'angular2/http';
import {UserStatus} from '../../types/types.ts';

@Injectable()
export class UserData {

    constructor(public http:Http) {
    }

    getUserStatus(): any {
        var headers = new Headers();
        headers.append('Content-Type', 'application/json');
        this.http.get('/restservice/userstatus', {headers: headers})
            .map((data: any) => data.json())
            .subscribe(
            (data: any) => {
                return data;
            },
            err => console.log(err), // error
            () => console.log('getUserStatus Complete') // complete
        );
    }
}

Now I was hoping I could call the service getUserStatus() method to perform the HTTP data fetch and call it in my App Component something like (I am using AngularJS 1.x promise example, but I know I should really use observables... I just don't know how!)

ngOnInit() {

     UserData.getUserStatus().then((resp) => {
        this.userStatus = resp;
     });

    }

This is obviously rubbish and I don't know what I am doing (examples/tutorials for Angular2 don't seem to be that great or practical so far). Can someone please show my how to wire the service then call it correctly in my Component?

Upvotes: 3

Views: 433

Answers (2)

Pardeep Jain
Pardeep Jain

Reputation: 86800

Well this is one of the best topic among all in angular2 so i am here to answer this question ;)

so according to the question lets come to point

Http request with separate service file (code)

so upto my knowledge this is best practice to use separate file for the service(http request) to do so we normally do http request stuff in the service file and then subscribe to the response in the component file. we also use modal(typescript for type checking as you posted in the question). you have to change your code like this to do via separate service file as following:

userStatus.ts:

import {Component, View} from 'angular2/core';
import {Http, Response, RequestOptions, Headers, Request, RequestMethod} from 'angular2/http';

import {UserStatus} from './types/types.ts'; // better to write here for type checking etc.....

@Component({
    selector: 'app-service'
})
export class appService {
    headers: Headers;
    requestoptions: RequestOptions;
    res: Response;

    student_category_array: Array<UserStatus> = [];
    constructor(private http: Http) { }

    getUserStatus(url) {

        this.headers = new Headers();
        this.headers.append("Content-Type", 'application/json');
        this.headers.append("Authorization", 'id_token or some thing else....')

        this.requestoptions = new RequestOptions({
            method: RequestMethod.Get,
            url: url,
            headers: this.headers
        })

        return this.http.request(new Request(this.requestoptions(url)))
            .map(res => {
                // DO YOUR STUFF HERE whihc you want to return to component or
                return [{status: res.status, json: res.json()}]
            })
    }
}

and your component file should look like as following:

import {Component, OnInit} from 'angular2/core';
import {RouteConfig, ROUTER_DIRECTIVES} from 'angular2/router';
import {Home} from './components/home/home';
import {UserStatus} from './types/types.ts'; // this is why my UserStatus Type is kept
import {UserData} from './services/user-data/UserData.ts'; // here is where I wish to write my sevice to perform the http tasks...
import {Http, Headers} from 'angular2/http';

@Component({
    selector: 'app', // <app></app>
    providers: [...FORM_PROVIDERS,UserStatus], //better to import FORM_PROVIDERS and basic providers at the time of bootstrapin
    directives: [...ROUTER_DIRECTIVES],
    pipes: [],
    styles: [require('./app.scss')],
    template: require('./app.html')
})

export class App implements OnInit {
    constructor(private userStatus:UserStatus){

    }

    ngOnInit(){
        this.serviceCalled();
    }

    serviceCalled(){
        userData.getUserStatus(url or path to json....)
            .subscribe(res=>{
                //get your data and CODE HERE....
                console.log(res)
            })
    }
}

for reference purpose you can read out my these answer too for the Http request

https://stackoverflow.com/a/34823818/5043867

https://stackoverflow.com/a/34758630/5043867

hope it help you and clear some point related to your question !

Upvotes: 0

inoabrian
inoabrian

Reputation: 3800

I like this question because I went down the same road too and had to refactor my code. So best practice right now with RxJs observables is not to return the json in your .subscribe() method from the http request. Just map it and return it. What you are doing there is stripping it from all of the extra info it has.

You are supposed to return the observable which is the whole implementation of the get().

getUserStatus(): any {
        var headers = new Headers();
        headers.append('Content-Type', 'application/json');
        return this.http.get('/restservice/userstatus', {headers: headers})
            .map((data: any) => data.json())
            .catch(this.handleError)
        );
    }

private handleError(error: Response) {
    // in a real world app, we may send the server to some remote logging infrastructure
    // instead of just logging it to the console
    console.error(error);
    return Observable.throw(error.json().error || 'Server error');
  }

Then on your component side you can simply subscribe to the observable

export class App {
    public userStatus:any;
    // Have to inject the UserData Service it into our component here.
    constructor(public http: Http, private _userData: UserData ) {
    }

    ngOnInit() {
         this._userData.getUserStatus()
         .subscribe(
            (status) => {
              this.userStatus = status;
            },
            (err) => {
              console.log(err);
            },
            ()=>{console.log("User status complete")}
         );

        }
}

Upvotes: 4

Related Questions