Simple AngularJS Weather App
Today, let’s have fun. Let’s build a simple AngularJS Weather App. You might be wondering how I come up with some of my apps.
I don’t see visions in my sleep nor do I hear voices from any unknown source. Some of the projects I share on my blog here, such as the Laravel Survey App, and Siya, were all inspired by a job interview.
Although I am not able to complete all these projects on time or to the satisfaction of the potential employers, I end up learning so much and enjoy the experience.
For a job interview, I built this simple Angular JS Weather Report app, and I’m here to share with you.
I’ll be skipping the obvious points, cutting to the chase directly. So, let’s get going.
Project Setup
Over the months and years, I’ve come to have a simple project structure for quick apps I wish to build in AngularJS 1.5. This project comes with toastr
and ngProgress
ready to go. I did previous tutorial on using the ngProgress
you can see how it works.
I’m working on something like that for Angular 2 as well
Go ahead and clone the project for this article, then check out to the initial commit. Follow from there, and you’re good to go.
Grab the repository of this article from here: https://github.com/seanmavley/cc-weather-app
This article is not to teach you the step-by-step code involved, but share how to approach simple tasks with Angular JS and how easy the most popular Front-End framework from Google makes building single page apps a breeze.
Interested Features
The app, in general, takes in coordinates of a place and returns with a weather report regarding the place in context.
In the idea above comprises of these features we’re looking for:
- Request user location
- Send the information to a remote server
- Retrieve response from remote server, and display the data accordingly
Let’s break down the above processes into simpler forms and tackle them individually.
Request User Location
HTML5 GeoLocation makes requesting user location access a breeze. By simply calling the getCurrentLocation
, you’re requesting permission from the User
Read more about GeoLocation in HTML5
In Angular, there’s a package that wraps around Geolocation stuff you can take advantage of, namely ngGeolocation.
In this project, that’s the package I used, and worked well. Simply reference the .js
of the package in your index.html
and do the installation requirements.
Then enjoy its use in your app like so:
angular.module('myApp') .controller('HomeController', ['$scope', '$geolocation', function($scope, $geolocation) { $geolocation.getCurrentPosition({ timeout: 6000 }).then(function(position) { $scope.position = position; console.log(position); // will touch on this function in a bit doWeather(position); }) .catch(function(error) { // at this stage, something went wrong // likely, user reject auto geolocation, so drop // down to manual. Will show code below console.log(error); }) } ])
The above snippet looks straightforward enough. By calling $getCurrentPosition
, we’re requesting permission from the user. If the user approves, we do something withing the .then()
part.
The .catch()
happens when an error occurs. This error could be of many forms. It could be the user requesting access, or user’s device not supporting HTML5 Geolocation.
Whichever the case, we’re able to fall back to doing things manually. We’ll see how to do manual things soon.
We end up showing the results in template via something like this:
<div ng-show="position"> <h5 class="lead">Here's your location</h5> <p>Your latitude is: <strong>{{ position.coords.latitude || position.data.coord.lat }}</strong> and longitude is: <strong>{{ position.coords.longitude || position.data.coord.lon }}</strong> </p> <h4 class="lead"> and here's the weather report </h4> <div class="row"> <div class="medium-6 columns"> Location: {{ position.data.name }} <br> Country Code: {{ position.data.sys.country}} <br> position: {{ position.data.weather[0].description }} <br> Wind: Travelling at {{ position.data.wind.speed }} speed at {{ position.data.wind.deg }} degrees </div> <div class="medium-6 columns"> Ground Level: {{ position.data.main.grnd_level }} <br> Humidity: {{ position.data.main.humidity }} <br> Pressure: {{ position.data.main.pressure }} <br> Sea Level: {{ position.data.main.sea_level }} <br> Temp: {{ position.main.data.temp }} <br> Max Temperature: {{ position.data.main.temp_max }} <br> Min Temperature: {{ position.data.main.temp_min }} </div> </div> </div>
You can see the full snippet in the repository, however, we’re basically accessing the direct fields in the position
object we throw into the view from the controller, which in turn was the response from the remote server we’re talking to.
Sending info to Server
If you’re wondering where the position comes from, see the the doWeather()
function and let’s discuss:
doWeather = function(position) { $scope.loading = true; // get the weather report openweathermapFactory.getWeatherFromLocationByCoordinates({ lat: position.coords.latitude, lon: position.coords.longitude, appid: "95fce1ea5c2190183b85f3e518de44cd" }).then(function(data) { $scope.loading = false; $scope.position = data; console.log(data); toastr.info('Weather report is ready'); }).catch(function(error) { console.log(error); toastr.error('Could not complete task', 'Some error happened. See console for details'); }) };
The doWeather()
function wasn’t tucked under the resolve function of the $geolocation
because we want to call the function multiple times, and don’t want to repeat ourselves.
The doWeather()
(what a crazy name!) also uses the openweathermapFactory
which wraps around the Open Weather Map API.
The package which provides the wrapper for the Open Weather Map is such a useful one you should check it out
We specifically call the getWeatherFromLocationByCoordinates()
to go grab the weather report using specific location we have, basically from the auto-acquired geolocation data from the user’s browser.
The Open Weather API wrapper comes with other nifty functions to help save us work.
Display Map
You can see from the final project that we display a map. Wondering how we do so?
<ng-map ng-if="positionReady" center="{{ position.coords.latitude }}, {{ position.coords.longitude }}"> <marker position="[{{ position.coords.latitude }}, {{ position.coords.longitude }}]" title="My location" animation="Animation.BOUNCE"></marker> </ng-map>
To make use of the above snippet in our template, we need to first include the Google Maps API Javascript file,
<script src="http://maps.google.com/maps/api/js?key=YOUR_KEY_HERE"></script>
then
.controller('HomeController', ['$scope', '$geolocation', 'openweathermapFactory', '$http', function($scope, $geolocation, openweathermapFactory, $http) {
after you’ve injected these services/providers in your main module, in this case, our app.js
angular.module('myApp', ['ui.router', 'ngGeolocation', 'ngProgress', 'ui.router.title', 'jtt_openweathermap', 'ngMap'])
Doing the above gives us a simple Google Maps display after we receive the coordinates from our user.
The <ng-map>
tag has an ng-if
attribute attached Wondering why?
Well, in order to prevent race conditions, there’s the positionReady
condition that needs to be met. This condition refers to ‘If the user coordinates are ready from the user’
If the map starts loading without the coordinates available, we don’t expect a proper form to render. The conditional check there ensures that part of the site regarding the map starts loading only after the coordinates are ready.
Conclusion