- Tutorials / JavaScript
- Sunday, 3rd Jan, 2021
Home » Tutorials » JavaScript » Build A Weather App In Vue JS
In this article, we will show you how to build a simple weather app with API using Vue JS in less than 20 minutes.
We expect that you are familiar with the following:
Your key learnings from this project will be:
<!DOCTYPE> <html> <head> <style> body { font-family: Helvetical, sans-serif; line-height: 1.64; margin: 0; height: 100vh; /* 100% of the viewport height */ } #app { background-size: cover; height: 100%; } .freeze { background-image: url(https://image.freepik.com/free-photo/closeup-frozen-surface-during-winter_181624-22234.jpg); } .cozy { background-image: url(https://image.freepik.com/free-photo/beautiful-shot-mountains-trees-covered-snow-fog_181624-17590.jpg); } .warm { background-image: url(https://image.freepik.com/free-photo/beautiful-shot-dry-desert-hill-with-mountains_181624-1974.jpg); } .hot { background-image: url(https://image.freepik.com/free-photo/rising-sun-with-seascape_1357-293.jpg); } .content { background: rgba(0,0,0,.3); box-sizing: border-box; color: #fff; height: 100%; text-align: center; padding: 24px 12px 12px; } .search-box input { border: none; border-radius: 10px; box-sizing: border-box; box-shadow: 0 0 1px 6px rgba(255,255,255,0.3); text-align: center; padding: 8px; width: 100%; } .weather-wrap { margin: 12px; } .location { font-size: 22px; margin-bottom: 0; } .date { color: #ddd; font-style: italic; margin-top: -8px; } .temp { border-radius: 6px; background: rgba(255,255,255,.3); display: inline-block; font-size: 28px; font-weight: bold; margin-top: 12px; padding: 6px 12px; } .description { font-weight: bold; } .error-msg { margin-top: 12px; color: #cc0000; font-size: 22px; } </style> <script src="https://cdn.jsdelivr.net/npm/vue@2"></script> <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script> </head> <body> <div id="app" :class="bgImageObj"> <div class="content"> <h1>Today's Weather</h1> <div class="search-box"> <input type="text" placeholder="Search city. e.g. London" v-model="query" @keyup.enter="fetchWeather" /> </div><!-- END search-box --> <div class="weather-wrap" v-if="typeof weather.main != 'undefined'"> <div class="location-box"> <div class="location"> {{ weather.name }}, {{ retrieveCountry }} </div> <div class="date"> {{ todayDate }} </div> </div> <div class="weather-box"> <div class="temp"> {{ retrieveTemp }} ℃ </div> <div class="description">{{ retrieveDesc }}</div> </div> </div><!-- END weather-wrap --> <div class="error-msg" v-if="hasError"> {{ errorMessage }} </div> </div> </div> <script> var vm = new Vue({ el: '#app', data: function() { return { query: '', weather: {}, todayDate: '', hasError: false, errorMessage: '', } }, computed: { bgImageObj() { return { freeze: this.retrieveTemp < 10, cozy: this.retrieveTemp > 10 && this.retrieveTemp < 24, warm: this.retrieveTemp > 24 && this.retrieveTemp < 32, hot: this.retrieveTemp > 32 } }, retrieveDesc() { var obj = this.weather.weather[0]; for(const property in obj) { if(property == 'description') { return obj[property]; } } }, retrieveCountry() { var obj = this.weather.sys; for(const property in obj) { if(property == 'country') { return obj[property]; } } }, retrieveTemp() { var obj = this.weather.main; for(const property in obj) { if(property == 'temp') { return obj[property]; } } } }, created: function () { this.displayDate(); }, methods: { displayDate() { var dateObj = new Date(); var days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]; var day = days[dateObj.getDay()]; var date = dateObj.getDate(); var months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]; var month = months[dateObj.getMonth()]; var year = dateObj.getFullYear(); this.todayDate = `${day}, ${date} ${month} ${year}`; }, fetchWeather() { var _this = this; const options = { method: 'GET', url: 'https://community-open-weather-map.p.rapidapi.com/weather', params: { q: _this.query, lat: '0', lon: '0', id: '{ID}', lang: 'null', units: 'metric' }, headers: { 'x-rapidapi-key': '{API_KEY}', 'x-rapidapi-host': 'community-open-weather-map.p.rapidapi.com' } }; axios.request(options).then(function (response) { _this.weather = response.data; _this.hasError = false; }).catch(function (error) { _this.hasError = true; _this.errorMessage = "Not found, Please try other city."; }); } } }) </script> </body> </html>
The first thing you’ll want to do is to draw boxes around every component (or we call it section) in the mock and give them all names. Example:
You will see that we have 5 main section in the weather app:
Now that we’ve identified the hierarchy of the section, let’s translate it into a static HTML code:
<div id="app"> <div class="content"> <h1>Today's Weather</h1> <div class="search-box"> <input type="text" placeholder="Search city. e.g. London" /> </div><!-- END search-box --> <div class="weather-wrap"> <div class="location-box"> <div class="location"> Tokyo, JP </div> <div class="date"> Sunday, 3 January 2021 </div> </div> <div class="weather-box"> <div class="temp"> 27 ℃ </div> <div class="description">Cloudy</div> </div> </div><!-- END weather-wrap --> <div class="error-msg"> Not found, please try other city. </div> </div> </div>
Next, we need to apply some “make up” to the app, please copy following CSS code to your file.
We have defined 4 different classes (freeze, cozy, warm, hot) for switching the app’s background image when the temperature is in a certain range.
Background image sources are taken from freepik.com: Background photo created by Waewkidja – www.freepik.com, Cloud photo created by wirestock – www.freepik.com, Tree photo created by wirestock – www.freepik.com, Winter photo created by wirestock – www.freepik.com
body { font-family: Helvetical, sans-serif; line-height: 1.64; margin: 0; height: 100vh; /* 100% of the viewport height */ } #app { background-size: cover; height: 100%; } .freeze { background-image: url(https://image.freepik.com/free-photo/closeup-frozen-surface-during-winter_181624-22234.jpg); } .cozy { background-image: url(https://image.freepik.com/free-photo/beautiful-shot-mountains-trees-covered-snow-fog_181624-17590.jpg); } .warm { background-image: url(https://image.freepik.com/free-photo/beautiful-shot-dry-desert-hill-with-mountains_181624-1974.jpg); } .hot { background-image: url(https://image.freepik.com/free-photo/rising-sun-with-seascape_1357-293.jpg); } .content { background: rgba(0,0,0,.3); box-sizing: border-box; color: #fff; height: 100%; text-align: center; padding: 24px 12px 12px; } .search-box input { border: none; border-radius: 10px; box-sizing: border-box; box-shadow: 0 0 1px 6px rgba(255,255,255,0.3); text-align: center; padding: 8px; width: 100%; } .weather-wrap { margin: 12px; } .location { font-size: 22px; margin-bottom: 0; } .date { color: #ddd; font-style: italic; margin-top: -8px; } .temp { border-radius: 6px; background: rgba(255,255,255,.3); display: inline-block; font-size: 28px; font-weight: bold; margin-top: 12px; padding: 6px 12px; } .description { font-weight: bold; } .error-msg { margin-top: 12px; color: #cc0000; font-size: 22px; }
The easiest way to build a simple single page Vue JS application, you just need to include the Vue library into the HTML file.
<script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
Next, we need to setup 5 pieces of data in the Vue JS application:
var vm = new Vue({ el: '#app', data: function() { return { query: '', weather: {}, todayDate: '', hasError: false, errorMessage: '', } } })
Please heading to Open Weather Map at Rapid API – https://rapidapi.com/community/api/open-weather-map and get your API.
Install Axios HTTP Client tool on your app:
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
To learn more about Axios, please see here.
methods: { fetchWeather() { var _this = this; const options = { method: 'GET', url: 'https://community-open-weather-map.p.rapidapi.com/weather', params: { q: _this.query, lat: '0', lon: '0', id: '{{ID}}', lang: 'null', units: 'metric' }, headers: { 'x-rapidapi-key': '{{API_KEY}}', 'x-rapidapi-host': 'community-open-weather-map.p.rapidapi.com' } }; axios.request(options).then(function (response) { _this.weather = response.data; _this.hasError = false; }).catch(function (error) { _this.hasError = true; _this.errorMessage = "Not found, Please try other city."; }); } }
Create a fetchWeather() method under the Vue JS’s methods object and fetch the weather data using Axios HTTP Client.
If we successfully receives the data from the API, we store the result into this.weather object & set this.hasError boolean to false, otherwise we set this.hasError boolean to true & an error message to the this.errorMessage.
Note: Please console.log the result and identify the structure & info of the returned result before we proceed to step 2-2.
Now we have a complete set of weather data in the this.weather object, let’s extract the piece we need it for the weather app. Create 3 computed methods under computed object in Vue JS: retrieveDesc(), retrieveCountry(), retrieveTemp().
computed: { retrieveDesc() { var obj = this.weather.weather[0]; for(const property in obj) { if(property == 'description') { return obj[property]; } } }, retrieveCountry() { var obj = this.weather.sys; for(const property in obj) { if(property == 'country') { return obj[property]; } } }, retrieveTemp() { var obj = this.weather.main; for(const property in obj) { if(property == 'temp') { return obj[property]; } } } }
In our case, we need following info from the weather data:
Next, we want the app’s background image changed according to the temperature. Let’s create another computed methods under computed object and named it as bgImageObj().
computed: { bgImageObj() { return { freeze: this.retrieveTemp < 10, cozy: this.retrieveTemp > 10 && this.retrieveTemp < 24, warm: this.retrieveTemp > 24 && this.retrieveTemp < 32, hot: this.retrieveTemp > 32 } } }
The bgImageObj() returns a CSS classname in certain range of temperature: freeze, cozy, warm, hot.
We need to create a method displayDate() under Vue JS’s methods object for getting today’s date.
Then we call the displayDate() method when Vue JS application is created.
created: function () { this.displayDate(); }, methods: { displayDate() { var dateObj = new Date(); var days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]; var day = days[dateObj.getDay()]; var date = dateObj.getDate(); var months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]; var month = months[dateObj.getMonth()]; var year = dateObj.getFullYear(); this.todayDate = `${day}, ${date} ${month} ${year}`; }, } })
So we’ve prepared all the data we need in Step 2, next we need to identify where we want to host & display the data.
<div id="app" :class="bgImageObj"> <div class="content"> <h1>Today's Weather</h1> <div class="search-box"> <input type="text" placeholder="Search city. e.g. London" v-model="query" @keyup.enter="fetchWeather" /> </div><!-- END search-box --> <div class="weather-wrap" v-if="typeof weather.main != 'undefined'"> <div class="location-box"> <div class="location"> {{ weather.name }}, {{ retrieveCountry }} </div> <div class="date"> {{ todayDate }} </div> </div> <div class="weather-box"> <div class="temp"> {{ retrieveTemp }} ℃ </div> <div class="description">{{ retrieveDesc }}</div> </div> </div><!-- END weather-wrap --> <div class="error-msg" v-if="hasError"> {{ errorMessage }} </div> </div> </div>
As you seen above,
Hope you learned something from this article & it’s now time for you to deploy your application, and share it with your peers. 🙂