[Fixed] HackerRank Angular Basic Test Weather App

Issue

I am taking the HackerRank Angular Basic Skills Test and trying to make a weather app

I have a weatherDetails.component with the following code

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

    @Component({
      selector: "weather-details",
      templateUrl: "./weatherDetails.component.html",
      styleUrls: ["./weatherDetails.component.scss"],
    })
    export class WeatherDetails implements OnInit {
      @Input() weatherData: data[];
    
      city: string = "";
    
      ngOnInit() {}
    }
    
    interface data {
      name: string;
      temperature: string;
      wind: string;
      humidity: string;
    }

The child component inherits weatherData from its parent

weatherData = [
    {
      name: "San Jose",
      temperature: "36º F",
      wind: "31Kmph",
      humidity: "66%",
    },
    {
      name: "Seattle",
      temperature: "30º F",
      wind: "4Kmph",
      humidity: "9%",
    },
    {
      name: "New York",
      temperature: "20º F",
      wind: "8Kmph",
      humidity: "61%",
    },
    {
      name: "Chicago",
      temperature: "27º F",
      wind: "35Kmph",
      humidity: "2%",
    },
    {
      name: "Atlanta",
      temperature: "22º F",
      wind: "25Kmph",
      humidity: "5%",
    },
    {
      name: "Austin",
      temperature: "25º F",
      wind: "1Kmph",
      humidity: "5%",
    },
    {
      name: "Denver",
      temperature: "30º F",
      wind: "8Kmph",
      humidity: "48%",
    },
  ];

I am rendering the child component via weatherDetails.component.html with classes removed for better readability

<div>

  <section>
    <label>Enter City: </label>
    <input [(ngModel)]="city" type="text" placeholder="Seattle" data-test-id="app-input"/>
  </section>

  <section *ngFor="let weather of weatherData">
    <div *ngIf="city.toUpperCase() === weather.name.toUpperCase()">
        <div *ngIf="city" data-test-id="weather-details">
            <div>
              <button>
                <i>wb_sunny</i>
              </button>
              <span data-test-id="output-temperature">{{weather.temperature}}</span>
            </div>
            <div>
              <div data-test-id="output-wind">
                Wind: {{weather.wind}}
              </div>
              <div data-test-id="output-humidity">
                Humidity: {{weather.humidity}}
              </div>
            </div>
        </div>
        <div *ngIf="city.toUpperCase() !== weather.name.toUpperCase()" data-test-id="no-results">No Results Found</div>
    </div>
  </section>
</div>

My approach was to use two-way binding via [(ngModel)]="city" in input and store the city variable in my component.

As I loop through the array of objects in weatherData via

*ngFor="let weather of weatherData"

every time the input value is changed, I would use

*ngIf="city.toUpperCase() === weather.name.toUpperCase()" 

to check if the input value matches the weather of weatherData object and render the div if it does.

My problem is that the div that outputs "No Results Found" when the input value does not match any object in weather of weatherData is a child of the div that renders the app. So I can’t render "No Results Found" because its

*ngIf="city.toUpperCase() !== weather.name.toUpperCase()"

logic clashes with its parent.

How do I rewrite my code so that I can render "No Results Found" when the input value does not match any object in weather of weatherData?

My weather app with input of ‘S’ (should render "No Results Found" but is blank) weather app

My weather app with input of ‘Seattle" weather app

Solution

This will work.
Here I have moved the logic to the component file instead of the HTML file to check if the city exists or not. Also, a button to validate the value on click of it.
so I took a function "validatedata()" to validate the city entered and to return the result.
mainly to display the invalid city. Take a variable "invaliddata" of type boolean to display HTML based on its true or false value.
Weatherdetails.component.html

   <section>
  <label>Enter City: </label>
  <input [(ngModel)]="city" type="text" placeholder="Seattle" id="app-input" />
</section>
<button (click)="validatedata()">Get Weather</button>

<section >
  <div *ngIf ="!invaliddata">
    <h2>The current weather in {{this.enteredData.name}} is </h2>
    <div id="output-temperature">
      Temperature : {{this.enteredData.temperature}}
    </div>
    <div id="output-wind">
      Wind: {{this.enteredData.wind}}
    </div>
    <div id="output-humidity">
      Humidity: {{this.enteredData.humidity}}
    </div>
  </div>
  <div *ngIf ="invaliddata">
    <h3>Please Enter valid city</h3>
  </div>
</section>



*WeatherdetailsComponent.ts*

    import { Component, Input, OnInit } from '@angular/core';
import { resetFakeAsyncZone } from '@angular/core/testing';
import { WeatherdataService } from './../weatherdata.service';

@Component({
  selector: 'app-weatherdetails',
  templateUrl: './weatherdetails.component.html',
  styleUrls: ['./weatherdetails.component.css'],
  providers: [WeatherdataService]

})
export class WeatherdetailsComponent {
  getdata: any[];
  city: string = "";
  enteredData: any[];
  invaliddata: boolean;
  constructor(public weatherData: WeatherdataService) { }


  ngOnInit(): void {
    this.getdata = this.weatherData.getdata();


  }
  public validatedata() {
    if (this.city) {
      for (var i = 0; i <= this.getdata.length; i++) {
        if (((this.getdata[i].name).toUpperCase()) == ((this.city).toUpperCase())) {
          this.enteredData = this.getdata[i];
          console.log(this.enteredData);
          this.invaliddata = false;
          break;
        }
        else {
          this.invaliddata = true;
        }
      }
    }
    else {
      alert("Please enter a City Name");

    }
  }
}

interface data {
  name: string;
  temperature: string;
  wind: string;
  humidity: string;
}


*weatherdata.Service.ts*
 
import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class WeatherdataService {

  getdata(){
    return[
        {
            name: "San Jose",
            temperature: "36º F",
            wind: "31Kmph",
            humidity: "66%",
          },
          {
            name: "Seattle",
            temperature: "30º F",
            wind: "4Kmph",
            humidity: "9%",
          },
          {
            name: "New York",
            temperature: "20º F",
            wind: "8Kmph",
            humidity: "61%",
          },
          {
            name: "Chicago",
            temperature: "27º F",
            wind: "35Kmph",
            humidity: "2%",
          },
          {
            name: "Atlanta",
            temperature: "22º F",
            wind: "25Kmph",
            humidity: "5%",
          },
          {
            name: "Austin",
            temperature: "25º F",
            wind: "1Kmph",
            humidity: "5%",
          },
          {
            name: "Denver",
            temperature: "30º F",
            wind: "8Kmph",
            humidity: "48%",
          },
    ]
}}

The result is as shown below.
enter image description here

valid output result looks as below:
enter image description here

Note: Kindly note that I have not worked on CSS part

Leave a Reply

(*) Required, Your email will not be published