Building A Weather App Using VueJS
Last Updated :
16 May, 2024
Improve
We will explore how to create a weather application using Vue.js. We'll cover an approach to fetching weather data, displaying current weather conditions, and forecasting. By the end of this guide, you'll have a fully functional weather application that displays current weather information and forecasts for any location.
Prerequisites
- Basic HTML, CSS, and JavaScript
- Familiarity with Vue.js.
- Node.js installed.
Approach to build a Weather App
- Set up a Vue.js project, install dependencies, and create reusable Single File Components (SFC).
- Use Axios to fetch weather data from OpenWeatherMap API, including current weather and forecasts.
- Display fetched data in components, showing current weather conditions, including temperature, description, and icon.
- Format and display hourly and daily forecasts for the upcoming hours and days, respectively.
- Allow user input for city selection, trigger data fetching, and apply CSS styles for visual appeal.
Setup Vue.js Project
Step 1: Install Vue CLI globally if you haven't already.
npm install -g @vue/cli
Step 2: navigate to root directory Then, create a new Vue.js project:
vue create client
Step 3: navigate to client folder
cd client
Project Structure:
Step 4: Install required dependencies
npm i axios
updated dependencies looks like
"dependencies": {
"axios": "^1.6.8",
"core-js": "^3.8.3",
"vue": "^3.2.13"
},
Step 5: Manage files and folders
- Replace the code of App.vue with give code below
- Create styles.css insider src folder
Example: This example show the creation of the weather app.
/* styles.css */
@import url(
'https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap');
@import url(
'https://fonts.googleapis.com/css2?family=Orbitron:wght@300;700&display=swap');
/* General Styles */
html, body {
height: 100%;
margin: 0;
padding: 0;
}
body {
font-family: 'Roboto', sans-serif;
background-color: #f8fafa;
color: #333;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 2rem;
}
/* Header */
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 2rem;
}
.header h1 {
font-family: 'Orbitron', sans-serif;
font-size: 2.5rem;
color: #2b6cb0;
margin: 0;
}
/* Search Bar */
.search-bar {
display: flex;
align-items: center;
margin-bottom: 2rem;
}
.search-input {
font-size: 1rem;
padding: 0.5rem;
border: 1px solid #ccc;
border-radius: 0.25rem;
background-color: #f5f5f5;
width: 60%;
margin-right: 1rem;
}
.search-button {
font-size: 1rem;
background-color: #4a90e2;
color: white;
border: none;
border-radius: 0.25rem;
padding: 0.5rem 1rem;
cursor: pointer;
transition: background-color 0.3s ease;
}
.search-button:hover {
background-color: #357dbf;
}
/* Weather Widget */
.weather {
text-align: center;
color: #fff;
margin-bottom: 3rem;
padding: 2rem;
background-color: #4caf50;
border-radius: 1rem;
box-shadow: 0px 0px 15px 0px rgba(0, 0, 0, 0.1);
}
.weather h2 {
font-size: 2rem;
margin-bottom: 1rem;
}
.weather-icon {
width: 120px;
height: 120px;
border-radius: 50%;
background-color: rgba(240, 248, 255, 0.5);
}
.temperature {
font-size: 3rem;
margin-top: 1rem;
}
.clouds {
display: inline-block;
background-color: #f5f5f5;
padding: 0.5rem 1rem;
border-radius: 0.5rem;
font-size: 1.2rem;
color: #4caf50;
}
/* Forecast */
.forecast {
margin-bottom: 3rem;
}
.cast-header {
font-size: 1.5rem;
color: #333;
background-color: #ffc107;
padding: 0.5rem 1rem;
border-radius: 1rem;
margin-bottom: 1rem;
}
.divider {
height: 10px;
background-color: #ffc107;
border-radius: 1rem;
margin-bottom: 2rem;
}
.forecast-list {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
.next {
width: calc(50% - 1rem);
padding: 1.5rem;
border-radius: 1rem;
background-color: #fff;
box-shadow: 0px 0px 15px 0px rgba(0, 0, 0, 0.1);
margin-bottom: 1rem;
transition: transform 0.3s ease;
}
.next:hover {
transform: translateY(-5px);
}
.time {
font-size: 1.2rem;
color: #4a90e2;
margin-bottom: 0.5rem;
}
.temp-max,
.temp-min {
font-size: 1.2rem;
margin-bottom: 0.5rem;
}
.desc {
color: #555;
}
/* Responsive Styles */
@media screen and (max-width: 768px) {
.header {
flex-direction: column;
align-items: stretch;
}
.search-bar {
flex-direction: column;
}
.search-input,
.search-button {
width: 100%;
border-radius: 0.5rem;
}
.search-input {
margin-right: 0;
margin-bottom: 1rem;
}
.weather-icon {
width: 100px;
height: 100px;
}
.temperature {
font-size: 2.5rem;
}
.cast-header {
font-size: 1.2rem;
}
.divider {
height: 7px;
}
.next {
width: 100%;
}
}
<!-- App.vue -->
<template>
<div class="container">
<div class="header">
<h1>WEATHER APP</h1>
<div class="search-bar">
<input
type="text"
v-model="city"
placeholder="Enter city name"
class="search-input"
/>
<button @click="searchByCity"
class="search-button">Search</button>
</div>
</div>
<main class="main-section">
<div v-if="weatherData" class="weather">
<h2>{{ weatherData.name }},
{{ weatherData.sys.country }}</h2>
<div class="temp-box">
<img :src="iconUrl" alt="Weather Icon"
class="weather-icon" />
<p class="temperature">{{ temperature }} °C</p>
</div>
<span class="clouds">{{ weatherData.weather[0].description }}</span>
</div>
<div v-else class="loading">Loading...</div>
<div class="divider"></div>
<div class="forecast">
<div class="cast-header">Upcoming forecast</div>
<div class="forecast-list">
<div
class="next"
v-for="(forecast, index) in hourlyForecast"
:key="index"
>
<div>
<p class="time">{{ forecast.time }}</p>
<p class="temp-max">{{ forecast.temp_max }} °C</p>
<p class="temp-min">{{ forecast.temp_min }} °C</p>
</div>
<p class="desc">{{ forecast.description }}</p>
</div>
</div>
</div>
</main>
<div v-if="dailyForecast.length" class="forecast">
<div class="cast-header">Next 4 days forecast</div>
<div class="forecast-list">
<div
class="day"
v-for="(forecast, index) in dailyForecast"
:key="index"
>
<p class="date">{{ forecast.date }}</p>
<p class="temp-max">{{ forecast.temp_max }} °C</p>
<p class="temp-min">{{ forecast.temp_min }} °C</p>
<p class="desc">{{ forecast.description }}</p>
</div>
</div>
</div>
</div>
</template>
<script>
import axios from "axios";
const apikey = "feff206daa60b539abe8fae8f2ab7f29";
export default {
name: "App",
data() {
return {
city: "",
weatherData: null,
hourlyForecast: [],
dailyForecast: [],
};
},
computed: {
temperature() {
return this.weatherData
? Math.floor(this.weatherData.main.temp - 273)
: null;
},
iconUrl() {
return this.weatherData
?
`http://api.openweathermap.org/img/w/${this.weatherData.weather[0].icon}.png`
: null;
},
},
mounted() {
this.fetchCurrentLocationWeather();
},
methods: {
async fetchCurrentLocationWeather() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(async (position) => {
const { latitude, longitude } = position.coords;
const url = `ht
tp://api.openweathermap.org/data/2.5/weather?lat=${latitude}&lon=${longitude}&appid=${apikey}`;
await this.fetchWeatherData(url);
});
}
},
async fetchWeatherData(url) {
try {
const response = await axios.get(url);
this.weatherData = response.data;
// Fetch forecast data
await this.fetchForecast(this.weatherData.name);
} catch (error) {
console.error("Error fetching weather data:", error);
}
},
async fetchForecast(city) {
const urlcast =
`http://api.openweathermap.org/data/2.5/forecast?q=${city}&appid=${apikey}`;
try {
const response = await axios.get(urlcast);
const forecast = response.data;
this.hourForecast(forecast);
this.dayForecast(forecast);
} catch (error) {
console.error("Error fetching forecast data:", error);
}
},
async searchByCity() {
if (!this.city) return;
try {
const urlsearch =
`http://api.openweathermap.org/data/2.5/weather?q=${this.city}&appid=${apikey}`;
const response = await axios.get(urlsearch);
this.weatherData = response.data;
// Fetch forecast data
await this.fetchForecast(this.weatherData.name);
} catch (error) {
console.error("Error fetching weather data:", error);
}
this.city = "";
},
hourForecast(forecast) {
this.hourlyForecast = [];
for (let i = 0; i < 5; i++) {
const date = new Date(forecast.list[i].dt * 1000);
this.hourlyForecast.push({
time: date
.toLocaleTimeString(undefined, "Asia/Kolkata")
.replace(":00", ""),
temp_max: Math.floor(forecast.list[i].main.temp_max - 273),
temp_min: Math.floor(forecast.list[i].main.temp_min - 273),
description: forecast.list[i].weather[0].description,
});
}
},
dayForecast(forecast) {
this.dailyForecast = [];
for (let i = 8; i < forecast.list.length; i += 8) {
const date = new Date(forecast.list[i].dt * 1000);
this.dailyForecast.push({
date: date.toDateString(undefined, "Asia/Kolkata"),
temp_max: Math.floor(forecast.list[i].main.temp_max - 273),
temp_min: Math.floor(forecast.list[i].main.temp_min - 273),
description: forecast.list[i].weather[0].description,
});
}
},
},
};
</script>
Step 6: Start the Application
# Inside client directory
npm run serve
Output: