Building A Location Aware Weather Application Using Aurelia and YQL (Yahoo Query Language)

in #utopian-io6 years ago

Repository

https://github.com/Vheissu/aurelia-weather

What Will I Learn?

  • You will learn how to bootstrap a new Aurelia application using the Aurelia CLI
  • you will learn how to create a view and view-model in Aurelia
  • You will learn how to use the Aurelia Fetch Client to make API requests and consume data
  • You will learn how to use the browser navigator API to get the current user location
  • You will learn how to communicate with YQL (Yahoo Query Language) to get weather based on latitude and longitude coordinates

Requirements

  • A computer running any operating system; macOS, Windows, Linux
  • A code editor or IDE such as Visual Studio Code where you will be writing code
  • Node.js which can be downloaded here for installing dependencies and tooling
  • The Yarn package manager, downloaded here
Required Knowledge:
  • Understanding of modern Javascript and HTML
  • Some experience working with Javascript SPA frameworks or libraries like Vue.js (although not essential to follow along)
  • Familiarity with PowerShell or Terminal

Difficulty

Intermediate

Tutorial Contents

In this tutorial you will learn how to use the Aurelia CLI to create a new Aurelia application, interact with an API in the form of Yahoo's Query Language service, manipulate the returned JSON data and then display it. By the end of this tutorial, you will have a basic understanding of templating in Aurelia as well as being able to query and display weather information based on the users current location.

Step 1: Prepare your machine

Prepare your machine by running the Node.js installer link in the requirements section above. For brevity, the link to the Node.js installer is also provided here. All you have to do is download the relevant installer for your machine and follow each installation step, no changes should be required. Make sure you install the LTS version of Node.js, not the latest version.

