Dynamic Weather Cards in NuxtJS

Eugene Musebe

Introduction

When building a weather app with weather sharing capabilities, it is important we allow our users to easily share brief insightful images of the weather conditions. In this article, we explore how we can dynamically create these weather cards using Google Maps and OpenWeathers APIs`.

Codesandbox

Check the sandbox demo on Codesandbox.

You can also get the project GitHub repo using Github.

Nuxt.Js Setup & Configuration

We will be using Nuxt.Js for this tutorial. It is a versatile and performant Vue.Js framework. To get started ensure you have Yarn or NPM v5.2+/v6.1+ installed. Open the terminal in your preferred working directory and run the following command.

1yarn create nuxt-app nuxtjs-weather-cards
2# OR
3npx create-nuxt-app nuxtjs-weather-cards
4# OR
5npm init nuxt-app nuxtjs-weather-cards

This will result in a series of setup questions. Here are our recommended defaults.

Once the setup is complete you may enter and run your project. It will be accessible on localhost:3000

1cd nuxtjs-weather-cards
2
3yarn dev

Google Maps Setup

We will be using Google Maps APIs to retrieve the coordinates of the city as well as the static image map. Before we do this, we need a Google Cloud account to use. Once logged in, create a project to use if you do not have one. Proceed to the Library section and ensure the following APIs are enabled:

  • Places API
  • Maps Static API
  • Maps JavaScript API

Proceed to the credentials section to create an API key to use. Restrict the API key to the above APIs as well as localhost:3000 as the only web referrer.

We are now going to add the API key to our .env file. This is the file that will host our environmental variables, values we do not want to be stored in our codebase.

1touch .env

Add your Google Maps API Key

1<!-- .env -->
2NUXT_ENV_GOOGLE_MAPS_API_KEY=<your-google-maps-api-key>

Additionally, we need to set up the Google Places JS script globally. We do this by adding it to the head section of the nuxt.config.js file.

1// nuxt.config.js
2export default {
3 head: {
4 ...
5 script: [
6 {
7 src: `https://maps.googleapis.com/maps/api/js?key=${process.env.NUXT_ENV_GOOGLE_MAPS_API_KEY}&libraries=places`,
8 },
9 ],
10 },
11 ...
12}

The library will automatically authenticate using your API key.

OpenWeather Setup

We will be using OpenWeather to obtain the weather data. Once registered or loggedin, proceed to the API keys section to create your API key.

Add your API key to the env file.

1...
2NUXT_ENV_OPEN_WEATHER_API_KEY=<your-open-weather-api-key>

Cloudinary setup

Cloudinary is a powerful media management platform with a comprehensive set of APIs and SDKs. If you do not have an account, feel free to create one here.

Proceed to the media library and create a folder called nuxtjs-weather-cards. Upload this file into the folder. This is the template we will use to create the weather cards.

Once the file is uploaded, proceed to the dashboard to obtain your cloudname. Add this to your .env file, it will be used to link your codebase to your account.

1<!-- .env -->
2NUXT_ENV_CLOUDINARY_CLOUD_NAME=<your-cloudinary-cloud-name>

Let us now install the recommended Nuxt.JS plugin.

1yarn add @nuxtjs/cloudinary
2# OR
3npm install @nuxtjs/cloudinary

Add the plugin to the modules section of the nuxt.config.js file.

1// nuxt.config.js
2export default {
3 ...
4 modules: [
5 '@nuxtjs/cloudinary'
6 ],
7}

We now need to link the module to our account by configuring it. Let us do this in a dedicated cloudinary section.

1// nuxt.config.js
2export default {
3 ...
4 cloudinary: {
5 cloudName: process.env.NUXT_ENV_CLOUDINARY_CLOUD_NAME,
6 useComponent: true,
7 }
8}

The above configuration additional enables us to use the custom components that come with the package.

Searching cities

Now that setup is complete, we need to enable our users to search for the city they want to generate the weather card for. First, we add a simple HTML input element.

1<!-- pages/index.vue -->
2<template>
3 ...
4 <input id="city-search" />
5 ...
6</template>

Let us now configure the above input form to autocomplete cities and obtain coordinate data.

1// pages/index.vue
2
3<script>
4export default {
5 data(){
6 return {
7 city: null,
8 };
9 },
10 mounted(){
11 const input = document.getElementById("city-search");
12
13 const options = {
14 fields: ["geometry","name"],
15 strictBounds: false,
16 types: ["(cities)"],
17 };
18
19 const autocomplete = new google.maps.places.Autocomplete(input, options);
20
21 google.maps.event.addListener(
22 autocomplete,
23 'place_changed',
24 () => this.city = autocomplete.getPlace()
25 );
26 },
27}
28</script>

The above code specifies that we want to query cities only. For each city we will receive the city name and geometry. Once a city is selected, it will be saved in the page state.

Obtaining weather data

Once the city is changed, we want to reset the weather data in the state as well as get new city's weather data. Let us do this using a watcher.

1// pages/index.vue
2<script>
3export default {
4 data(){
5 return {
6 ...
7 weather:null
8 }
9 }
10 ...
11 watch:{
12 city(){
13 this.weather = null;
14 if(this.city){
15 this.getWeatherData();
16 }
17 },
18 },
19 ...
20}
21</script>

