ERROR TypeError: Cannot read property 'list' of undefined - javascript

Getting the error as mentioned when I am trying to get the response from API call and display the information in the response object in the template.
ERROR TypeError: Cannot read property 'list' of undefined.
In service.ts, I am making a server call. I am using OpenWeatherMap API.
getDetails(cityName, countryCd): Observable<any>{
return this.httpclient.get(`${this.url}${cityName},${countryCd}&APPID=${this.apiKey}`);
}
Object format returned by the API:
{"city":{"id":1851632,"name":"Shuzenji",
"coord":{"lon":138.933334,"lat":34.966671},
"country":"JP",
"cod":"200",
"message":0.0045,
"cnt":38,
"list":[{
"dt":1406106000,
"main":{
"temp":298.77,
"temp_min":298.77,
"temp_max":298.774,
"pressure":1005.93,
"sea_level":1018.18,
"grnd_level":1005.93,
"humidity":87,
"temp_kf":0.26},
"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04d"}],
"clouds":{"all":88},
"wind":{"speed":5.71,"deg":229.501},
"sys":{"pod":"d"},
"dt_txt":"2014-07-23 09:00:00"}
]}
In home.component.ts, I am subscribing to the service API call. I am getting the reposnse which is of type 'object'.
I have an array of objects called cities. I am loading the response object from the server as one of the property in each object of the array cities.
this.cities = this.cityService.getCities().slice();
this.cities.forEach(element => {
this.weatherService.getDetails(element.cityName, element.countryCd).subscribe(response => {
element['cityWeather'] = response;});
});
In home.component.html, I am iterating this cities array and the using the response data from the API. The data is displayed properly without any issues.
But in the console I am seeing an error for every iteration of cities array -
ERROR TypeError: Cannot read property 'list' of undefined.
<div class="col-md-4" *ngFor="let city of cities; let i = index">
<span>{{city.cityWeather.list[0].main.temp | kelvinToCelcius}} ℃</span>
<span>{{city.cityWeather.list[0].wind.speed | mtrPersecToKmPerhr}} km/h</span>
</div>
I am relatively new to Angular, need to know what is this error about and how this can be resolved.

Your issue is that in your home component you refer to
<span>{{city.cityWeather.list[0].main.temp | kelvinToCelcius}} ℃</span>
<span>{{city.cityWeather.list[0].wind.speed | mtrPersecToKmPerhr}} km/h</span>
These values may not be defined at all times, so if city.cityWeather is undefined, accessing the .list property would result in the error
Cannot read property 'list' of undefined.
You can fix this by using the Angular safe navigation operator (also known as the Elvis operator).
The Angular safe navigation operator (?.) is a fluent and convenient way to guard against null and undefined values in property paths
-- Angular docs
By changing . to ?., Angular will not throw an error if it can't find the property on the object.
Note that this only works in component templates, and not in TypeScript files.

You can either use The Angular
safe navigation operator (?.)
city.cityWeather?.list[0]?.main.temp
city.cityWeather?.list[0]?.wind.speed
or you can try using Angular async pipe
https://angular.io/api/common/AsyncPipe

Try to change your code like this:
<div class="col-md-4" *ngFor="let city of cities; let i = index">
<span>{{city?.cityWeather?.list[0]?.main?.temp | kelvinToCelcius}} ℃</span>
<span>{{city?.cityWeather?.list[0]?.wind?.speed | mtrPersecToKmPerhr}} km/h</span>
</div>
This may be due the fact that your getCities() request is completed and your getDetails() call is yet to return response which it will give at some point in time due to the asynchronous nature of code. So when the loop execute cityWeather is not available and you get the error. The ? will not throw error but instead it will show data when it will be available.

Related

InvalidPipeArgument: '[object Object]' for pipe 'AsyncPipe', cannot modify HTML

I am working to populate a dropdown on an application using Angular and ngrx. The app uses a dropdown component which is used across the entire application for dropdowns, so I cannot modify it.
The HTML from that component which is most important for my question is as follows:
<option *ngFor="let option of options" [value]="stringifyOption(option.value)" [selected]="option.value === (selectedValue | async)">
{{option.text}}
</option>
I am getting my dropdown options, which is an array of objects (dateOptions), from the store. Each object has a property value (week) which I am getting and setting as the options for the dropdown.
this.weekDropdownOptions = dateOptions.map(weekObj => weekObj.week);
Logging weekDropdownOptions in the console returns an array of the options like I was expecting, like this:
["04/01 - 04/07", "04/08 - 04/14"]
My HTML has the selector for the dropdown and sets [options]="weekDropdownOptions"
I can't seem to get past this error:
InvalidPipeArgument: '[object Object]' for pipe 'AsyncPipe'.
It is pointing towards the dropdown component, however I can't modify the HTML. What can I do in my code to make this work?
the async pipe expects an Observable and not an Object. it would be interesting to see how exactly selectedValue is populated and why it's used with the async pipe.
here is an example on the difference in stackblitz https://stackblitz.com/edit/angular-vv7pzs
The async pipe subscribes to an Observable or Promise and returns the latest value it has emitted.
According to the error, selectedValue is an object so you can not use the async pipe.

