Building a Remote Backend for an Ionic Framework App with Parse

In my previous article we built an Ionic framework app that pulls weather data from Forecast.io for a given latitude and longitude. In this tutorial, we’re going to extend the app to use Parse as a remote backend.

If you’re not familiar with Parse, it’s a web-based platform that provides backend services. It allows developers to focus on the application development without having to worry about server setup and maintenance.

Since parse provides SDKs for Android, iOS and JavaScript, it’s a natural choice for hybrid app developers looking for an easy-to-use remote backend.

Parse setup

Before we can begin using Parse, we have to sign up for a free developer account. Signing up is quick and easy. Just go here.

Once you signup, create an application by pressing the ‘create new app’ button

create_new

Once your app is created, Parse.com displays a window listing API keys that are specific to you application.

keys

Cick the button labelled databrowser. You should now see the dashboard for your app.

dashboard

From the dashboard you can manage your app settings, configure push notification, view analytics, create cloud code and create your data classes from the data browser. In this tutorial, we’ll just be looking at the databrowser section. We’ll explore analytics, push notifications and cloud code in future articles.

Click the new class button to, well, create a new class.

create_class

The default option for class type will be custom. Enter City for class name and press enter. You should now see a newly created class in the dashboard with the following default columns

  • objectId
  • createdAt
  • updatedAt
  • ACL

objectId is a unique alphanumeric ID generated by parse. We will be covering ACL’s in future articles.

To add columns specific to the class City, click the button labelled + Col. Add the following columns.

  • name – string
  • latitude – integer
  • longitude – integer

Next, click the button labelled + Row to add blank rows.

Lastly, add the following data to pre-populate the class.

  • Miami, 25.7877,80.2241
  • New York City, 40.7127 , 74.0059
  • London, 51.5072, 1.1275
  • Los Angeles, 34.0500, 118.2500
  • Dallas, 32.7758, 96.7967
  • Frankfurt, 50.1117, 8.6858
  • New Delhi, 28.6100, 77.2300

Great, we have so far created an account on parse.com, got our API keys and created a class to store location details.

Before we begin coding the app we have to download the Parse JavaScript SDK here.

About the app

Our app shows the weather data for a given latitude and longitude, with the help of Forecast.io.

If you’re just joining us, please refer to Part 1 of this tutorial for the preliminary coding and set up required.

Now, we’re going to modify the application to use data from Parse.com instead of working with static data. You can begin by either making a copy of the code from Part 1 and make changes as specified, or download the complete source code for this version at the end of this tutorial. We’ll be working with a copy of the existing code making changes as we go along.

To complete the app we need to:

  • Add parse-1.2.17.js to index.html
  • Modify LocationsCtrl in controllers.js to fetch data from Parse.
  • Initialize the Parse SDK in app.js
  • Modify the view file tab-cities.html to pass the currently selected items index to LocationsCtrl.changeCity.

If you downloaded the code for the previous article, you’ll want to look for the ionic-weather-appfolder. If you downloaded code into a different folder, substitute the folder name accordingly.

Go to the command prompt, navigate to the parent folder containing the code for part one of this tutorial, and type the following commands.

  1. $cp -R ionic-weather-app ionic-weather-app2
  2. $cd ionic-weather-app2/www

Now, copy parse-1.2.17.js to the folder js/lib/ionic/js/.

Next, open index.html, which is found in the www folder and find the line containing the following.

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

Add the following after below the line.

  1. <script src="lib/ionic/js/parse-1.2.17.js"></script>

