Very new to Angular. I checked similar questions but they either dive into specifics or I just don't understand the solutions.
The actual error:
"Cannot read property 'idPlanet' of undefined at Object.eval [as updateRenderer] (PlanetComponent.html:11)"
The issue:
planetDetail.idPlanet is undefined most probably?
Suspect:
getPlanetDetail()
planet.component.html:
<div class="col-sm-9">
ID: {{ planetDetail.idPlanet }}
</div>
planet.component.ts:
import {
Component,
OnInit
} from '#angular/core';
import { Planet } from './planet';
import { PlanetService } from './planet.service';
#Component({
selector: 'planets',
templateUrl: './planet.component.html'
})
export class PlanetComponent implements OnInit {
private planets: Planet[];
private planetDetail: Planet;
constructor(
private planetService: PlanetService
) {}
public ngOnInit(): void {
this.getPlanetDetail();
this.getPlanets();
}
public getPlanets() {
this.planetService.getPlanets()
.subscribe(
(planets) => this.planets = planets
);
}
public getPlanetDetail() {
this.planetService.getPlanetDetail()
.subscribe(
(planets) => this.planetDetail = planets
);
}
}
planet.service.ts:
import { Injectable } from '#angular/core';
import { Headers, Http, Response } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import { Planet } from './planet';
#Injectable()
export class PlanetService {
private planetsUrl = 'http://localhost/index.php/api/planet/'; // URL to web api
// Injecting the http client into the service
constructor(private http: Http) {}
public getPlanets(): Observable<Planet[]> {
return this.http.get(this.planetsUrl + 'planets/id_sol/1')
.map(this.parseData)
.catch(this.handleError);
}
public getPlanetDetail(): Observable<Planet> {
return this.http.get(this.planetsUrl + 'planet/id_planet/1')
.map(this.parseData)
.catch(this.handleError);
}
private parseData(res: Response) {
let body = res.json();
if (body instanceof Array) {
return body || [];
} else {
return body.post || {};
}
}
private handleError(error: any): Promise<any> {
console.error('An error occurred', error); // for demo purposes only
return Promise.reject(error.message || error);
}
}
I'm at a loss tbh, I tried to build my getPlanetDetail() method from getPlanets() which works fine. Should I use a promise?
I'm having a hard time figuring out where exactly I can put console.log() to debug. I'm using angular-starter kit from Github.
Thanks for your time.
edit 1: the api outputs {"idPlanet":"1","name":"Earth"}
As the request is asynchronous sometime the value will not be fetched from the server when the page loads, therefore, the planetDetail is undefined. in order to avoid this you can use add '?' between planetDetail and idPlanet. that prints only if it has the value
ID: {{ planetDetail?.idPlanet }}
If you want to print the result or error
public getPlanetDetail() {
this.planetService.getPlanetDetail()
.subscribe(
(planets) => {
console.log(planets);
this.planetDetail = planets
},
error => {console.log(error);}
);
}
Since your response is {"idPlanet":"1","name":"Earth"}, try the following:
public getPlanetDetail(): Observable<Planet> {
return this.http.get(this.planetsUrl + 'planet/id_planet/1')
.map(res => res.json())
.catch(this.handleError);
}
my friend to debug if your 'planetDetail' is populated, just add 'console.log (planetDetail)' in the 'subscribe' method. Follow the example below.
public getPlanetDetail() {
this.planetService.getPlanetDetail()
.subscribe(
(planets) => this.planetDetail = planets
);
}
public getPlanetDetail() {
this.planetService.getPlanetDetail()
.subscribe(
(planets) => this.planetDetail = planets, (err) => (err), () => console.log(this.palnetDetail)
);
}
More about subscribe()
subscribe(function(response) {
console.log("Success Response" + response)
},
function(error) {
console.log("Error happened" + error)
},
function() {
console.log("the subscription is completed")
});
Related
My Service
import { Injectable } from "#angular/core";
import { Observable, of } from "rxjs";
import { SearchResult } from "../Components/container-search/Models/SearchResult";
import { environment } from "../../environments/environment";
import { HttpClient, HttpHeaders } from "#angular/common/http";
#Injectable({
providedIn: "root",
})
export class ContainerService {
constructor(public http: HttpClient) {}
private SearchResults: SearchResult[] = [];
public headers = {
headers: new HttpHeaders({
"Content-Type": "application/json",
}),
};
public Search(): Observable<SearchResult[]> {
if (this.SearchResults.length === 0) {
this.http
.get<SearchResult[]>(
environment.endpointURL + "/FooBar/Search",
this.headers
)
.subscribe((x) => {
this.SearchResults = x;
return of(this.SearchResults);
});
} else {
return of(this.SearchResults);
}
}
}
When I call Search() in my component it returns
TypeError: Cannot read property 'subscribe' of undefined
My calling code is
ngOnInit(): void {
this.dataSource.paginator = this.paginator;
this.searchService.Search().subscribe((x) => {
this.dataSource = new MatTableDataSource<SearchResult>(x);
});
}
Can someone explain why this code this.searchService.Search() would always return with the above error?
The .subscribe call is returning an Observable, but that isn't what's being returned by the Search method. The subscription is an asynchronous process. The subscribe kicks off that process and only reacts when the http call returns, but the Search method keeps executing and returns undefined.
The below code will return the Observable from the http call directly and fix your issue.
import { tap } from 'rxjs/operators';
public Search(): Observable<SearchResult[]> {
if (this.SearchResults.length === 0) {
return this.http
.get<SearchResult[]>(
environment.endpointURL + "/FooBar/Search",
this.headers
).pipe(tap(x => this.SearchResults = x));
} else {
return of(this.SearchResults);
}
}
I am trying to post data to my api. The post.subscribe does not send any data, no error is being thrown. The API is 100% working.
Here is my code:
httpservice.ts
import { HttpClient, HttpHeaders } from '#angular/common/http';
import { Product } from './Product';
import { Observable, of } from 'rxjs';
import { Injectable } from '#angular/core';
#Injectable({
providedIn: 'root',
})
export class HttpService {
baseURL = 'https://localhost:2403/testapi/';
constructor(private http: HttpClient) {
this.products = new Array();
}
products: Product[];
post(product: Product): boolean {
if ( !this.checkIfProductExistsAlready(product) ) {
console.log('posting product');
this.http.post<any>(baseURL,{"product": product.name, "price": 10, "done": false})
.subscribe((data) => {
console.log(data);
product.id = data.id;
console.log('hi');
},
error => console.log('uojdsigdk' + error)
);
console.log('posted ' + product.id);
this.products.push(product);
return true;
} else {
return false;
}
}
form.component.ts
addItem(): void {
this.isError = false;
if (!this.httpservice.post(new Product(-1, this.name, this.price, 0))) {
this.isError = true;
}
}
This is the provider declaration in the app.module.ts
[...]
providers: [HttpService],
[...]
Is it possible that this is caused by a config file?
Maybe this happens because you try to access the local webserver over https?
baseURL = 'https://localhost:2403/testapi/';
Otherwise use fiddler, do a post request on your api and look what the server is returning. :)
I think baseURL is undefined inside your function scope. Try this.baseURL instead. Also make sure your local webserver is serving HTTPS, as mentioned before
this.http.post<any>(baseURL, product);
becomes
this.http.post<any>(this.baseURL, product);
On a side node, a couple of things are potentially wrong with your Observable code, as well as the way you are injecting your Service in your app, as has been mentioned in comments.
add response type
addItem(): void {
this.isError = false;
if (!this.httpservice.post(new Product(-1, this.name, this.price, 0), { observe: 'response',responseType: 'text' })) {
this.isError = true;
}
}
In my Angular application, there is a global error handler and a service responsible for making http calls, having return type as Observable<any>. Some services have handled the errors explicitly and some not.
For those, which has not been catched, the global error (having class 'CustomErrorHandler') runs fine. For the service calls, which has been handled and catched gracefully, the global error doesn't seem to fire.
My question: Is there a way to execute the global error handling irrespective of whether the http service calls has been handled or not?
custom-error-handler.service.ts
#Injectable()
export class CustomErrorHandler implements ErrorHandler {
constructor(private injector: Injector) { }
handleError(error) {
error = error || new Error('There was an unexpected error');
const loggingService: LoggerService = this.injector.get(LoggerService);
const location = this.injector.get(LocationStrategy);
const message = error.message ? error.message : error.toString();
const url = location instanceof PathLocationStrategy
? location.path() : '';
// get the stack trace, lets grab the last 10 stacks only
StackTrace.fromError(error).then(stackframes => {
const stackString = stackframes
.splice(0, 20)
.map((sf) => {
return sf.toString();
}).join('\n');
// log with logging service
loggingService.error({ message, url, stack: stackString });
});
throw error;
}
}
auth.service.ts
#Injectable()
export class UserAuthService {
login(emailAddress: string, password: string): Observable<boolean> {
if (this.isAuthenticated()) {
return Observable.of(true);
}
return Observable.create(observer => {
this.http.get('http://someurl/login')
.map(res => res.json())
.subscribe(
data => {
observer.next(this.isAuthorized);
observer.complete();
}, err => {
observer.error(err);
}
);
});
}
}
my-component.ts
import { UserAuthService } from '../services/auth.service';
#Component({
selector: 'my-component',
templateUrl: './my-component.html',
styleUrls: ['./my-component.scss']
})
export class MyComponent {
constructor(private authService: UserAuthService) {}
handleLogin() {
this.authService.login(formValues.emailAddress, formValues.password)
.subscribe(res => {
console.log('Logged In');
}, err => {
console.log('Logging Failed');
// Global Error DOESN'T fire here as the Error has been handled
});
}
handleLoginPart2() {
this.authService.login(formValues.emailAddress, formValues.password)
.subscribe(res => {
console.log('Logged In');
}); // Global Error does fire here as the Error has NOT been handled
}
}
I was able to resolve the issue myself, by creating a HttpClient which inherits from Http.
By doing this I am able to handle the error gracefully.
http-client.service.ts
import { ConnectionBackend, Http, RequestOptions, RequestOptionsArgs, Response } from '#angular/http';
#Injectable()
export class HttpClient extends Http {
http;
constructor(backend: ConnectionBackend, defaultOptions: RequestOptions) {
super(backend, defaultOptions);
}
get(url, options?: RequestOptionsArgs): Observable<Response> {
return super.get(url, options)
.catch(this.handleError);
}
private handleError(errorRes: Response | any) {
return Observable.throw(retError);
}
}
I have a trouble with angular 2 here.
I use service that return promise but when i try to retrive the response i got an error.
i was read this this stact question
this my code.
this is HotelService.ts
import { Injectable } from '#angular/core';
import { Http } from '#angular/http';
//rxjs promises cause angular http return observable natively.
import 'rxjs/add/operator/toPromise';
#Injectable()
export class HotelService {
private BASEURL : any = 'http://localhost:8080/hotel/';
constructor(private http: Http) {}
load(): Promise<any> {
return this.http.get(this.BASEURL + 'api/client/hotel/load')
.toPromise()
.then(response => {
response.json();
//console.log(response.json());
})
.catch(err => err);
}
}
this Hotel.ts (component)
import { Component, OnInit } from '#angular/core';
import { NavController } from 'ionic-angular';
import { HotelService } from '../../providers/hotel/hotelservice';
import { AboutPage } from '../../pages/about/about';
import { HotelDetailPage } from '../../pages/hoteldetail/hotel';
#Component({
selector: 'page-home',
templateUrl: 'home.html',
providers: [HotelService]
})
export class HomePage implements OnInit {
public searchBoxActive = false;
public hotels: any;
constructor(
private navCtrl: NavController,
private hotelServ: HotelService
) { }
load() {
this.hotelServ.load()
.then(res => {
this.hotels = res;
console.log(res); //why the rest is undefined?
console.log('ini component');
},
err => err);
}
toggleSearchBox() {
if (this.searchBoxActive == false) {
this.searchBoxActive = true;
} else {
this.searchBoxActive = false;
}
}
showAbout() {
this.navCtrl.setRoot(AboutPage);
}
pushDetail(evt, id) {
this.navCtrl.push(HotelDetailPage)
}
ngOnInit(): void {
this.load();
}
}
I have no idea.
You need to return response.json() from promise then callback:
load(): Promise<any> {
return this.http.get(this.BASEURL + 'api/client/hotel/load')
.toPromise()
.then(response => {
return response.json();
})
.catch(err => err);
}
The dfsq's answer is correct, but for the completeness' sake, below is an example according to the official Angular.io recommendations:
load(): Promise<any> {
return this.http.get(this.BASEURL + 'api/client/hotel/load')
.toPromise()
.then(response: Response) => response.json() || {})
.catch((error: Response | any) =>
{
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();
return Promise.reject(errMsg);
});
}
Key differences:
handle empty response in the then;
pretty up the error before throwing it further.
I am using the following code value of this become null when i call it inside the then function here is the code. Am i doing something wrong or it is like this or there is any work around to resolve this issue
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Headers, RequestOptions } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import { CanActivate, Router } from '#angular/router';
import { AuthService } from '../services/auth.service';
import { WebAPISettings } from '../services/webapisettings.service';
#Injectable()
export class LoginService {
//_ngWEBAPISettings: WebAPISettings;
//_authService: AuthService;
constructor(private http: Http, private ngWEBAPISettings: WebAPISettings, private authService: AuthService) {
//this._ngWEBAPISettings = ngWEBAPISettings;
//this._authService = authService;
}
public login(username: string, password: string): Promise<any> {
let data = "grant_type=password&username=" + username + "&password=" + password;
let headers = new Headers({ 'Content-Type': 'application/x-www-form-urlencoded' });
let options = new RequestOptions({ headers: headers });
try {
debugger;
return this.http.post(this.ngWEBAPISettings.apiServiceBaseUri + "token", data, options)
.toPromise()
.then(function (res: Response) {
debugger;
let body = res.json();
//let _authService: AuthService = new AuthService();
this.authService.fillAuthDataFromLogin(body);
//this.router.navigate(['/Home']);
return body.data || {};
})
.catch(this.handleError);
}
catch (error) {
console.log(error);
}
}
private extractData() {
}
private handleError(error: any) {
debugger;
let errMsg = (error.message) ? error.message :
error.status ? `${error.status} - ${error.statusText}` : 'Server error';
console.error(errMsg); // log to console instead
return Observable.throw(errMsg);
}
}
and i am debugging it in the chrome here is the screenshot please help me in fixing it.
after using the arrow function same thing check the screen shot
one thing to mention i am using Angular2 RC4.
You could use an arrow function to be able to use the lexical this:
return this.http.post(this.ngWEBAPISettings.apiServiceBaseUri + "token", data, options)
.toPromise()
.then((res: Response) => { // <-----
(...)
});
This way, this will correspond to the instance of the LoginService service.
See this doc for more details:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions