Home > Mobile >  How to use behavioursubject to change parameters in service function
How to use behavioursubject to change parameters in service function

Time:12-13

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 (or null if there isn't one). Initial value is null
  • A subscription to the output of this subject which then triggers the weatherService.getWeatherData request. This subscription lives inside the weather-data.component.ts
  • Click events (buttons) originating from your home.component which emit coordinate values from your BehaviorSubject 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

  • Related