May 25, 2020

Simple Weather App using Angular and Animated Icons

Weather App with Angular

In this article, we are going to create a simple but beautiful and responsive weather app in Angular using the OpenWeatherMap Api and animated icons. The end result will be something like below but since a lot more data is exposed by the API you can customize it as per your requirement.

Weather App

Initial Setup

  • Free account on the Openweathermap portal for the API key.
  • The Angular CLI must be installed and configured to create a new app
  • Background image and animated svg (can be download from the github link mentioned in the end of this post)

Now that we have new angular app setup we will first generate a new service and use it to consume the API to fetch weather data based on city.

Weather Service

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

@Injectable()
export class WeatherService {
apikey = 'Your API key generated on the portal';
baseUrl = 'http://api.openweathermap.org/data/2.5/weather?q=';

constructor(private http: HttpClient) { }

getWeatherByCity(city: string): Observable<any> {
return this.http.get(this.baseUrl + city + '&appid=' + this.apikey);
}
}

 

  • Firstly, we have imported the required modules and created variables to to store the apiKey and the baseUrl.
  • We will use the simplest endpoint which returns the weather data based upon the city value passed onto the “q” parameter in the url
  • We then have a method getWeatherByCity which makes a GET request to the baseUrl

Weather-Data Interface

Create a new interface which we will used as a model of our weather data returned by the api.

export interface WeatherData {
    city?: string;
    temp: number;
    desc?: string;
    humidity: number;
    max: number;
    min: number;
    icon: number;
    iconName: string;
}

 

  • We will only show the city, current, max and min temperature along with humidity and description
  • The api also returns a static icon image and icon number but we wont be using these icons and will instead use the animated once

Weather Component

HTML & CSS

<div class="mainDiv">
    <div class="content">
        <div class="myForm">
            <form #form="ngForm">
                <input type="text" placeholder="City" name="cityName" #cityName="ngModel" [(ngModel)]="city">
                <button (click)="getWeather()">Get Weather</button>
            </form>
        </div>


        <div class="weather">
            <h1 class="city">{{this.weather.city}}</h1>
            <img [src]="'../../assets/animated/' + this.weather.iconName" alt="">
            <h2 class="temp">{{this.weather.temp}}&deg;</h2>
            <h3 class="maxmin">{{this.weather.max}}&deg; &nbsp; {{this.weather.min}}&deg;</h3>
            <h3 class="desc">{{this.weather.desc}}</h3>
            <h3 class="desc"> {{this.weather.humidity}}% Humidity</h3>
        </div>

    </div>

</div>

 

  • Here we just have a simple form with one input field and a button to get the city from user
  • We also have few heading tags to display the weather related content.
  • Make sure you specify the path of svg correctly. I have them inside animated folder in the assets