After you have installed Node.js and Npm, install the Yarn package manager (if you haven't already done so). Yarn is a modern package manager that is a drop-in replacement for the Node Package Manager (Npm) that ships with the Node.js. It offers a nicer way of working with Node packages, including caching and performance improvements.

If you haven't downloaded an IDE or code editor just yet, I highly recommend downloading Visual Studio Code which is an excellent IDE provided by Microsoft that is free and supports all modern web application development workflows.

You should now have Node.js installed, Yarn package manager and a code editor where you will spend a lot of your time coding.

Step 2: Install the CLI

You should have a working development environment capable of allowing you to install Node packages now, so let's install the Aurelia CLI tool. The Aurelia CLI is a command line application that allows you to easily create new Aurelia applications without having to write a single line of code and it is the preferred means of building Aurelia applications and scaffolding.

To install the Aurelia CLI, open up a PowerShell/Terminal window and run the following from anywhere:

npm install aurelia-cli -g

The first part of that command npm install uses the Node Package Manager to install a package, the aurelia-cli part is the name of the package and finally, the -g flag tells the package manager to install Aurelia CLI globally. It's a recommended practice to always install CLI tools as global dependencies because they're applications that can be run from anywhere.

You can test if Aurelia CLI has successfully installed by running au -v and a version number should be displayed. At the time of publishing this tutorial, the version number is 0.33.1.

aurelia-tutorial-1.PNG

Step 3: Build a new Aurelia project

We have everything we need to start building an Aurelia application. Navigate to your home directory if you're on Mac or Linux cd ~ or if you're on Windows, navigate to cd / which will take you to the root directory of your C drive. This is where we are going to be creating our Aurelia application.

aurelia-tutorial-2.PNG

Now run the following command for creating a new project: au new - the CLI will by default use the name aurelia-app let's keep it as that, so we don't have to type in anything and just hit enter. You should then be presented with a screen with three options, once again to keep things simple we will stick with the default which is option 1 "Default ESNext (Default)" - hit enter.

aurelia-tutorial-3.PNG

You should then be presented a project configuration summary which chooses some defaults for you:

aurelia-tutorial-4.PNG

  • Name: aurelia-app
  • Platform: Web
  • Bundler: Webpack
  • Loader: None
  • Transpiler: Babel
  • Markup Processor: Minimal Minification
  • CSS Processor: None
  • Unit Test Runner: Jest
  • Unit Test Runner: Karma
  • Integration Test Runner: None
  • Editor: Visual Studio Code

We are happy with these defaults, so once again hit 'enter' to continue. You will now be asked if you want to install project dependencies, we do, so hit 'enter' once again. Once the dependencies have installed, you should be presented with a screen resembling the following:

aurelia-tutorial-5.PNG

We now have a functional barebones Aurelia application which does nothing, but works. Let's confirm everything installed correctly by going into the project directory cd aurelia-app and then running the run command au run --watch - this will spin up a local server on port 8080 which is available by visiting http://localhost:8080 in your web browser.

Don't be concerned, the run command will cause a bit of text to be displayed in your terminal. This is just Webpack (the bundler being used) building your application and getting it ready to be served in a browser.

aurelia-tutorial-6.PNG

You should see the following in your browser:

aurelia-tutorial-7.PNG

Now open up Visual Studio Code (or equivalent code editor) and open up your newly created project directory. This is where we are going to be building our application.

Step 5: Install aurelia-fetch-client

By default, Aurelia does not ship with the Fetch client, which is a lightweight wrapper around the browsers native Fetch implementation. All we have to do is open up a terminal window in our project directory and run the following:

yarn add aurelia-fetch-client
Step 6: Create an API service

We are now going to create an API service class. The primary purpose of this API service is to communicate with the server and fetch data, in our case the server is Yahoo's Query Language service which will return JSON data.

In your code editor inside of the src directory create a new file called api.js and populate it with the following:

import { HttpClient } from 'aurelia-fetch-client';
import { inject, Lazy } from 'aurelia-framework';

@inject(Lazy.of(HttpClient))
export class Api {
  http;

  constructor(getHttpClient) {
    this.http = getHttpClient();
  }

  async getWeather(lat, lon, unit = 'c') {
    // https://developer.yahoo.com/weather/
    const req = await this.http.fetch(`https://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20weather.forecast%20where%20woeid%20in%20(SELECT%20woeid%20FROM%20geo.places%20WHERE%20text%3D%22(${lat}%2C${lon})%22)%20and%20u%3D%22${unit}%22&format=json&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys`);
    const res = await req.json();

    return res.query.results.channel;
  }
}

This simplistic looking class will allow us to make a request via YQL and return weather information. The imports at the top of the file are importing the Aurelia Fetch Client and a couple of dependency injection methods for instantiating the fetch client.

Above the API class is a decorator @inject(Lazy.of(HttpClient)) which injects a new instance of the HttpClient into the class as a function which we call to return a new instance of the Fetch client. We do it this way because in a real world application, you might have more than one class which uses the Fetch client and you want a unique instance every time, not a singleton.

The inject decorator passes the HttpClient to the constructor as an argument constructor(getHttpClient) and then inside of the constructor we call the lazy function to get an instance of the Fetch client and store it in a class variable called http we can access from other functions in our API class.

And then we get to the functionality of our API, a single method called getWeather with 3 arguments (two required, one optional). We make this an async method so we can use the await keyword inside to cleanly make Fetch requests without needing to use .then and chain callback functions like you traditionally had too with promises.

This method is making a Fetch request to Yahoo's Query Language service and passing in a query which queries Yahoo's weather Forecast API and gets the weather based on the provided latitude and longitude coordinates. We use a template string we can can use interpolation for the lat and lon values seen here ${lat}%2C${lon}.

Our function accepts three arguments, the first two lat and lon are required, the third is for specifying what unit of measurement we are using for the weather information: Celsius or Fahrenheit. In this instance, Celsius is the assumed unit by providing c as the value. If the desired format is Fahrenheit, we provide f as the value.

The request will then need to be resolved using a Fetch resolver, in this case, we are expecting JSON data, so we await the JSON resolver and assign the result to a variable called res. Lastly, our function returns the data from query.results.channel which contains weather information.

We now have a functional API service which can fetch weather information, let's integrate it into our application.

Step 7: Integrate API and Display Data

We are going to leverage two existing files in our generated Aurelia CLI application to add in our API to load and display weather.

Firstly, open up src/app.js and replace the file contents with the following:

import { Api } from './api';

import { inject } from 'aurelia-framework';

@inject(Api)
export class App {
  // Fields for weather information
  lat;
  lon;
  description;
  location;
  temp;
  tempLow;
  tempHigh;

  loading = false;

  // Pass in API from above inject decorator
  constructor(api) {
    this.api = api;
  }

  // attached method is called when page is loaded and DOM is ready
  attached() {
    // We are loading, make the UI display loading text until we are ready
    this.loading = true;

    // Prompt the users browser to ask for permission to get their current location
        navigator.geolocation.getCurrentPosition(async (pos) => {
      // User has allow their location to be shared, get their latitude and longitude
            this.lat = pos.coords.latitude;
      this.lon = pos.coords.longitude;

      // Make a request to our API service and pass the latitude and longitude
      const weather = await this.api.getWeather(this.lat, this.lon);

      // Get the city and region name
      this.location = `${weather.location.city}, ${weather.location.region}`;

      // Temperature value (either in Fahrenheit or Celsius)
      this.temp = weather.item.condition.temp;

      // Lowest predicted temperature for today
      this.tempLow = weather.item.forecast[0].low;

      // Highest predicted temperature for today
      this.tempHigh = weather.item.forecast[0].high;

      // Description of the weather (sunny, partly cloudy, etc)
      this.description = weather.item.condition.text;

      // Set loading to false
      this.loading = false;
        }, (err) => {
      console.warn(`ERROR(${err.code}): ${err.message}`);

      // Location could not be determined/user blocked attempt to get location so set loading to false
      this.loading = false;
    });
  }
}

Now, open up src/app.html and replace the contents with the following:

<template>
  <h1 if.bind="!loading">Weather: ${location}</h1>
  <h1 if.bind="loading">Loading...</h1>

  <section if.bind="!loading">
    <p><strong>Temperature:</strong> ${temp}&deg; - ${description}</p>
    <p>Low: ${tempLow}&deg;</p>
    <p>High: ${tempHigh}&deg;</p>
  </section>
</template>

Aurelia has an intuitive templating language, using familiar template literal string interpolation to echo/display values in the view. Think of ${} as being the same as Vue.js/Handlebars/Angular's {{myvariable}} it works the same.

In our HTML view, we are using an if.bind to conditionally show or hide weather information based on whether we are loading or not.

Conclusion: Running the app

Provided you followed each step closely, open up a terminal window in the project directory and run: au run --watch to load the application.

By default it will run on port 8080 so the app will be visible at: http://localhost:8080 - you should be prompted by your browser to allow or block the request for location. If you click allow you should see the weather for your locality. The following is what you will see if you load the app in Google Chrome.

location-popup.PNG

If you click allow, you should see in a few moments thereafter that weather information is displayed in all of its unstyled glory, resembling the following:

allowed-weather.PNG

As you can see, it took very little code to produce a functional (albeit unstyled and not very pretty) web application. We covered a couple of important concepts that are relevant to any modern web application, working with an API and displaying its data.

Further Reading

If you are interested in learning more about Aurelia or Yahoo Query Language, the following resources will allow you to further explore these topics.

Proof of Work Done

https://github.com/Vheissu/aurelia-weather

Sort:  

Awesome m8, this looks like it will be a fun series to follow along with and learn some different skills while honing existing skills.
Please link to previous posts of series in footer to make it easy to catch up on, as I won't have time till next weekend :)

Thank you for your contribution.

  • In the api.js code you should have put comments to better understand what the code does.

Your contribution has been evaluated according to Utopian policies and guidelines, as well as a predefined set of questions pertaining to the category.

To view those questions and the relevant answers related to your post, click here.


Need help? Write a ticket on https://support.utopian.io/.
Chat with us on Discord.
[utopian-moderator]

Congratulations! Your post has been selected as a daily Steemit truffle! It is listed on rank 17 of all contributions awarded today. You can find the TOP DAILY TRUFFLE PICKS HERE.

I upvoted your contribution because to my mind your post is at least 13 SBD worth and should receive 81 votes. It's now up to the lovely Steemit community to make this come true.

I am TrufflePig, an Artificial Intelligence Bot that helps minnows and content curators using Machine Learning. If you are curious how I select content, you can find an explanation here!

Have a nice day and sincerely yours,
trufflepig
TrufflePig

Hey @beggars
Thanks for contributing on Utopian.
We’re already looking forward to your next contribution!

Want to chat? Join us on Discord https://discord.gg/h52nFrV.

Vote for Utopian Witness!

Love your work @utopian-io and everything that you all do for supporting content creators on Steem. Thank you for existing.

Coin Marketplace

STEEM 0.35
TRX 0.12
JST 0.040
BTC 70351.33
ETH 3563.43
USDT 1.00
SBD 4.72