Let us now set up the getWeatherData method.

1// pages/index.vue
2export default {
3 data(){
4 return {
5 ...
6 status: "Waiting for country selection.."
7 };
8 }
9 ...
10 methods:{
11 getWeatherData(){
12 this.status = "Loading weather data..."
13 const requestOptions ={
14 method: 'GET',
15 redirect: 'follow'
16 };
17
18 const requestURL = "https://api.openweathermap.org/data/2.5/weather?";
19
20 const queryParams = new URLSearchParams({
21 lat: this.city.geometry.location.lat(),
22 lon: this.city.geometry.location.lng(),
23 appid: process.env.NUXT_ENV_OPEN_WEATHER_API_KEY,
24 units: "metric"
25 });
26
27 fetch(requestURL + queryParams, requestOptions)
28 .then(response => response.json())
29 .then( response => this.weather = response)
30 .then(() => this.status = null)
31 .catch(error => alert('Error:' + error));
32 }
33 }
34}

Within the getWeatherData method, we query the OpenWeather APIs for the weather data using metric units. We store the data received in the state and set the status to null.

Now we have all the data we need, let us generate the weather card. First, let us create a computed property to get the static map.

1// pages/index.vue
2export default {
3 ...
4 computed:{
5 staticMapUrl(){
6 let url = "https://maps.googleapis.com/maps/api/staticmap?";
7 url += `center=${this.city.geometry.location.lat()},${this.city.geometry.location.lng()}`;
8 url += "&zoom=13";
9 url += "&size=640x640";
10 url += "&maptype=roadmap";
11 url += `&key=${process.env.NUXT_ENV_GOOGLE_MAPS_API_KEY}`;
12
13 return url;
14 }
15 },
16 ...
17}

Let us now display the status as well as the weather card.

1<!-- pages/index.vue -->
2<template>
3
4 <p v-if="status" class="text-gray-600 text-sm">
5 {{status}}
6 </p>
7
8 <cld-image width="400" v-if="weather" class="mt-10 mx-auto w-full" public-id="nuxtjs-weather-cards/template">
9 <cld-transformation
10 :overlay="`fetch:${staticMapUrl}`"
11 gravity="north"
12 />
13 <!-- Weather description -->
14 <cld-transformation
15 :overlay="`text:Sacramento_100_normal:${weather.weather[0].description},co_rgb:000000`"
16 gravity="center"
17 y="130"
18 />
19 <!-- Feels like -->
20 <cld-transformation
21 :overlay="`text:Roboto_18_normal:Feels like ${weather.main.feels_like} °C,co_rgb:32465c`"
22 gravity="center"
23 y="220"
24 />
25 <!-- Main temp -->
26 <cld-transformation
27 :overlay="`text:Roboto_50_normal:${weather.main.temp} °C,co_rgb:000000`"
28 gravity="center"
29 y="300"
30 />
31 <!-- Min temp -->
32 <cld-transformation
33 :overlay="`text:Roboto_18_normal:MIN,co_rgb:31465d`"
34 gravity="center"
35 y="400"
36 x="-170"
37 />
38 <cld-transformation
39 :overlay="`text:Roboto_18_normal:${weather.main.temp_min} °C,co_rgb:31465d`"
40 gravity="center"
41 y="430"
42 x="-170"
43 />
44 <!-- Max temp -->
45 <cld-transformation
46 :overlay="`text:Roboto_18_normal:MAX,co_rgb:31465d`"
47 gravity="center"
48 y="400"
49 x="170"
50 />
51 <cld-transformation
52 :overlay="`text:Roboto_18_normal:${weather.main.temp_max} °C,co_rgb:31465d`"
53 gravity="center"
54 y="430"
55 x="170"
56 />
57 <!-- Pressure -->
58 <cld-transformation
59 :overlay="`text:Roboto_18_normal:PRESSURE,co_rgb:31465d`"
60 gravity="center"
61 y="510"
62 x="-170"
63 />
64 <cld-transformation
65 :overlay="`text:Roboto_18_normal:${weather.main.pressure} Pa,co_rgb:31465d`"
66 gravity="center"
67 y="540"
68 x="-170"
69 />
70 <!-- Humidity -->
71 <cld-transformation
72 :overlay="`text:Roboto_18_normal:HUMIDITY,co_rgb:31465d`"
73 gravity="center"
74 y="510"
75 x="170"
76 />
77 <cld-transformation
78 :overlay="`text:Roboto_18_normal:${weather.main.humidity} g.kg-1,co_rgb:31465d`"
79 gravity="center"
80 y="540"
81 x="170"
82 />
83 </cld-image>
84</template>

In the above code, we obtain the template from Cloudinary. We additionally overlay the city map, weather description, temperature, pressure and humidity data.

We customize the font, text size, font weight as well as color. This ensures that our output is appealing.

Conclusion

With the above, we have been able to enable our users to search for a city and generated a weather card they can view or share. This is just a scratch of what is possible with these three APIs combined. Feel free to review the below references to learn more about the services.

Eugene Musebe

Software Developer

I’m a full-stack software developer, content creator, and tech community builder based in Nairobi, Kenya. I am addicted to learning new technologies and loves working with like-minded people.