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)
My weather app with input of ‘Seattle"
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%",
},
]
}}
valid output result looks as below:
Note: Kindly note that I have not worked on CSS part