I am working on a weather app from OpenWeatherMap API, I blocked my service twice already, because of too many requests. I checked my code many times and I can't find a single place which would cause loop demand from the server, I checked the console and it gave the following error: ERROR TypeError: ctx.amindiDGES is undefined.
I get this error on a couple of lines in my main.component.html:
MainComponent_Template main.component.html:8
getLocation main.component.ts:39
ngOnInit main.component.ts:27
This is what my Service looks like today.service.ts
import { Injectable } from '@angular/core';
import { HttpClient, HttpParams } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError, retry } from 'rxjs/operators';
@Injectable({
providedIn: 'root'
})
export class TodayService {
url = 'http://api.openweathermap.org/data/2.5/weather';
apiKey = '***********************';
constructor(private http: HttpClient) { }
daitriecoordinatebi(lat, lon) {
let params = new HttpParams()
.set('lat', lat)
.set('lon', lon)
.set('units', 'metric')
.set('appid', this.apiKey)
return this.http.get(this.url, { params });
This is my main.component.ts
import { Component, OnInit } from '@angular/core';
import { TodayService } from './today.service';
@Component({
selector: 'app-main',
templateUrl: './main.component.html',
styleUrls: ['./main.component.scss']
})
export class MainComponent implements OnInit {
lat;
lon;
amindiDGES;
kvirisdgeToday;
ikonkaToday;
title = 'Day1';
today = new Date();
constructor(private todayService: TodayService) { }
ngOnInit(): void {
// lokacia
this.getLocation();
// zusti saati
this.today = new Date();
}
getLocation() {
if ("geolocation" in navigator) {
navigator.geolocation.watchPosition((success) => {
this.lat = success.coords.latitude;
this.lon = success.coords.longitude;
this.todayService.daitriecoordinatebi(this.lat, this.lon).subscribe(data => {
this.amindiDGES = data;
})
})
}
}
}
And this is part of my main.component.html
<table>
<tbody>
<tr>
<td><i class="fas fa-wind"></i></td>
<td> Wind - {{amindiDGES.wind.speed}}</td>
</tr>
<tr>
<td><i class="far fa-eye"></i></td>
<td> Visibility - {{amindiDGES.visibility}}</td>
</tr>
<tr>
<td><i class="fas fa-tachometer-alt"></i></td>
<td> Preassure - {{amindiDGES.main.pressure}}</td>
</tr>
<tr>
<td><i class="fas fa-tint"></i></td>
<td> Humidity - {{amindiDGES.main.humidity}}</td>
</tr>
</tbody>
</table>
The funny thing in all of this is that I get data from the server and I do see results, but somehow the app is blocked after a couple of use due to too many demands, allowed amount is 60 per minute and my app demands 800+ requests per minute, apart from it there is a constant error in console: ERROR TypeError: ctx.amindiDGES is undefined
My guess is that somehow the app may try to display data before it is fetched via server, it gives error in the console and after that, it makes multiple requests over and over again until API is blocked.
I was wondering if you had such a problem with data fetching
Well, the below function has some serious issues:
getLocation() {
if ("geolocation" in navigator) {
navigator.geolocation.watchPosition((success) => {
this.lat = success.coords.latitude;
this.lon = success.coords.longitude;
this.todayService.daitriecoordinatebi(this.lat, this.lon).subscribe(data => {
this.amindiDGES = data;
})
})
}
}
As the document states :
The Geolocation method watchPosition() method is used to register a handler function that will be called automatically each time the position of the device changes.
So your code will subscribe daitriecoordinatebi
observable every time the location changed. So if the location is changed 3 times you will have 3 subscriptions. So you will call the API 3 times...
You have many options to resolve this.
- Use
toPromise
and then await the result
async (success) => {
this.amindiDGES = await
this.todayService.daitriecoordinatebi(this.lat, this.lon).toPromise();
// or you can use
await lastValueFrom(this.todayService.daitriecoordinatebi(this.lat, this.lon))
// since toPromise will be deprecated
})
- use
first
operator within pipe to make the subscription destroyed automatically
this.todayService.daitriecoordinatebi(this.lat, this.lon).pipe(first()).subscribe...
// or you can use take(1)
- You can use a
Subject
and amergeMap
operator to make it more elegant
positionChanged = new Subject();
getLocation() {
if ("geolocation" in navigator) {
navigator.geolocation.watchPosition((success) => {
this.lat = success.coords.latitude;
this.lon = success.coords.longitude;
this.positionChanged.next();
})
}
this.positionChanged.pipe(
mergeMap(()=>this.todayService.daitriecoordinatebi(this.lat, this.lon)))
.subscribe(data => {
this.amindiDGES = data;
})
}