Also Read: Upload Files and Images using Angular

    .mainDiv {
        background: url(../../assets/background.jpg) center no-repeat;
        background-size: cover;
        position: relative;
        top: 0;
        left: 0;
        height: 100vh;
        width: 100%;
    }
    
    .content {
        display: flex;
        flex-direction: column;
        justify-content: space-evenly;
        padding-top: 5%;
        width: 50%;
        margin: 0 auto;
    }
    
    .weather {
        padding: 15px;
        font-family: 'Montserrat', sans-serif;
        color: white;
        z-index: 102;
        display: flex;
        justify-content: space-around;
        flex-direction: column;
        align-items: center;
        height: 50vh;
        margin-top: 10px;
    }
    
    img {
        width: 20%;
        height: auto;
    }
    
    .city {
        font-size: 3.5rem;
        text-transform: uppercase;
    }
    
    Form {
        width: inherit;
        display: flex;
        justify-content: space-evenly;
    }
    
    input {
        width: 250px;
        padding: 10px;
        outline: none;
        border: none;
        background: transparent;
        color: white;
        border: 2px solid #FDC830;
        font-size: 1.1rem;
        text-decoration: none;
    }
    
    button {
        z-index: 102;
        font-family: 'Montserrat', sans-serif;
        text-decoration: none;
        font-size: 1.3rem;
        font-weight: bolder;
        text-transform: uppercase;
        color: #c81912;
        letter-spacing: .5px;
        color: white;
        outline: none;
        border: none;
        font-size: 1rem;
        padding: 10px 12px;
        background: #FDC830;
        /* fallback for old browsers */
        background: -webkit-linear-gradient(to right, #F37335, #FDC830);
        /* Chrome 10-25, Safari 5.1-6 */
        background: linear-gradient(to right, #F37335, #FDC830);
        /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */
        transition: all 0.5 ease-in;
        box-shadow: rgba(0, 0, 0, 0.2);
    }
    
    button:hover {
        cursor: pointer;
        font-size: 1.1rem;
    }
    
    @media screen and (max-width: 540px) {
        img {
            width: 30%;
            height: auto;
        }
        .city {
            font-size: 1.6rem;
        }
    }

 

Weather Component ts

import { Component, OnInit } from '@angular/core';
import { WeatherService } from './../services/weather.service';
import { WeatherData } from './../weather-data';

@Component({
  selector: 'app-weather',
  templateUrl: './weather.component.html',
  styleUrls: ['./weather.component.css']
})
export class WeatherComponent implements OnInit {
city: string;
weather = {}  as WeatherData;
  constructor(private WeatherService: WeatherService) { }

  ngOnInit() {
    this.city = 'London';
    this.getWeather();
  }

  getWeather() {
    console.log(this.city);
    this.WeatherService.getWeatherByCity(this.city).subscribe((response) => {
      this.weather.desc = response.weather[0].main;
      this.weather.city = this.city;
      this.weather.humidity = response.main.humidity;
      this.weather.temp = Math.floor(response.main.temp - 273.15) ;
      this.weather.max =  Math.floor(response.main.temp_max - 273.15);
      this.weather.min = Math.floor(response.main.temp_min - 273.15) ;
      this.weather.icon = response.weather[0].id;
      this.getImageType(this.weather.icon);
      console.log(response.weather[0].id);
      console.log(this.weather);
      console.log(this.weather.icon);
}
);
  }

  getImageType(icon: number) {
    if (icon >= 200 && icon < 233) {
      this.weather.iconName = 'thunder.svg';
    }
    if (icon >= 300 && icon < 332) {
        this.weather.iconName = 'rainy-3.svg';
      }
    if (icon >= 500 && icon < 532) {
          this.weather.iconName = 'rainy-6.svg';
        }
    if (icon >= 600 && icon < 632) {
          this.weather.iconName = 'snowy-3.svg';
        }
    if (icon === 800) {
        this.weather.iconName = 'day.svg';
        }

    if (icon > 800) {
        this.weather.iconName = 'cloudy-day-3.svg';
        }
    if (icon >= 700 && icon < 800)  {
          this.weather.iconName = 'cloudy-day-2.svg';
          }
     }
}

 

  •  Import the Weather Service  and Weather Data interface in the ts file
  • Now create two variables, one to store the city entered by the user and other of type WeatherData
  • In the Init life cycle hook we will set the city to London and call the getWeather method so that our applications loads with some initial weather data
  • In the getWeather method we call our method from the service and then subscribe it to receive the data
  • We then map our interface fields to their respective field from the json returned
  • We will also convert the temperature from the default Kelvin scale

Getting the icon based on weather condition

The api returns an icon number which belongs to range of corresponding weather conditions of that area. Although the api returns different icon for each condition we have only few of them so we will generalize them categorically.

If you visit here,  you can check the grouping. For example any icon number in the 5XX range would be denoting different forms of rainfall.

Use the grouping on the portal and assign our svg’s name to the iconName field of the weather data interface for different weather conditions.

App Module ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import {HttpClientModule} from '@angular/common/http';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { WeatherComponent } from './weather/weather.component';
import { AngularFontAwesomeModule } from 'angular-font-awesome';
import {FormsModule} from '@angular/forms';
import { WeatherData } from './weather-data';

import { WeatherService } from './services/weather.service';
import { environment } from '../environments/environment';


@NgModule({
   declarations: [
      AppComponent,
      WeatherComponent

   ],
   imports: [
BrowserModule,
      AppRoutingModule,
      HttpClientModule,
      AngularFontAwesomeModule,
      FormsModule,
   ],
   providers: [WeatherService],
   bootstrap: [
      AppComponent
   ]
})
export class AppModule { }

 

That’s it, we just created a weather app in angular which can be further customized from the additional data shared by the api. Make sure to explore and create a much more informative screen.

Download the complete source code.

Leave a Reply

Your email address will not be published. Required fields are marked *