Reputation: 100
I'm doing a simple weather app project for college and I'm having a real headache using ionic storage to return values back that have been passed in to storage. The application is fairly basic, GET lat/lon values from an API, save them in storage and then pass them to another API to GET more information and display it on a home page.
I can get the values from the API initially straightforward enough and I've pulled the relevant information I need from the API, stored them in an object, cityInfo, inside a provider (below, this may be horribly incorrect way to do it so all recommendations are welcome.
private cityInfo = {
cca2: "",
commonName: "",
flag: "",
lat: 0,
lng: 0
};
then I've set the value in storage as follows
packCityInfo(cityData: any): void {
this.cityInfo.cca2 = (cityData[0].cca2);
this.cityInfo.commonName = (cityData[0].name.common);
this.cityInfo.flag = (cityData[0].flags.png);
this.cityInfo.lat = (cityData[0].latlng[0]);
this.cityInfo.lng = (cityData[0].latlng[1]);
this.storage.set('cityInfo', this.cityInfo);
If I console.log(cityInfo) it prints the different values for the different properties correctly i.e.
cca2: "GB"
commonName: "United Kingdom"
flag: "https://flagcdn.com/w320/gb.png"
lat: 54
lng: -2
The problem I'm having is figuring out how to then access these values out of storage. I think the issue I'm having is around synchronicity. I found that the first time I try to save my settings the request will fail and say that the ".lat is undefined" but the second time I save my settings the request will succeed and I'll get information from the API request from storage.
My saveSettings() function In my settings.ts page the following
this.storage.set('city', this.cityName);
this.storage.set('units', this.units);
this.cityDataService.getCityDataFromAPI(this.cityName);
this.storage.get("cityInfo").then((val) => {
let cityInfo = val;
// return val;
this.weatherService.getWeatherFromApiCoordinates(cityInfo.lat, cityInfo.lng, this.units);
}).catch((err) => {
console.log(err);
});
Returns "TypeError: Cannot read properties of null (reading 'lat') at settings.ts:48"
The Services are pretty standard and just handle the http get requests so I don't think the issue is with them? (Could be wrong ofc)
getWeatherFromApiCoordinates(lat: number, lon: number, units: string) : void {
let weatherData = this.http.get('https://api.weatherbit.io/v2.0/current?lat=' + lat + '&lon=' + lon + "&key=" + this.apiKey + '&units=' + units);
weatherData.subscribe(data => {
let currentWeather = data;
this.packWeatherData(currentWeather);
});
}
The second time the saveSettings() function is run it will correctly display values but its pulling from the previously set values. So for example I run
Each time storage is cleared it will run Error initially obv
I'm a little green around the concepts of promises and synchronicity so any help will be massively appreciated. (I understand the concepts by and large but not so much practically)
Upvotes: 1
Views: 1554
Reputation: 73387
Seems to be a race condition going on, http-request is still performing and storage value is not set storage when you are calling get
on it. Setting data into ionic storage is asynchronous. If you need to wait for something to happen, you can always call async ... await
on storage:
async doSomething() {
await this.storage.set(...)
await this.storage.get(...) // here is available!
}
Though you also have http-requests using observables. You can of course turn them into promises as well, or otherwise chain them so that you correctly have all data when needed.
I don't actually even see that you would need to wait for the storage here, why don't use switchMap
to perform the second request as you need the response from the previous to perform the next one. That is what I would do, so just a suggestion...
Service:
getCityDataFromAPI() {
return this.http.get(....)
}
getWeatherFromApiCoordinates(...) {
return this.http.get(...);
}
Then the component would do:
this.weatherService.getCityDataFromAPI().pipe(
switchMap(cityData => {
return this.weatherService.getWeatherFromApiCoordinates(cityData[0].latlng[0], cityData[0].latlng[1], this.units)
})
).subscribe(console.log)
If you need to save in storage for future purposes, you can always handle side-effects with tap
.
Upvotes: 1