Save index.html, open www/js/controllers.js and replace its contents with the code below.

  1. angular.module('starter.controllers', ['ionic'])
  2. .constant('FORECASTIO_KEY', 'add-your-forecastio-key-here')
  3. .controller('HomeCtrl', function($scope,$state,Weather,DataStore) {
  4.    //read default settings into scope
  5.    console.log('inside home');
  6.  
  7.    $scope.city  = DataStore.city;
  8.    var latitude  =  DataStore.latitude;
  9.    var longitude = DataStore.longitude;
  10.  
  11.     //call getCurrentWeather method in factory ‘Weather’
  12.     Weather.getCurrentWeather(latitude,longitude).then(function(resp) {
  13.     $scope.current = resp.data;
  14.     console.log('GOT CURRENT', $scope.current);
  15.     }, function(error) {
  16.       alert('Unable to get current conditions');
  17.       console.error(error);
  18.    });
  19. })
  20. .controller('LocationsCtrl', function($scope,$rootScope, $state,DataStore) {
  21.      $scope.cities = [];
  22.      var data = window.localStorage.getItem('cities');
  23.  
  24.     if (data != null )  {
  25.         $scope.cities   = null;
  26.         $scope.cities   = JSON.parse(data);
  27.         console.log('using local storage');
  28.    }
  29.    else {
  30.        var cityObj = Parse.Object.extend("City");
  31.        var query = new Parse.Query(cityObj);
  32.        //query.descending("createdAt");  //specify sorting
  33.       //query.limit(20);  //specify limit -- fetch only 20 objects
  34.  
  35.        query.find({
  36.            success:function(results) {
  37.                $scope.$apply(function() {
  38.                   var index =0;
  39.                   var Arrlen=results.length ;
  40.  
  41.                    for (index = 0; index < Arrlen; ++index) {
  42.                        var obj = results[index];
  43.                         $scope.cities.push({
  44.                           id :  obj.id,
  45.                           name: obj.attributes.name,
  46.                           lat:  obj.attributes.latitude,
  47.                           lgn:  obj.attributes.longitude
  48.                         });
  49.                    }
  50.                   window.localStorage.setItem('cities', JSON.stringify($scope.cities));
  51.             });    
  52.         },
  53.         error:function(error) {
  54.               console.log("Error retrieving cities!");
  55.         }
  56.     }); //end query.find
  57. }
  58.  
  59.  
  60. $scope.changeCity = function(cityId) {
  61.  
  62.     //get lat and longitude for seleted location
  63.     var data = JSON.parse(window.localStorage.getItem('cities'));
  64.  
  65.     var lat  = data[cityId].lat; //latitude
  66.     var lgn  = data[cityId].lgn; //longitude
  67.     var city = data[cityId].name; //city name
  68.  
  69.     DataStore.setCity(city);
  70.     DataStore.setLatitude(lat);
  71.     DataStore.setLongitude(lgn);
  72.  
  73.     $state.go('tab.home');
  74. }
  75. })
  76. .controller('SettingsCtrl', function($scope) {
  77.   //manages app settings
  78. });

Here’s what occurs in LocationsCtrl upon initialization.

  • Locate an item called cities from local storage.
  • If it’s found, we populate $scope.cities with data from local storage.
  • Otherwise, we make a call to Parse for it to fetch objects of type ‘City’ and, upon successful retrieval, we parse (pun fully intended) the data into an array, add it to $scope.cities and store the data in local storage for subsequent use.

The reason we do the above is to ensure that we reduce the number of server calls and work with a local copy of data to improve performance.

The next thing to notice is that the method changeCity is also modified to use the data from local storage to lookup the selected city’s latitude and longitude.

Save controllers.js, open www/templates/tab-cities.html and replace its content the code below.

  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('{{$index}}')">
  7.                         {{city.name}}
  8.                      </span>  
  9.           </ion-item>
  10.         </ion-list>
  11.     </ion-content>
  12. </ion-view>

In the above code, $index is the iterator offset provided by ng-repeat. It’s a handy way to access elements in a collection by index.

Lastly open www/js/app.js and replace its content the code 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.       //initialize parse
  15.       Parse.Initialize("add-your-parse-application-Id","add-you-parse-javascript-key");
  16.    });
  17. })
  18. .config(function($stateProvider, $urlRouterProvider) {
  19.  $stateProvider
  20.   // setup an abstract state for the tabs directive
  21.      .state('tab', {
  22.        url: "/tab",
  23.        abstract: true,
  24.        templateUrl: "templates/tabs.html"
  25.    })
  26.    // Each tab has its own nav history stack:
  27.     .state('tab.home', {
  28.       url: '/home',
  29.       views: {
  30.         'tab-home': {
  31.            templateUrl: 'templates/tab-home.html',
  32.            controller: 'HomeCtrl'
  33.         }
  34.       }
  35.     })
  36.     .state('tab.changecity', {
  37.        url: '/city',
  38.        views: {
  39.          'tab-cities': {
  40.           templateUrl: 'templates/tab-cities.html',
  41.           controller: 'LocationsCtrl'
  42.         }
  43.       }
  44.     })
  45.     .state('tab.settings', {
  46.        url: '/settings',
  47.        views: {
  48.          'tab-settings': {
  49.           templateUrl: 'templates/tab-settings.html',
  50.           controller: 'SettingsCtrl'
  51.         }
  52.       }
  53.     });
  54.    // if none of the above states are matched, use this as the fallback
  55.     $urlRouterProvider.otherwise('/tab/home');
  56.  });

That’s it. Save the file, then build and run the app from the command line.

  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 handy, you can run the app in an emulator with the following command.

  1. $ionic emulate ios

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

  1. $python -m SimpleHTTPServer 3000

Now, open your favorite browser and vist http://localhost:3000. If this doesn’t work, make sure SimpleHTTPServer is installed and that the path is correct.

If all is well, you should see the app run in the browser.

Conclusion

In this article, we’ve seen how easy it is to integrate Parse as a remote backend with Ionic apps. In future tutorials, we’ll see how to add push notifications to an Ionic app.

If you’re unfamiliar with AngularJS, I recommended referring to the AngularJS docs for a better understanding of it. In the mean time, you can donwload this article’s source code on GitHub

Till next time, happy coding!

Author: Sriram Kota