NEXT.js isomorphic-unfetch errs out

I am doing the following exercise on Next.js remote fetching. What I am not able to understand and adapt is the exercise fetches data from http://api.tvmaze.com.
Data is like:
[
{"score":24,"show":{"id":975,"url":"", "name":""}}
,{"score":25,"show":{"id":976,"url":"", "name":""}}
,{"score":26,"show":{"id":977,"url":"", "name":""}}
]
The index page uses the following mapping:
{props.shows.map(({show}) => (
<li key={show.id}>
<Link as={'/p/${show.id}'} href={'/post?id=${show.id}'}>
<a>{show.name}</a>
</Link>
</li>
))}
Note, it has {"score":"", "show":{}} structure. So, the props.shows.map uses a variable show and refers to show.id and show.name. And it works fine. I am confused to understand how does it work? Because id is nestled inside and to me it should be show.show.id.
Also, I am trying to fetch a different source of data that has slightly different structure.
[
{"id": 1, "name":"name1"}
,{"id": 2, "name":"name2"}
,{"id": 3, "name":"name3"}
]
The same code fails with error:
Unhandled Rejection (TypeError): Cannot read property 'id' of undefined
So, apart from it has obviously different data structure. I could not understand why, if my data structure is slightly different, how do I change the props.shows.map function change accordingly?
Are you familiar with object destructuring in JS? https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
In the example, writing ({show}) is shorthand for writing something like (resp) and then later show = resp.show. So in this case, due to destructuring, show already is the show object. Therefore, id works.
Your data doesn't have a show property, so when destructuring, show is undefined. And, of course, you can't get a property of undefined. Removing the curly braces from {show} should do the trick.

Angular 5 - Replacement for json().token in HttpClient? [duplicate]

I am trying to change an existing app from using Http to using HttpClient, however i have an error.
So in my service now you can see the new code vs the old code that's been commented out:
constructor(
// private http: Http
private http: HttpClient
) { }
getSidebar() {
// return this.http.get('http://localhost:3000/sidebar/edit-sidebar')
// .map(res => res.json());
return this.http.get('http://localhost:3000/sidebar/edit-sidebar');
}
And in my page.component.ts I have this
this.sidebarService.getSidebar().subscribe(sidebar => {
this.sidebar = sidebar.content; // this does not work now
});
However for the line above which I commented on I get this error now:
Property 'content'
does not exist on type 'Object'.
However if I console.log(sidebar) I get the following:
{_id: "59dde326c7590a27a033fdec", content: "<h1>sidebar here</h1>"}
So what's the issue?
Once again, Http works but HttpClient does not.
You can specify the type that is being returned, using an interface, class, etc. For example, you can use something like the following:
return this.http.get<Sidebar>('http://localhost:3000/sidebar/edit-sidebar');
As an example, Sidebar might be defined as:
interface Sidebar {
_id: string;
content: string;
}
See Typechecking the response from the Angular docs for further information:
...TypeScript would correctly complain that the Object coming back from HTTP does not have a results property. That's because while HttpClient parsed the JSON response into an Object, it doesn't know what shape that object is.
HttpClient parse automatically the JSON response to an Object and the shape of that object is not known, that's why Typescript show this error
alternative solution, using bracket notation:
this.sidebarService.getSidebar().subscribe(sidebar => {
this.sidebar = sidebar["content"];
});
You can assign the variable(sidebar) an interface to explicitely tell what it will get or assign to it so it doesnt throw compile time error.
this.sidebarService.getSidebar().subscribe((sidebar: any) => {
this.sidebar = sidebar.content;
});
you can do this like that with type <any>
if you do this you will not get that error
this.http.get<any>('http://localhost:3000/sidebar/edit-sidebar');
Using bracket notation instead of dot notation to reference sidebar['content'] instead of sidebar.content will solve the issue.
The Type-checking the response section of the Angular docs on HttpClient mention's this, although it's not all that clear:
The subscribe callback above requires bracket notation to extract the
data values. You can't write data.heroesUrl because TypeScript
correctly complains that the data object from the service does not
have a heroesUrl property.
Error Description
ERROR in src/app/home.component.ts(131,20): error TS2339: Property
'someProperty' does not exist on type '{}'.
why Occured ?
I think this error is in service file , whenever you making http call and that call returns any Object(json) or array of Objects (json) , then yours http call should return Observable or Model of that result type
Home.service.ts
connectToServer(data) {
return this.http.post('http://localhost:5000/connect' , data)
}
Suppose , I have above Post http call , where i connect to server by passing data inside 'data' and this request returns me connection Object (Json).But where u seen that i didn't specify observable or model for holding result and if i access the result Object properties from component.ts it will says result.somepropery does not exist on {} .
Solutions
First :
connectToServer(data) : Observable<any> {
return this.http.post('http://localhost:5000/connect' , data)
}
Note : Don't forget to import Observable
Second :
connectToServer(data) : <Connection> {
return this.http.post('http://localhost:5000/connect' , data)
}
In second case you need to define Interface for declaring Connection Object fields and needs to import here.

