简体   繁体   中英

Angular 2 - http.get never being call

Im learning Angular 4 and have run into a problem that I cannot seem to find a solution to. Here is the context: I have a simple app that displays info about US Presidents. The backend is a rest API provided by webapi...this works fine. The front end is an Angular app.

Ive distilled the problem down to 3 components, 1 data service and 1 model. Here is the model:

export class President {
  constructor(
    public id: number,
    public presidentName: string,
    public presidentNumber: number,
    public yearsServed: string,
    public partyAffiliation: string,
    public spouse: string) {}
}

The 3 components are 1. SearchComponent 2. HomeComponent 3. PresidentComponent

When the app bootstraps, it loads the ApplicationComponent - it is the root component:

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

@Component({
    selector: 'my-app',
    template: `
        <search-component></search-component>
        <home-component></home-component>
    `
})
export class ApplicationComponent  {}

PresidentComponent is a child component of HomeComponent. When home component loads, it makes an http call to the api to get a list of presidents and renders 1 presidentComponent for each row returned. This works fine.

What Im trying to do is implement a search feature where the dataService exposes an EventEmitter and provides the search method as shown here:

import { Injectable, EventEmitter, Output } from '@angular/core'
import { President } from '../models/President'

import { Http } from '@angular/http';
import 'rxjs/add/operator/toPromise';
import 'rxjs/add/operator/map';
import { Observable } from 'rxjs/Observable';

@Injectable()
export class DataService {

    constructor(private http: Http) {
    }

    searchEvent: EventEmitter<any> = new EventEmitter();

    // simple property for the url to the api
    get presidentUrl {
        return "http://localhost:51330/api/presidents";
    }

    search(params: any): Observable<President[]> {
        let encParams = encodeParams(params);
        console.log(encParams);
        return this.http
          .get(this.presidentUrl, {search: encParams})
          .map(response => response.json());
    }


    getParties(): String[] {
        return ['Republican', 'Democrat', 'Federalist', 'Whig', 'Democratic-Republican', 'None'];
    }

    getPresidents(): Observable<President[]> {
        return this.http.get(this.presidentUrl)
                        .map(response => response.json());
    }

}

/**
 * Encodes the object into a valid query string.
 * this function is from the book Angular2 Development with TypeScript
 */
function encodeParams(params: any): URLSearchParams {
    return Object.keys(params)
      .filter(key => params[key])
      .reduce((accum: URLSearchParams, key: string) => {
        accum.append(key, params[key]);
        return accum;
      }, new URLSearchParams());
  }

The Search Component houses the search form and when the search button is clicked, it executes the onSearch() function and calls emit on the data service:

onSearch(){
            if(this.formModel.valid){
                console.log('emitting event from search.ts');
                this.dataService.searchEvent.emit(this.formModel.value);
            }
        }

Then, in the HomeComponent, I want to subscribe to this event and execute a search via the dataservice when it fires:

 ngOnInit(): void {
        //when component loads, get list of presidents
        this.dataService.getPresidents()
        .subscribe(
            presidents => {
                console.log('sub');
                this.presidents = presidents;

            },
            error => console.error(error)
        )

    //when search event is fired, do a search
    this.dataService.searchEvent
        .subscribe(
            params => {
                console.log('in home.ts subscribe ' + JSON.stringify(params));
                this.result = this.dataService.search(params);
            },
            err => console.log("cant get presidents. error code: %s, URL: %s"),
            () => console.log('done')
        );
    }

When I run this in the browser, everything works except the http call is never executed. If I subscribe() to the http.get call in the dataservice itself, it executes but why should I have to do that when I have a subscription being setup on the HomeComponent? I want to handle the Observable in the HomeComponent and update the list of presidents that is being displayed in the UI based on the search result. Any advice is greatly appreciated.

The entire idea of using EventEmitter in the service is not right. The EventEmitter should be used with @Output properties to send data from the child component to its parent.

Even though the EventEmitter is a subclass of the Subject, you shouldn't be using it in services. So inject the service into your component, subscribe to its observable in the component, and emit an event using EventEmitter to the parent component if need be.

In the code this.result = this.dataService.search(params); , result is an observable. You have not made a subscription.

In that case you should have used the async pipe to display the data.

Why not use Subject from rxjs . Here is what i am proposing:

DataService:

import { Observable, Subject } from "rxjs";
import 'rxjs/add/operator/catch';

@Injectable()
export class DataService {
  private _dataSubject = new Subject();

  constructor(private http: Http) {
      this.http.get(this.presidentUrl)
         .map(response => this._dataSubject.next(response.json()))
         .catch(err => this._dataSubject.error(err));        
       );
     }

// simple property for the url to the api
get presidentUrl {
    return "http://localhost:51330/api/presidents";
}

search(params: any){
    let encParams = encodeParams(params);
    console.log(encParams);
    this.http
      .get(this.presidentUrl, {search: encParams})
      .map(response => this._dataSubject.next(response.json()))
      .catch(err => this._dataSubject.error(err));
}


getParties(): String[] {
    return ['Republican', 'Democrat', 'Federalist', 'Whig', 'Democratic-Republican', 'None'];
}

getPresidents(): Observable<President[]> {
    return this._dataSubject;
}

SearchComponent:

 onSearch(){
         if(this.formModel.valid){
            console.log('emitting event from search.ts');
            this.dataService.search(this.formModel.value);
        }
    }

With these modifications you should be able to have only 1 subscriber in homeCompoent and then get new data emitted every time onSearch() is called.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM