Crafting a Weather App with Ionic Framework and Forecast.io

In this article we’re going to build a simple weather forecast app with Ionic framework, an open-source front-end development kit for building cross-platform mobile apps with HTML5 and Javascript.

Forecast.io is web service that provides weather data for a given latitude and longitude.

Installation

Installing Ionic is quick and easy. To get started, I’ve summarized the installation steps below. You can find additional information here.

First, download the build of Node.js appropriate for your platform and install it, if you don’t already have it on your machine, that is. This should install NPM (Node Package Manager) as well.

Ionic runs on Linux, Mac OS X and Windows, but you’ll need to use the command line to create the app, add platforms and launch the app in an emulator or a device. For Windows users, git bash is a better alternative to the platform’s command prompt. More details can be found right here.

Next, let’s install Cordova.

  1. $ npm install -g cordova

Finally, install Ionic.

  1. $ sudo npm install -g gulp ionic

Ionic framework should now be successfully installed on your machine.

Now let’s create an app. From the terminal, go to your project folder. Type the following command to create a blank app with tabs.

  1. $ ionic start weather tabs

Simply replace tabs in the above line with blank or sidemenu to create a blank app, or an application with a side menu.

Finally navigate to the project directory and add iOS and/or Android platforms to Ionic and test the app.

  1. $cd weather
  2. $ ionic platform add ios
  3. $ ionic build ios
  4. $ ionic emulate ios

That’s it! In a few lines, we’ve installed Ionic and quickly created a starter app. It doesn’t really do much at this time so let’s get to work.

Building the app

We’re building a simple app that displays current weather data for a selected city by pulling data from Forecast.io.

Our app has three pages. Page one displays the current temperature and weather conditions for a default city. The second page contains a list of cities. Selecting one of these cities will display its weather details. While the third page is a placeholder for storing app settings, we won’t be covering them in this tutorial.

Before we begin, head over to Forecast.io and signup as a developer to get your own API key for use with this walkthrough.

The application should look like the image below when we are done.

Screen 1 – Home:

home-v1

Screen 2 – Cities

cities

To complete the app we need to:

  • Define the front end views
  • Define controllers that provide data
  • Define routes to tie the views to the controllers

So, let’s get started. Navigate to the folder www/templates from your application root directory. This folder contains template files that will be rendered for the app. Since we opted to use the tabs style starter app, Ionic has created some files in this folder.

To make things a bit easier, let’s modify the generated files slightly to suit our needs.

  • Delete friend-detail.html
  • Rename tab-dash.html to tab-home.html
  • Rename tab-friends.html to tab-cities.html
  • Rename tab-account.html to tab-settings.html

Replace code in tab-home.html with the code below.

  1. <ion-view title="Home">
  2.   <ion-content class="has-header">
  3.     <!-- Display current location's weather data-->
  4.     <h3>{{city}}</h3>
  5.     <h5><weather-icon icon="current.icon" id="current-icon"></weather-icon> {{current.currently.summary}}</h5>
  6.     <p>The temperature now is</p>  
  7.   <span class="large">{{current.currently.temperature}} ° </span>
  8.   </ion-content>
  9. </ion-view>

Here we’re displaying the weather data for the current city. Current is a scope item that contains weather data returned from Forecast.io.

Save your changes. Next, open tab-cities.html and replace the code inside it with the code below to display a list of cities using ion-list.

  1. <ion-view title="Cities">
  2.   <ion-content class="has-header">
  3.       <h1>Select city</h1>
  4.         <ion-list>
  5.               <ion-item ng-repeat="city in cities">
  6.               <span href="#" ng-click="changeCity('{{city.id}}')">
  7.                 {{city.name}}
  8.               </span>
  9.               </ion-item>
  10.         </ion-list>
  11.    </ion-content>
  12. </ion-view>

Next open tabs.html. This contains code that defines the tabs that appear at the bottom of all pages. Replace the code inside the file with the code below to link the tabs with the view templates we just modified.

  1. <ion-tabs class="tabs-icon-top">
  2.   <!-- home Tab -->
  3.   <ion-tab title="Home" icon="icon ion-home" href="#/tab/home">
  4.     <ion-nav-view name="tab-home"></ion-nav-view>
  5.   </ion-tab>
  6.  
  7.   <!-- Cities Tab -->
  8.   <ion-tab title="Change City" icon="icon ion-heart" href="#/tab/city">
  9.     <ion-nav-view name="tab-cities"></ion-nav-view>
  10.   </ion-tab>
  11.  
  12.   <!-- Settings Tab -->
  13.   <ion-tab title="Settings" icon="icon ion-gear-b" href="#/tab/settings">
  14.     <ion-nav-view name="tab-settings"></ion-nav-view>
  15.   </ion-tab>
  16. </ion-tabs>