How to show TypeScript object in HTML (Ionic/Firebase/Angular)

How do I display an object in my HTML file? (Using Ionic) It seems fairly simple, but it won't work. I retrieve the object like this, from a snapshot of my database path:
this.userPath.subscribe(snapshots => {
this.snapshotValue = snapshots.val()
console.log(this.snapshotValue);
})
And the console looks something like this:
http://i.imgur.com/Kk0A6n5.png (I don't have 'reputation' to post images)
I feel like I've tried everything. Like:
<h1>{{snapshotValue}}</h1> //this returns "[object object]"
<h1>{{snapshotValue.navn}}</h1> //this gives me an error: "cannot read property navn of undefined"
<h1>{{snapshotValue[0]}}</h1> //this gives me an error: "cannot read property '0' of undefined.
How do I get the individual values from this object?
you want to use the async pipe with the elvis operator (?) for observables. Below is using just the elvis.
<h1>{{snapshotValue?.navn}}</h1>
The elvis operator checks if the object exists before checking for a property on the object.
In the future instead of using this
this.userPath.subscribe(snapshots => {
this.snapshotValue = snapshots.val()
console.log(this.snapshotValue);
})
you can do
<h1>{{(userPath | async)?.navn}}</h1>
https://angular.io/api/common/AsyncPipe
The async pipe does the subscribe and gets the latest value for you
This happens when you try to render the view before the data is available.Try the following method
In your component declare a boolean variable
private isDataAvailable:boolean=false;
And in your subscribe method make this true once the data is available
this.userPath.subscribe(snapshots => {
this.snapshotValue = snapshots.val()
console.log(this.snapshotValue);
this.isDataAvailable=true;
})
And reneder the template once the data is available using *ngIf
<div *ngIf="isDataAvailable">
<h1>{{snapshotValue}}</h1>
<h1>{{snapshotValue.navn}}</h1>
<h1>{{snapshotValue[0]}}</h1>
</div>
This should do the trick.You can also use async pipe for the same.Find docs here
<h1>{{snapshotValue | json}}</h1> // this should return you a { stringified json }

Receiving error using ngFor and ngIf in Angulsrjs2 and Ionic 2

JoinedArray is an array with some values.
I have to check if the event.listDetails.accessId is present there in the array or not . If accessId is not there in the array then the event-card should not print
<ion-list *ngSwitchCase="'joined'">
<event-card *ngIf="event.listDetails.accessId in joinedArray " ></event-card>
</ion-list>
error is:
EXCEPTION: Error: Uncaught (in promise): Template parse errors:
Property binding ngIfIn not used by any directive on an embedded
template. Make sure that the property name is spelled correctly and
all directives are listed in the "directives" section. ("e keys and i
have to check if it is present in the array or not ..please
suggest-->-->
[ERROR ->]
"): EventListComponent#31:12
Instead of including that logic in your view, you could just create a property in the component like this:
public showCard: bool;
// In the constructor or somewhere else
//...
this.showCard = joinedArray.indexOf(event.listDetails.accessId) > -1;
// ...
And then in the view:
<ion-list *ngSwitchCase="'joined'">
<event-card *ngIf="showCard"></event-card>
</ion-list>
That way we keep things simple, the view just takes care of showing or hiding things, and the component code decides whether if that should be shown or not.
Please also notice that I've used the indexOf() (Reference) method to see if the array contains that value.

Categories

Resources