Angular 2/5. Service with HttpClient makes mistake

2019-08-31 00:58发布

问题:

I'm trying to make service which takes (trying to take) XML-file from remote server. I haven't enough experience to find mistakes by myself. Relevant code of app.component.ts:

import { Component, OnInit, AfterViewInit, ElementRef, ViewChild, Renderer2 } from '@angular/core';
import { CanvasSettings } from './canvas-settings';
import { Rate } from './rate';
import { RateService } from './rates.service';
import { CBRRateService } from './cbrrates.service';
import { DatePoints, Year, Month, Day, Monthes, ofMonth } from './dates';
import { HttpClient, HttpHandler, HttpClientModule } from '@angular/common/http';

@Component({ // line 9, there is an error
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css'],
    providers: [ RateService, CBRRateService, HttpClient, HttpHandler, HttpClientModule ]
})

export class AppComponent implements OnInit, AfterViewInit {

    @ViewChild("helper") helper: ElementRef;

    //...
    rates: Rate[];
    cbrrates: any;
    //...

    constructor(private _rateService: RateService, private _rateCBRService: CBRRateService) {}

    ngOnInit() {

        //...

    }

    getCBRRates():void { // Uses http. Doesn't work.

        this._rateCBRService.getCBRRates().subscribe(cbrrates => {this.cbrrates = cbrrates});

    }
    getRates():void { // Doesn't use http, works fine

        this._rateService.getRates().subscribe(rates => this.rates = rates);

    }

    //...

}

And code listing of cbrrates.service.ts:

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { HttpClient, HttpHandler, HttpClientModule, HttpHeaders } from '@angular/common/http';
import { of } from 'rxjs/observable/of'; // It used in earlier version, now it useless

const httpOptions = {
    headers: new HttpHeaders({ 'Content-Type': 'plain/text' }) // I've found no way to get XML, so 'plain/text'. Everywhere JSON.
};

@Injectable()
export class CBRRateService {

    targetURL: string = "http://www.cbr.ru/scripts/XML_dynamic.asp?date_req1=01/01/2016&date_req2=02/02/2018&VAL_NM_RQ=R01235";

    constructor(private http: HttpClient){}

    getCBRRates(): Observable<any> {
        return (this.http.get(this.targetURL, {responseType: 'text'}));
    }

}

Error in bash console:

 ERROR in src/app/app.component.ts(9,12): error TS2345:
 Argument of type '{ selector: string; templateUrl: string;
 styleUrls: string[]; providers: (typeof HttpHandler | ty...' is not
 assignable to parameter of type 'Component'.

   Types of property 'providers' are incompatible.

     Type '(typeof HttpHandler | typeof HttpClient | typeof HttpClientModule
 | typeof CBRRateService)[]' is not assignable to type 'Provider[]'.

       Type 'typeof HttpHandler | typeof HttpClient | typeof HttpClientModule
 | typeof CBRRateService' is not assignable to type 'Provider'.

         Type 'typeof HttpHandler' is not assignable to type 'Provider'.

           Type 'typeof HttpHandler' is not assignable to type 'ClassProvider'.

             Property 'provide' is missing in type 'typeof HttpHandler'.

Error in browser console:

Failed to load resource: the server responded with a status of 404 (Not Found)

Total 2 questions:

  1. How to fix service CBRRateService and make it works?

  2. Does exist some way to get and parse XML-data via service which gets data from remote server?

回答1:

@MikhailFilchushkin, when you write this.http.get(this.targetURL, {responseType: 'text'}), you recibe in the response a "string". So, you must convert this string to an object. You can do something like

getCBRRates(): Observable<any> {
    return this.httpClient.get(this.targetURL, { responseType: 'text' })
      .map((response: string) => { //response is a string
        let result: any[] = [];
        let records = response.split('<Record'); //make a array of string
        let index = 0;
        records.forEach(r => {
          let date = r.split('Date=');
          let id = r.split('Id=');
          let nominal = r.split('<Nominal>');
          let value = r.split('<Value>');
          if (index > 0) { //the first is the header of the xml
            result.push({
              date: date[1].substr(1,10), //date[0] is the text before "Date"
              id: id[1].substr(1,6), //idem id
              nominal: nominal[1].substring(0,nominal[1].indexOf('<')),
              value: value[1].substring(0,value[1].indexOf('<')),
            })
          }
          index++;

        });
        return result;
      });
  }


回答2:

Cannot leave comment so I'm posting an Answer: import HttpModule into the app module and inject it in the constructor.

And beware of the fact that new HttpClientModule has been introduced in version 4.3 and is available in package @angular/common/http and a complete re-implementation of the former HttpModule. The new HttpClient service is included in HttpClientModule and can be used to initiate HTTP request and process responses within your application. You're probably using the wrong version!

UPDATE: Since you have angular modules in the providers array tour messing the dependency injection

providers: [
    CBRRateService, OtherService, // don't bring angular modules and classes here
]