Search code examples
angulartypescriptcomponentsobservable

Update a Angular Component Template after API Response


i'm realative new to Angular, but i have some baggage from other JS Frameworks like Vue or React. Right now i'm creating an Angular Lab app in which i ask 2 APIs for information, and when this its delivered i try to refresh the child component i have included inside the parent one (child is called Main and father is called App).

My issue is, i don't really know how to update this child component template, and i'm not really finding useful information online, so its a bit of a struggle.

My execution flow: Father component has a textbox and a button --> when you write down a city and press de button 1st api is called, it delivers de coordinates of the 1st city ocurrence --> when its done, calls with thos coordinates to the second API that delivers the weather code for thats coordinates --> Those weather codes get printed in the child component.

To handle the button click i've created a Directive, this directive uses a Service to storage the values.

PD= I'm using Angular 14

Here is some code:

Directive


@Directive({
  selector: '<button [showWeather]>'
})
export class ShowWeatherDirective {

  constructor(private sharedService: SharingService,
    private askApi: ServiceAskAPIService,
     private askLocation: ServiceGetCityLocationAPIService,) { }

  currentWeather = this.sharedService.weatherResults$;


  @HostListener('click',['$event.target'])
  onClick(btn: any){
    console.log("se ha pulsado el botón", btn);
    this.getCityWeather();
  }


  getCityWeather(){
    var city =(document.getElementById("cityInput") as HTMLInputElement).value;
   this.askLocation.getCityLocation(city)
   .then(location=>{
    this.askApi.getCityWeather(location.toString()).then(answer=>{
      // after both apis have answered we process the data
      console.log(answer);
      console.log(answer.valueOf)
      let jsonparsed = JSON.parse(answer);
      let myWeeklyIntervals = jsonparsed.data.timelines[0].intervals
      const weatherCodes : WeatherCodes[] = [
        {
          id:0,
          weather:"",
          img:"",
        }
      ]

      for(let res of myWeeklyIntervals){
        let weathercode : WeatherCodes = {
          id: 0,
          weather:"",
          img:""
        }
        console.log(res);
       weathercode.id = res.values.weatherCode;
       weatherCodes.push(weathercode);
      }
        console.log(weatherCodes)
        this.sharedService.updateWeatherResults(weatherCodes);
   })
   });


  }
}


Sharing service

import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class SharingService {
  private sharingObject = new Subject<any[]>();
  weatherResults$ = this.sharingObject.asObservable();

  constructor() { }

  get sharingValue() {
    return this.sharingObject
  }

  set sharingValue(obj) {
    this.sharingObject = obj;
  }

  updateWeatherResults(results: any[]){
    this.sharingObject.next(results);
  }
}

Child component


@Component({
  selector: 'component-main',
  templateUrl: './main.component.html',
  styleUrls: ['./main.component.css']
})


export class MainComponent {

 private weatherResults =new BehaviorSubject<Object>(this.sharedService.weatherResults$);
 weatherResultObservable$: Observable<Object> = this.weatherResults.asObservable();

  weatherCodes: WeatherCodes[] = [
    { id: 0, weather: "Unknown", img: "" },
    { id: 1000, weather: "Clear, Sunny", img: "" },
    { id: 1100, weather: "Mostly Clear", img: "" },
    { id: 1101, weather: "Partly Cloudy", img: "" },
    { id: 1102, weather: "Mostly Cloudy", img: "" },
    { id: 1001, weather: "Cloudy", img: "" },
    { id: 2000, weather: "Fog", img: "" },
    { id: 2100, weather: "Light Fog", img: "" },
    { id: 4000, weather: "Drizzle", img: "" },
    { id: 4001, weather: "Rain", img: "" },
    { id: 4200, weather: "Light Rain", img: "" },
    { id: 4201, weather: "Heavy Rain", img: "" },
    { id: 5000, weather: "Snow", img: "" },
    { id: 5001, weather: "Flurries", img: "" },
    { id: 5100, weather: "Light Snow", img: "" },
    { id: 5101, weather: "Heavy Snow", img: "" },
    { id: 6000, weather: "Freezing Drizzle", img: "" },
    { id: 6001, weather: "Freezing Rain", img: "" },
    { id: 6200, weather: "Light Freezing Rain", img: "" },
    { id: 6201, weather: "Heavy Freezing Rain", img: "" },
    { id: 7000, weather: "Ice Pellets", img: "" },
    { id: 7101, weather: "Heavy Ice Pellets", img: "" },
    { id: 7102, weather: "Light Ice Pellets", img: "" },
    { id: 8000, weather: "Thunderstorm", img: "" }
  ]

  currentWeather: WeatherCodes[] = [];

  constructor(private sharedService: SharingService){}

}

Child component template

<p>main works!</p>
<div id="MyClass" *ngIf="weatherResultObservable$| async">
 <p> {{weatherResultObservable$}}</p>
</div>
<script>
  console.log("nope")
</script>

My most sincere apologize if you find anything ultrageously wrong, i'm still a learner. Thank you very much for you answers

I tried using observable as i think its mainly the resource i should be using, but i'm missing something in the "live-reload" part thats getting me really confused


Solution

  • i don't how i even managed to do it, but this is the answer to the issue i have, so i post iot in order to help anyone in the future that finds this:

    I modified my ChildComponent (main) to subscribe to the service that stores the data on its initialization (ngOnInit()):

    export class MainComponent implements OnInit{
    
      weatherResultObservable$: Observable<any[]> | undefined;
    
      currentWeather: WeatherCodes[] = [];
    
      constructor(private sharedService: SharingService){}
    
      ngOnInit(): void {
          this.weatherResultObservable$ = this.sharedService.weatherResults$;
          this.weatherResultObservable$.subscribe((results)=>{
            this.currentWeather=results;
          })
      }
    }
    

    After this, i only had to update my ChildComponent Template to fit the object properly: (Note that weather.weather references a Interface i did not share with you in this example because it was not needed)

    <div id="MyClass" *ngIf="weatherResultObservable$ | async">
      <p *ngFor="let weather of currentWeather">{{ weather.weather }}</p>
    </div>
    

    Output:

    enter image description here