Next, let’s define the routes. Open js/app.js and replace the code with the lines below.

  1. angular.module('starter', ['ionic', 'starter.controllers', 'starter.services'])
  2. .run(function($ionicPlatform) {
  3.   $ionicPlatform.ready(function() {
  4.     // Hide the accessory bar by default (remove this to show the accessory bar above the keyboard
  5.     // for form inputs)
  6.     if(window.cordova && window.cordova.plugins.Keyboard) {
  7.       cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
  8.     }
  9.     if(window.StatusBar) {
  10.       // org.apache.cordova.statusbar required
  11.       StatusBar.styleDefault();
  12.     }
  13.   });
  14. })
  15. .config(function($stateProvider, $urlRouterProvider) {
  16. $stateProvider
  17. // setup an abstract state for the tabs directive
  18.     .state('tab', {
  19.       url: "/tab",
  20.       abstract: true,
  21.       templateUrl: "templates/tabs.html"
  22.     })
  23.     // Each tab has its own nav history stack:
  24.     .state('tab.home', {
  25.       url: '/home',
  26.       views: {
  27.         'tab-home': {
  28.           templateUrl: 'templates/tab-home.html',
  29.           controller: 'HomeCtrl'
  30.         }
  31.       }
  32.     })
  33.     .state('tab.changecity', {
  34.       url: '/city',
  35.       views: {
  36.         'tab-cities': {
  37.           templateUrl: 'templates/tab-cities.html',
  38.           controller: 'LocationsCtrl'
  39.         }
  40.       }
  41.     })
  42.     .state('tab.settings', {
  43.       url: '/settings',
  44.       views: {
  45.         'tab-settings': {
  46.           templateUrl: 'templates/tab-settings.html',
  47.           controller: 'SettingsCtrl'
  48.         }
  49.       }
  50.     });
  51. // if none of the above states are matched, use this as the fallback
  52.   $urlRouterProvider.otherwise('/tab/home');
  53. });

After defining templates and adding routes, we’ll create controllers. Open js/controllers.js and replace the code inside it with what’s below.

  1. angular.module('starter.controllers', ['ionic'])
  2. .constant('FORECASTIO_KEY', 'your forecastio key here')
  3. .controller('HomeCtrl', function($scope,$state,Weather,DataStore) {
  4.     //read default settings into scope
  5.     console.log('inside home');
  6.     $scope.city  = DataStore.city;
  7.     var latitude  =  DataStore.latitude;
  8.     var longitude = DataStore.longitude;
  9.  
  10.     //call getCurrentWeather method in factory ‘Weather’
  11.     Weather.getCurrentWeather(latitude,longitude).then(function(resp) {
  12.       $scope.current = resp.data;
  13.       console.log('GOT CURRENT', $scope.current);
  14.       //debugger;
  15.     }, function(error) {
  16.       alert('Unable to get current conditions');
  17.       console.error(error);
  18.     });
  19.  
  20. })
  21. .controller('LocationsCtrl', function($scope,$state, Cities,DataStore) {
  22.   $scope.cities = Cities.all();
  23.  
  24.   $scope.changeCity = function(cityId) {
  25.     //get lat and longitude for seleted location
  26.     var lat  = $scope.cities[cityId].lat; //latitude
  27.     var lgn  = $scope.cities[cityId].lgn; //longitude
  28.     var city = $scope.cities[cityId].name; //city name
  29.  
  30.     DataStore.setCity(city);
  31.     DataStore.setLatitude(lat);
  32.     DataStore.setLongitude(lgn);
  33.  
  34.     $state.go('tab.home');
  35.   }
  36. })
  37. .controller('SettingsCtrl', function($scope) {
  38.     //manages app settings
  39. });

