Hello i am trying to implement behaviour subject to change data in datatable component based on which button is clicked in home component and cant figure it out its really confusing.
So i have service function which gets data from api with two parameters and with click on a button in home component i want to change the parameters of the service so the datatable component info update with the new parameters this is service
export class WeatherService {
constructor(private http: HttpClient) {}
getWeatherData(
lat: string = '51.51', //these are default parameters for london
lon: string = '-0.13' //i want to change these with click from home component
): Observable<WeatherData> {
return this.http.get<WeatherData>(
`https://archive-api.open-meteo.com/v1/era5?latitude=${lat}&longitude=${lon}&start_date=2005-08-25&end_date=2005-08-25&hourly=temperature_2m,relativehumidity_2m,dewpoint_2m,apparent_temperature,surface_pressure,precipitation,rain,cloudcover,windspeed_10m,winddirection_10m,soil_temperature_0_to_7cm`
);
}
}
the service is used here with the default service parameters but want to change the parameters based on the City button clicked on home page
ngOnInit(): void {
this.weatherDataLoading = true;
this.weatherService
.getWeatherData() //service is used here with default parameters (lat,long for london)
.pipe(
finalize(() => (this.weatherDataLoading = false)),
takeUntil(this.componentDestroyed$)
)
.subscribe({
next: (historicalWeatherData) => { ...code
this is home component with the buttons and each route to weather data component and each should pass new parameters (lat,long) to the service when clicked
<div >
<div >
<button pButton type="button" routerLink="/weather-data">London</button>
<button pButton type="button" routerLink="/weather-data">New York</button>
<button pButton type="button" routerLink="/weather-data">Tokyo</button>
<button pButton type="button" routerLink="/weather-data">Sydney</button>
<button pButton type="button" routerLink="/weather-data">Madrid</button>
<button pButton type="button" routerLink="/weather-data">Paris</button>
</div>
so when i click on Tokyo for example in home button it redirects me to the weather datatable component and change the lat and long in the service so accurate data is shown for Tokyo and not the default lat and long parameters for London
CodePudding user response:
The solution involves
- A
BehaviorSubject
which emits the coordinates of the currently selected location (ornull
if there isn't one). Initial value isnull
- A subscription to the output of this subject which then triggers the
weatherService.getWeatherData
request. This subscription lives inside theweather-data.component.ts
- Click events (buttons) originating from your
home.component
which emit coordinate values from yourBehaviorSubject
when clicked
weather.model.ts
export interface ICoordinates {
lat: number;
lon: number;
}
weather.service.ts
coordinatesSubject: BehaviourSubject<ICoordinates | null> = new BehaviorSubject<ICoordinates | null>(null);
getWeatherData$(
lat: string = '51.51', //these are default parameters for london
lon: string = '-0.13' //i want to change these with click from home component
): Observable<WeatherData> {
return this.http.get<WeatherData>(
`https://archive-api.open-meteo.com/v1/era5?latitude=${lat}&longitude=${lon}&start_date=2005-08-25&end_date=2005-08-25&hourly=temperature_2m,relativehumidity_2m,dewpoint_2m,apparent_temperature,surface_pressure,precipitation,rain,cloudcover,windspeed_10m,winddirection_10m,soil_temperature_0_to_7cm`
);
}
weather-data.component.ts
ngOnInit(): void {
this.watchForCoordinatesChanges();
}
watchForCoordinatesChanges(): void {
this.weatherService.coordinatesSubject
.asObservable()
.subscribe({
next: (coordinates: Coordinates | null) => {
this.getWeatherData(coordinates)
}
})
}
getWeatherData(coordinates: Coordinates | null): void {
const weatherData$: Observable<WeatherData> =
coordinates
? this.weatherService.getWeatherData$(coordinates.lat, coordinates.long)
: this.weatherService.getWeatherData$();
const weatherDataWithLoader$: Observable<WeatherData> =
defer(
() => {
this.weatherDataLoading = true;
return weatherData$
.pipe(
finalize(() => { this.weatherDataLoading = false }),
takeUntil(this.componentDestroyed$)
)
}
)
weatherDataWithLoader$
.subscribe({
next: (historicalWeatherData: WeatherData) => { // ...code }
})
}
home.component.html
<div >
<div >
<button pButton type="button" routerLink="/weather-data" (click)="weatherService.coordinatesSubject.next({lat: 123, lon: 123})">London</button>
<button pButton type="button" routerLink="/weather-data" (click)="weatherService.coordinatesSubject.next({lat: 456, lon: 456})">New York</button>
...
</div>
</div>
Depending on your use case and what you are doing with the data, this could be improved further