I'm trying to make http call and if there will be any error do my things.
Here is my code:
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/throw';
// Operators
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/distinctUntilChanged';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/switchMap';
import 'rxjs/add/operator/toPromise';
#Injectable()
export class HttpCallService {
constructor(private http: Http) { }
getHeroes(): Observable<Hero[]> {
console.log('entered');
return this.http.get('Url')
.map(this.extractData)
.catch(this.handleError);
}
private extractData(res: Response) {
console.log('extract entered');
let body = res.json();
return body.data || {};
}
private handleError(error: Response | any) {
console.log('error entered');
let errMsg: string;
if (error instanceof Response) {
const body = error.json() || '';
const err = body.error || JSON.stringify(body);
errMsg = `${error.status} - ${error.statusText || ''} ${err}`;
} else {
errMsg = error.message ? error.message : error.toString();
}
console.error(errMsg);
return Observable.throw(errMsg);
}
}
export class Hero {
constructor(
public id: Date,
public author: string,
public text: string
) { }
}
When I call it it logs in console only entered which is in getHeroes. I know there will be error I haven't valid url but why it doesn't go to catch?
You need to invoke the subscribe() method to make actual HTTP call. Your method getHeroes() just declares that it'll return the Observable when someone will subscribe to it. That's why you only see the log from getHeroes() - extractData() and handleErrors() are not even invoked.
You need to do getHeroes().subscribe() somewhere in your code.
Related
I am facing issue when i am using http interceptors(to intercept the http request and response) and behvior subject ( for communication between 2 services based on the interceptors) . I have a scenario where i need to monitor all the http calls going on in the application and make a specific http post call only when there are no other http calls are going in the application .
I have interceptor service where i am intercepting all the http request and responses and when no call is happening ,counter variable is 0 then , using a behavior subject ok$ ,and subscribing it in the other Service 2 and from there making a specific http post call . This subscribing is not happening second time , when the value of ok$ is changed.
Interceptor service :
import { Injectable } from '#angular/core';
import { HttpResponse, HttpEvent, HttpClient, HttpHeaders, HttpInterceptor, HttpHandler, HttpRequest, HttpErrorResponse } from '#angular/common/http';
import { BehaviorSubject } from 'rxjs';
import 'rxjs/add/operator/do';
#Injectable()
export class InterceptorService {
counter: number = 0;
public ok$: BehaviorSubject<any>;
constructor() {
this.ok$ = new BehaviorSubject(false);
}
checkCounter() {
if (this.counter === 0) {
setTimeout(() => {
this.checkCounterFinally();
}, 1000);
}
}
checkCounterFinally() {
if (this.counter === 0) {
this.ok$.next(true);
}
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (this.counter === -1)
this.counter = 1;
else
this.counter++;
return next.handle(req).do(
(event: HttpEvent<any>) => {
if (event instanceof HttpResponse) {
console.log(event);
this.counter--;
this.checkCounter();
}
},
(error: any) => {
(event instanceof HttpErrorResponse)
{
if (error instanceof HttpErrorResponse) {
if (error.status != 200) {
console.log(error);
}
}
}
}
);
}
}
Service 2: Making Rest call :
import { Injectable } from '#angular/core';
import { LogService } from '../common/log.service';
import { HttpClient, HttpHeaders } from '#angular/common/http';
import { Observable, BehaviorSubject } from 'rxjs';
import { InterceptorService } from './interceptor.service';
#Injectable()
export class WorkerService {
data: string;
storage = this.LogService.storage;
RestUrl = // some url
constructor(private service1: LogService, private httpClient: HttpClient,
private interceptor: InterceptorService) {
this.service1.logData.subscribe((val) => {
this.storage.setItem("key", "value");
});
this.interceptor.ok$.subscribe((value) => {
if (value === true) {
this.getDataFromLocalStorage();
}
});
}
getDataFromLocalStorage(): void {
//getting data from the local storage and making rest call to server
}
pushDatatoServer(data: string) {
this.httpClient.post(this.RestUrl, this.data, this.httpHeaderObjRequestParam )
.subscribe((response) => {
// do something
}
}
}
I have created an api using ASP .net WebApi to get a list of companies and get a single company. Service call GetCompanies works fine gets the data and prints the list. But, issues is with GetCompany service, it gets the company when I print it in log, but it does not get in the Company object. What am I doing wrong in the Angular Component and or Service. Any help is appreciated.
Here is the output of the application. GetCompanies lists all the companies, but GetCompany prints as [object Object]. . here is the output
Here is the screen shot of data coming from APIs.
This is companies.component.ts
import { Component, OnInit } from '#angular/core';
import { CompaniesService } from './companies.service';
import { Company } from './company.model';
#Component({
selector: 'app-companies',
template: `
<p>
company name = {{company}}
</p>
<ul>
<li *ngFor = "let c of companies"> {{c.Name}} - {{c.CompanyID}} </li>
</ul>
`
})
export class CompaniesComponent implements OnInit {
text: string;
errorMessage: string;
public company: Company;
public companies: Company[];
constructor(private cService: CompaniesService) { }
ngOnInit() {
this.getCompanies();
this.getCompany(5);
console.log(this.company);
}
getCompanies() {
return this.cService.getCompanies()
.subscribe(companies => this.companies = companies,
error => this.errorMessage =<any>error);
}
getCompany(id: number) {
return this.cService.getCompany(id)
.subscribe(company => this.company = company,
error => this.errorMessage =<any>error);
}
}
This is companies.service.ts
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import { Headers, RequestOptions } from '#angular/http';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';
import { Company } from './company.model';
#Injectable()
export class CompaniesService {
constructor(private http: Http) {
}
getCompany(id: number): Observable<Company> {
return this.http.get(`api/getcompany/?id=${id}`)
.map ((res:Response) => res.json() )
.catch(this.handleError) ;
}
getCompanies(): Observable<Company[]> {
return this.http.get('api/getcompanies')
.map ((res:Response) => res.json() )
.catch(this.handleError) ;
}
private extractData(res: Response) {
let body = res.json();
return body.data || [];
}
private handleError (error: Response | any) {
// In a real world app, you might use a remote logging infrastructure
let errMsg: string;
if (error instanceof Response) {
const body = error.json() || '';
const err = body.error || JSON.stringify(body);
errMsg = `${error.status} - ${error.statusText || ''} ${err}`;
} else {
errMsg = error.message ? error.message : error.toString();
}
console.error(errMsg);
return Observable.throw(errMsg);
}
}
code of company.model.ts
export class Company {
CompanyID: number;
Name: string;
Description: string;
EmailAddress: string;
Phone: string;
Address: string;
CreatedBy: number;
CreatedDate: Date;
UpdatedBy: number;
UpdatedDate: Date;
IsActive: boolean;
}
As you get data asynchronously you can use safe navigation operator like:
{{ company?.Name }}
I have seen this error on SO quite a few times, all I can find on it is that I need to make sure that I have my service Provided in my app.modules, and then call it in my constructor of my component. I have done this and am still getting the error. Also I have both http and HTTPMODULES in my application. The error only occurs when I use the delete functionality in my application. Here is the error error_handler.js:45 EXCEPTION: Cannot read property 'get' of undefined, here is some relevant code....
app.module.ts
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { HttpModule, JsonpModule } from '#angular/http'; <------------HTTP
import { AppComponent } from './app.component';
import { PostRantComponent } from './postRant.component';
import { PostDataService } from './PostData.Service'; <------------service
import { Constants } from './app.const.service';
import { Routing } from './app.routes';
import { FormsModule } from '#angular/forms';
#NgModule({
imports: [NgbModule.forRoot(), BrowserModule, Routing, FormsModule, HttpModule, JsonpModule],
declarations: [AppComponent,,PostRantComponent],
providers: [PostDataService, Constants],
bootstrap: [AppComponent]
})
export class AppModule { }
Service (tried cutting it down to just show relevant code)
import { Injectable } from '#angular/core';
import { Http, Response, Headers } from '#angular/http';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import { Observable } from 'rxjs/Observable';
import { PostViewModel } from './models/Post';
import { Constants } from './app.const.service';
#Injectable()
export class PostDataService{
private actionUrl: string;
private headers: Headers;
constructor( private _http: Http, private _constants: Constants ){
this.actionUrl = _constants.ServerWithApi;
this.headers = new Headers();
this.headers.append('Content-Type', 'application/json');
this.headers.append('Accept','application/json');
}
public GetAll = (): Observable<PostViewModel[]> => {
return this._http.get(this.actionUrl)
.map((response: Response) => <PostViewModel[]>response.json())
.catch(this.handleError);
}
public Delete = (id: string) =>{
return this._http.delete(this.actionUrl + id)
.map(res => res.json())
.catch(this.handleError);
}
}
Component
import { Component, Attribute, OnInit,ViewChild } from '#angular/core';
import { PostViewModel } from './models/Post';
import { PostDataService } from './PostData.Service';
import { Constants } from './app.const.service';
#Component({
selector: 'postRant',
templateUrl: 'html/postRant.html',
})
export class PostRantComponent implements OnInit {
txtTitle: string;
txtDescription: string;
public myPosts : Array<PostViewModel>;
public newPost : PostViewModel = new PostViewModel();
constructor(private auth:Auth, private _dataservice: PostDataService){
}
ngOnInit(){
this.getAllItems();
}
private getAllItems():void {
this._dataservice
.GetAll()
.subscribe((Post: Array<PostViewModel>) => this.myPosts = Post,
error => console.log(error),
() => console.log('get all items complete'))
}
delete(id){
console.log(id);
this._dataservice.Delete(id)
.subscribe((res) => {
this.myPosts = res;
});
var index = this.myPosts.findIndex(x => x.id == id);
this.myPosts.splice(index, 1);
}
}
If you are interested in all the code I have it posted on my git located here, however it is rather large.
EDIT
picture of error....
it appears that the error is produced by line 52 of PostData.Service.ts
i.e. var applicationError = error.headers.get('Application-Error');
this makes me guess that your GetAll Http call is erroring out, but the server you are asking for data is not returning data in the format of error.headers
Add a debugger; to the handleError and check the object that it is receiving.
I'trying to use the HTTP service in Angular2 and i have some concerns.
I'm taking meteo datas from openweather API and I just want to put it inside a typeScript variable (meteo: {}) and use it as i want in my template.
Here are my .ts files:
meteo.service.ts
import {Injectable} from "angular2/core";
import {Http, Response} from "angular2/http";
import {Observable} from "rxjs/Observable";
import {MeteoComponent} from "../widgets/meteo/meteo.component";
import {Meteo} from "../widgets/meteo/meteo";
#Injectable()
export class MeteoService {
constructor(private http: Http) {}
// Nom de la ville sans accent
private _ville = 'Montreal';
// Initiales du pays
private _country = 'ca';
// Units (metric/imperial)
private _units = 'metric';
// API KEY
private _APPID = 'ewfw54f5646';
// url to get data
private _meteoUrl = 'http://api.openweathermap.org/data/2.5/weather?q='+this._ville+','+this._country+'&units='+this._units+'&APPID='+this._APPID;
getMeteo (): Observable<Meteo> {
return this.http.get(this._meteoUrl)
.map(this.extractData)
.catch(this.handleError);
}
private extractData(res: Response) {
if(res.status < 200 || res.status >= 300) {
throw new Error('Bad response status: ' + res.status);
}
let body = res.json();
return body || { };
}
private handleError(error: any) {
let errMsg = error.message || 'server error';
console.error(errMsg);
return Observable.throw(errMsg);
}
}
meteo.component.ts
import {Component, OnInit, OnChanges, AfterContentInit} from "angular2/core";
import {MeteoService} from "../../services/meteo.service";
import {Meteo} from "./meteo";
#Component({
selector: 'meteo',
templateUrl: 'dev/widgets/meteo/meteo.component.html',
providers: [MeteoService]
})
export class MeteoComponent implements OnInit {
errorMessage: string;
meteo: Meteo;
// We inject the service into the constructor
constructor (private _meteoService: MeteoService) {}
// Instantiate data in the ngOnInit function to keep the constructor simple
ngOnInit() {
this.getMeteo();
}
getMeteo() {
this._meteoService.getMeteo()
.subscribe(
data => this.meteo = data,
error => this.errorMessage = <any>error);
}
}
meteo.ts
export class Meteo {
data: {};
}
and meteo.component.html
<span class="meteo">{{meteo | json}}°C</span>
Actually the result is the entire json object:
{
"coord": {
"lon":-73.59,
"lat":45.51
},
"weather":[
{
"id":803,
"main":"Clouds",
"description":"broken clouds",
"icon":"04d"
}
],
"base":"cmc stations",
"main":{
"temp":3.96,
"pressure":1020,
"humidity":32,
"temp_min":2,
"temp_max":6.67
},
"wind":{
"speed":2.1
},
"clouds":{
"all":75
},
"dt":1461594860,
"sys":{
"type":1,
"id":3829,
"message":0.004,
"country":"CA",
"sunrise":1461577807,
"sunset":1461628497
},
"id":6077243,
"name":"Montreal",
"cod":200
}
And I would like to display just the temp field.
If you have any idea guys it's welcomed!
Thanks a lot.
You could leverage the Elvis operator since your data are loaded asynchronously:
<span class="meteo">{{meteo?.main.temp | json}}°C</span>
Try setting the data on this.meteo.data
getMeteo() {
this._meteoService.getMeteo()
.subscribe(
data => this.meteo.data = data,
error => this.errorMessage = <any>error);
}
and then displaying it with
<span class="meteo">{{meteo.data.main.temp}}°C</span>
Part of my code:
import {Injectable} from 'angular2/core';
import {Http, Headers, Request, Response} from 'angular2/http';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/map';
#Injectable()
export class myClass {
constructor(protected http: Http) {}
public myMethod() {
let request = new Request({
method: "GET",
url: "http://my_url"
});
return this.http.request(request)
.map(res => res.json())
.catch(this.handleError); // Trouble line.
// Without this line code works perfectly.
}
public handleError(error: Response) {
console.error(error);
return Observable.throw(error.json().error || 'Server error');
}
}
myMethod() produces exception in console of browser:
ORIGINAL EXCEPTION: TypeError: this.http.request(...).map(...).catch is not a function
Perhaps you can try adding this in your imports:
import 'rxjs/add/operator/catch';
You can also do:
return this.http.request(request)
.map(res => res.json())
.subscribe(
data => console.log(data),
err => console.log(err),
() => console.log('yay')
);
Per comments:
EXCEPTION: TypeError: Observable_1.Observable.throw is not a function
Similarly, for that, you can use:
import 'rxjs/add/observable/throw';
New service updated to use the HttpClientModule and RxJS v5.5.x:
import { Injectable } from '#angular/core';
import { HttpClient, HttpErrorResponse } from '#angular/common/http';
import { Observable } from 'rxjs/Observable';
import { catchError, tap } from 'rxjs/operators';
import { SomeClassOrInterface} from './interfaces';
import 'rxjs/add/observable/throw';
#Injectable()
export class MyService {
url = 'http://my_url';
constructor(private _http:HttpClient) {}
private handleError(operation: String) {
return (err: any) => {
let errMsg = `error in ${operation}() retrieving ${this.url}`;
console.log(`${errMsg}:`, err)
if(err instanceof HttpErrorResponse) {
// you could extract more info about the error if you want, e.g.:
console.log(`status: ${err.status}, ${err.statusText}`);
// errMsg = ...
}
return Observable.throw(errMsg);
}
}
// public API
public getData() : Observable<SomeClassOrInterface> {
// HttpClient.get() returns the body of the response as an untyped JSON object.
// We specify the type as SomeClassOrInterfaceto get a typed result.
return this._http.get<SomeClassOrInterface>(this.url)
.pipe(
tap(data => console.log('server data:', data)),
catchError(this.handleError('getData'))
);
}
Old service, which uses the deprecated HttpModule:
import {Injectable} from 'angular2/core';
import {Http, Response, Request} from 'angular2/http';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/observable/throw';
//import 'rxjs/Rx'; // use this line if you want to be lazy, otherwise:
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/do'; // debug
import 'rxjs/add/operator/catch';
#Injectable()
export class MyService {
constructor(private _http:Http) {}
private _serverError(err: any) {
console.log('sever error:', err); // debug
if(err instanceof Response) {
return Observable.throw(err.json().error || 'backend server error');
// if you're using lite-server, use the following line
// instead of the line above:
//return Observable.throw(err.text() || 'backend server error');
}
return Observable.throw(err || 'backend server error');
}
private _request = new Request({
method: "GET",
// change url to "./data/data.junk" to generate an error
url: "./data/data.json"
});
// public API
public getData() {
return this._http.request(this._request)
// modify file data.json to contain invalid JSON to have .json() raise an error
.map(res => res.json()) // could raise an error if invalid JSON
.do(data => console.log('server data:', data)) // debug
.catch(this._serverError);
}
}
I use .do() (now .tap()) for debugging.
When there is a server error, the body of the Response object I get from the server I'm using (lite-server) contains just text, hence the reason I use err.text() above rather than err.json().error. You may need to adjust that line for your server.
If res.json() raises an error because it could not parse the JSON data, _serverError will not get a Response object, hence the reason for the instanceof check.
In this plunker, change url to ./data/data.junk to generate an error.
Users of either service should have code that can handle the error:
#Component({
selector: 'my-app',
template: '<div>{{data}}</div>
<div>{{errorMsg}}</div>`
})
export class AppComponent {
errorMsg: string;
constructor(private _myService: MyService ) {}
ngOnInit() {
this._myService.getData()
.subscribe(
data => this.data = data,
err => this.errorMsg = <any>err
);
}
}
There are several ways to do this. Both are very simple. Each of the examples works great. You can copy it into your project and test it.
The first method is preferable, the second is a bit outdated, but so far it works too.
1) Solution 1
// File - app.module.ts
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { HttpClientModule } from '#angular/common/http';
import { AppComponent } from './app.component';
import { ProductService } from './product.service';
import { ProductModule } from './product.module';
#NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
HttpClientModule
],
providers: [ProductService, ProductModule],
bootstrap: [AppComponent]
})
export class AppModule { }
// File - product.service.ts
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
// Importing rxjs
import 'rxjs/Rx';
import { Observable } from 'rxjs/Rx';
import { catchError, tap } from 'rxjs/operators'; // Important! Be sure to connect operators
// There may be your any object. For example, we will have a product object
import { ProductModule } from './product.module';
#Injectable()
export class ProductService{
// Initialize the properties.
constructor(private http: HttpClient, private product: ProductModule){}
// If there are no errors, then the object will be returned with the product data.
// And if there are errors, we will get into catchError and catch them.
getProducts(): Observable<ProductModule[]>{
const url = 'YOUR URL HERE';
return this.http.get<ProductModule[]>(url).pipe(
tap((data: any) => {
console.log(data);
}),
catchError((err) => {
throw 'Error in source. Details: ' + err; // Use console.log(err) for detail
})
);
}
}
2) Solution 2. It is old way but still works.
// File - app.module.ts
import { BrowserModule } from '#angular/platform-browser';
import { NgModule } from '#angular/core';
import { HttpModule } from '#angular/http';
import { AppComponent } from './app.component';
import { ProductService } from './product.service';
import { ProductModule } from './product.module';
#NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
HttpModule
],
providers: [ProductService, ProductModule],
bootstrap: [AppComponent]
})
export class AppModule { }
// File - product.service.ts
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
// Importing rxjs
import 'rxjs/Rx';
import { Observable } from 'rxjs/Rx';
#Injectable()
export class ProductService{
// Initialize the properties.
constructor(private http: Http){}
// If there are no errors, then the object will be returned with the product data.
// And if there are errors, we will to into catch section and catch error.
getProducts(){
const url = '';
return this.http.get(url).map(
(response: Response) => {
const data = response.json();
console.log(data);
return data;
}
).catch(
(error: Response) => {
console.log(error);
return Observable.throw(error);
}
);
}
}
The RxJS functions need to be specifically imported. An easy way to do this is to import all of its features with import * as Rx from "rxjs/Rx"
Then make sure to access the Observable class as Rx.Observable.
in the latest version of angular4 use
import { Observable } from 'rxjs/Rx'
it will import all the required things.