In the code above, we define three controllers:

  • Home: Defines two scopes, city and current. Current is data returned from an API request. City names are retrieved from DataStore factory. Here we’re calling the method ‘getCurrentWeather’ defined in factory ‘Weather’ passing in latitude and longitude.
  • Locations: Controls city view
  • Settings: Controls the settings view

In the home and locations controllers, we inject three factories:

  • Cities
  • DataStore: Stores data used across controllers
  • Weather: Contains methods to access the Forecast.io API for weather data

Lastly, let’s take a look at js/services.js. This file defines services and factory objects. Factory objects allow different controllers to share the same data.

  1. 'use strict';
  2.  
  3. var forecastioWeather = ['$q', '$resource', '$http', 'FORECASTIO_KEY',
  4.   function($q, $resource, $http, FORECASTIO_KEY) {
  5.   var url = 'https://api.forecast.io/forecast/' + FORECASTIO_KEY + '/';
  6.  
  7.   var weatherResource = $resource(url, {
  8.     callback: 'JSON_CALLBACK',
  9.   }, {
  10.     get: {
  11.       method: 'JSONP'
  12.     }
  13.   });
  14.  
  15.   return {
  16.     //getAtLocation: function(lat, lng) {
  17.     getCurrentWeather: function(lat, lng) {
  18.       return $http.jsonp(url + lat + ',' + lng + '?callback=JSON_CALLBACK');
  19.     }
  20.   }
  21. }];
  22.  
  23. angular.module('starter.services', ['ngResource'])
  24. .factory('Cities', function() {
  25. var cities = [
  26.     { id: 0, name: 'Miami', lat:25.7877 , lgn: 80.2241 },
  27.     { id: 1, name: 'New York City' ,lat: 40.7127 , lgn: 74.0059 },
  28.     { id: 2, name: 'London' ,lat:51.5072 , lgn: 1.1275 },
  29.     { id: 3, name: 'Los Angeles' ,lat: 34.0500 , lgn: 118.2500 },
  30.     { id: 4, name: 'Dallas' ,lat: 32.7758 , lgn:96.7967  },
  31.     { id: 5, name: 'Frankfurt' ,lat:50.1117 , lgn: 8.6858 },
  32.     { id: 6, name: 'New Delhi' ,lat:28.6100 , lgn: 77.2300 }
  33.   ];
  34.  
  35.   return {
  36.     all: function() {
  37.       return cities;
  38.     },
  39.     get: function(cityId) {
  40.       // Simple index lookup
  41.       return cities[cityId];
  42.     }
  43.   }
  44. }).
  45. factory('DataStore', function() {
  46.     //create datastore with default values
  47.     var DataStore = {
  48.         city:       'Miami',
  49.         latitude:   25.7877,
  50.         longitude:  80.2241 };
  51.  
  52.     DataStore.setCity = function (value) {
  53.        DataStore.city = value;
  54.     };
  55.  
  56.     DataStore.setLatitude = function (value) {
  57.        DataStore.longitude = value;
  58.     };
  59.  
  60.     DataStore.setLongitude = function (value) {
  61.        DataStore.longitude = value;
  62.     };
  63.  
  64.     return DataStore;
  65. })
  66. .factory('Weather', forecastioWeather);

We have to make on last change before we can see the app in action. Navigate to the “www” folder and open index.html.

Next, locate the line containing

<script src="lib/ionic/js/ionic.bundle.js"></script>

Now, add the following after the line above.

<script src="lib/ionic/js/angular/angular-resource.js"></script>

That’s it. Save the file, and build and run the app from the command prompt:

  1. $ionic build ios
  2. $ionic run ios

ionic run ios installs the build on an ios device. If you don’t have a device on hand, you can run the app in an emulator with the following command:

$ionic emulate ios

You can also view the app in a browser. If you don’t have web server installed, you can use Python’s SimpleHTTPServer. From the terminal, navigate to the “www” folder and run the following command.

$python -m SimpleHTTPServer 3000

Now, open your favorite browser and visit http://localhost:3000 to see the app running.

Conclusion

In this article, we saw how to get started with Ionic, an advanced HTML5 framework for hybrid app development.

In future articles, we’ll take a look at enhancing the app by adding support for a remote backend with Parse.

If you’re unfamiliar with Angular, I recommended taking a look at the AngularJS docs for a better understanding of it. In the mean time, you can get this article’s source code on GitHub

Happy coding!

Author: Sriram Kota