What is the proper way of reading .json data from a RestAPI GET call into either an interface, class,...etc in Angular?

47 Views Asked by At

Currently learning Angular and API calls by writing a Weather app using WeatherAPI (https://app.swaggerhub.com/apis-docs/WeatherAPI.com/WeatherAPI/1.0.2-oas3-oas3.1-oas3.1/#/)

I am writing a Dashboard component that displays the location, temperature, and weather conditions (clear, cloudy,...etc.). as well as change the associated div's background between a "night" image and a "day" image. While I managed the former, it seems that the image URL isn't being loaded, so the component's background is in default white.

I wrote a DashboardService that calls the API:

//dashboard.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { catchError, retry } from 'rxjs';

import { API } from '../const';
import { ApiCurrentReturnModel } from '../schema';

@Injectable({
  providedIn: 'root'
})
export class DashboardService {
  
  constructor(private http: HttpClient) { }

  /**
   * 
   * @param currLocation the name of the area to be queried for
   * @returns current weather data of location specified
   */
  getCurrent(currLocation : string) {
    const url = API.URL.replace("{ call }", API.CURRENT)
                       .replace("{ q }", currLocation.replaceAll(" ", "%20"))
                       .replace("{ key }", API.KEY);
    return this.http.get(url);
  }
}

which returns the following (example) .json from WeatherAPI

{
  "location": {
    "name": "New York",
    "region": "New York",
    "country": "United States of America",
    "lat": 40.71,
    "lon": -74.01,
    "tz_id": "America/New_York",
    "localtime_epoch": 1658522976,
    "localtime": "2022-07-22 16:49"
  },
  "current": {
    "last_updated_epoch": 1658522700,
    "last_updated": "2022-07-22 16:45",
    "temp_c": 34.4,
    "temp_f": 93.9,
    "is_day": 1,
    "condition": {
      "text": "Partly cloudy",
      "icon": "//cdn.weatherapi.com/weather/64x64/day/116.png",
      "code": 1003
    },
    "wind_mph": 16.1,
    "wind_kph": 25.9,
    "wind_degree": 180,
    "wind_dir": "S",
    "pressure_mb": 1011,
    "pressure_in": 29.85,
    "precip_mm": 0,
    "precip_in": 0,
    "humidity": 31,
    "cloud": 75,
    "feelslike_c": 37,
    "feelslike_f": 98.6,
    "vis_km": 16,
    "vis_miles": 9,
    "uv": 8,
    "gust_mph": 11.6,
    "gust_kph": 18.7,
    "air_quality": {
      "co": 293.70001220703125,
      "no2": 18.5,
      "o3": 234.60000610351562,
      "so2": 12,
      "pm2_5": 13.600000381469727,
      "pm10": 15,
      "us-epa-index": 1,
      "gb-defra-index": 2
    }
  }
}

My current plan is to utilze the is_day value in the .json to change the link to the background image. Said link is stored in a variable called currWeatherBackgroundUrl. This is done in the getCurrentWeatherBackground() function in dashboard.component.ts

//dashboard.component.ts

import { Component, OnInit } from '@angular/core';

import { DashboardService } from '../service/dashboard.service';
import { ApiCurrent, ApiCurrentReturnModel, ApiLocation } from '../schema';

@Component({
  selector: 'app-dashboard',
  templateUrl: './dashboard.component.html',
  styleUrls: ['./dashboard.component.css', '../app.component.css']
})
export class DashboardComponent implements OnInit{
  private currentLocation: string = 'Hanoi';

  private apiResult!: ApiCurrentReturnModel; //interface for reading .json data
  private apiCurrent!: ApiCurrent; //child of ApiCurrentReturnModel
  private apiLocation!: ApiLocation; //child of ApiCurrentReturnModel

  // AN ATTEMPT AT USING A TERNARY OPERATION TO DETERMINE WHICH IMAGE TO USE AS THE DIV'S BACKGROUND
  //private currWeatherBackgroundUrl: string = this.currentData.current.is_day == 1 ? '../assets/bg/daytime-image.jpg' : '../assets/bg/night-image.jpg';

  private currWeatherBackgroundUrl = ''; //link init as empty

  constructor(private dashboardService: DashboardService) {
    //Same ternary operation attempt but in constructor instead.
    //this.currWeatherBackgroundUrl = this.currentData.current.is_day == 1 ? '../assets/bg/daytime-image.jpg' : '../assets/bg/night-image.jpg';
  }

  //ngOnInit life cycle hook
  ngOnInit(): void {
    this.loadData(); //read data from json
    this.getCurrentWeatherBackground(); //choose background image
    window.alert("ngOnInit"); //debug alert
    
  }

  loadData() {
    this.dashboardService.getCurrent(this.currentLocation).subscribe( rs => {
      let result = rs as ApiCurrentReturnModel;
      this.apiResult = result;
      this.apiLocation = result.location;
      this.apiCurrent = result.current;
    }
    );
  }

  getCurrentWeatherBackground() {
    if (this.currentData.current.is_day != 1) {
      //this.currWeatherBackgroundUrl = '../assets/bg/night-image.jpg';
      this.setCurrWeatherBackgroundUrl('../assets/bg/night-image.jpg');
      window.alert("night");
    }
    else {
      //this.currWeatherBackgroundUrl = '../assets/bg/daytime-image.jpg';
      this.setCurrWeatherBackgroundUrl('../assets/bg/daytime-image.jpg');
      window.alert("day");
    }
    
    window.alert("getCurrWeatherBg");
  }

  get currentData() {
    return this.apiResult;
  }

  getCurrWeatherBackgroundUrl() {
    return this.currWeatherBackgroundUrl;
  }

  setCurrWeatherBackgroundUrl(url: string) {
    this.currWeatherBackgroundUrl = url;
  }
}

However, ngOnInit() is unable to execute getCurrentWeatherBackground() as indicated by the debug alert not being executed. The same can be said of the alerts within getCurrentWeatherBackgroundUrl(). As a result, the initialized variable to hold the URL remains empty.

Additionally, ApiCurrent, ApiLocation, and ApiCurrentReturnModel are interfaces that stores the .json data as follows:

export interface ApiLocation {
    name: string
    region: string
    country: string
    lat: number
    lon: number
    tz_id: string 
    localtime_epoch: number
    localtime: string
}

export interface ApiCurrent {
    last_updated_epoch: number
    last_updated: string
    temp_c: number
    temp_f: number
    is_day: number
    condition: {
        text: string
        icon: string
        code: number
    }
    wind_mph: number
    wind_kph: number
    wind_degree: number
    wind_dir: string
    pressure_mb: number
    pressure_in: number
    precip_mm: number
    precip_in: number
    humidity: number
    cloud: number
    feelslike_c: number
    feelslike_f: number
    vis_km: number
    vis_miles: number
    uv: number
    gust_mph: number
    gust_kph: number
    air_quality: {
        co: number
        no2: number
        o3: number
        so2: number
        pm2_5: number
        pm10: number
        us_epa_index: number
        gb_defra_index: number
        
    }    
}

export interface ApiCurrentReturnModel {
    location: any
    current: any
}

I have verified that upon manually setting the URL in currWeatherBackgroundUrl to either of the links used getCurrentWeatherBackground() properly sets the background image of the div.

I've tried to use ternary operations to determine which image to use as indivated by the commented-out expressions in dashboard.component.ts.

More notably, upon removal of the getCurrentWeatherBackground() function from ngOnInit(), both loadData() and the alert is executed. Meaning the issue must be within getCurrentWeatherBackground(). I suspect it has something to with how I'm reading the .json data, as the is_day in said .json is typed as an int32. All I'm really doing in regards of reading the .json data is within loadData().

Why is it that the currWeatherBackgroundUrl isn't being changed? Why are the if-statements in getCurrentWeatherBackground() not triggering?

Sorry for the long post! I really appreciate any help and you staying with me throughout this process!

0

There are 0 best solutions below