Creating a Location Sharing App Using the Ionic Framework
It’s hard to remember what travel was like before Google Maps. Fortunately battling with cumbersome, badly folded paper maps and hand drawn directions are a thing of the past. Instead, a mobile phone is slipped from a pocket to confirm the user location, the location desired and how to get between the two.
In this tutorial, I’ll show how to use Google Maps whilst developing mobile apps using the IONIC. Using this app the user will be able to mark a particular position on the map, fill in the address and save the location in a database. I’ll be creating a custom directive to integrate Google Maps into our app. I’ll be using Firebase to save the data.
The source code from this tutorial is available on GitHub.
The IONIC Framework
IONIC is a mobile application framework for developing hybrid apps using HTML5. It uses AngularJS to create rich and robust mobile applications.
From the official site,
Free and open source, Ionic offers a library of mobile-optimized HTML, CSS and JS components, gestures, and tools for building interactive apps. Built with Sass and optimized for AngularJS.
Getting Started
Start by installing Node.js. This will also install the node package manager npm.
Using npm install IONIC.
npm install -g cordova ionic
This tutorial teaches how to create a mobile app for the Android platform, so ensure the required dependencies are installed.
Once platform dependencies are installed create a blank IONIC project.
ionic start iMapApp blank
Navigate to the project directory iMapApp, add the required platform, build and emulate.
cd iMapApp
ionic platform add android
ionic build android
ionic emulate android
The blank app should be running in the Android emulator.
Running the app each time on the emulator would be a time consuming task, so use the IONIC cli to run the app in browser. Install the required dependencies using npm.
npm install
Once the dependencies are installed, run ionic serve
in the terminal and the app should be running in the browser.
Creating the User Interface
Let’s start by adding a new template for displaying the map. Inside the project directory create a folder called www/templates. Inside templates create a file called map.html.
<ion-view title="iMap">
<ion-content>
<div>
<div id="map">
</div>
<div width="80%" class="list list-inset" style="margin-left:10%;margin-right:10%;">
<label class="item item-input">
<input type="text" ng-model="user.desc" placeholder="Description">
</label>
<button class="button button-full button-positive" ng-click="saveDetails()">
Save
</button>
</div>
</div>
</ion-content>
</ion-view>
Inside map.html there is a div called `#map’. The Google Map will be rendered here. Below the map is an input text box for the user to enter a description and a button to save the details.
We will make use of the ionNavView directive to render different templates based on different states. Let’s add the ionNavView
directive to the www/index.html page. Remove the current contents of the body
tag and above the ionNavView
add the ionNavBar
directive to create a top bar. Here is how the modified index.html should look:
<body ng-app="starter">
<ion-nav-bar class="bar-positive">
</ion-nav-bar>
<ion-nav-view></ion-nav-view>
</body>
The title for the ionNavBar
is set from the rendered ionView
. As seen in the above map.html code, the title is set for the ionView
.
IONIC uses the Angular UI router module to organize the app interfaces into different states. Let’s define a state for the map.html template. Open www/js/app.js and add the following code:
.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('map', {
url: '/map',
templateUrl: 'templates/map.html',
controller: 'MapCtrl'
})
$urlRouterProvider.otherwise('/map');
});
The above code defines a new state for the URL, /map that will render the template map.html and be controlled by the MapCtrl
controller (which will be defined shortly). $urlRouterProvider.otherwise('/map');
is used to set /map as the default state.
Inside www/js/ create a file called controller.js and add a reference in the www/index.html file.
<script src="js/controller.js"></script>
The controller code inside controller.js needs to be defined. Start by defining the angular module.
angular.module('starter.controllers', ['ionic'])
Define the controller MapCtrl
.
.controller('MapCtrl', ['$scope', function($scope) {
// Code will be here
}]);
Inject the starter.controllers
module into the starter
app in js/app.js.
angular.module('starter', ['ionic','starter.controllers'])
Once saved, the map.html template is viewable.

Next add the Google Map in map.html by creating a custom directive called map
. This directive will be used as an attribute, so let’s start by defining the directive in controller.js.
.directive('map', function() {
return {
restrict: 'A',
link:function(scope, element, attrs){
// Code will be here
}
};
});
In map.html is a div #map
. Let’s add the directive attribute to this.
<div id="map" map> </div>
Google Maps will require some default parameters like zoom, latitude, longitude etc. Pass these parameters to the directive:
<div id="map" lat="-23.639492" lng="133.709107" zoom="8" map>
</div>
These attributes can be accessed inside the directive’s link function using the parameter attrs
.
.directive('map', function() {
return {
restrict: 'A',
link:function(scope, element, attrs){
var zValue = scope.$eval(attrs.zoom);
var lat = scope.$eval(attrs.lat);
var lng = scope.$eval(attrs.lng);
}
};
});
scope.$eval
is used to evaluate the AngularJS expressions.
Next include the Google Maps API reference in index.html.
<script src="https://maps.googleapis.com/maps/api/js?v=3.exp"></script>
Define Google Map’s latitude and longitude using the default values.
var myLatlng = new google.maps.LatLng(lat,lng)
Define map options for the Google Map:
mapOptions = {
zoom: zValue,
center: myLatlng
}
Define the map with the above mapOptions
and bind it to the #map
div which can be accessed by element[0]
.
map = new google.maps.Map(element[0],mapOptions)
Here is how the directive should now look:
.directive('map', function() {
return {
restrict: 'A',
link:function(scope, element, attrs){
var zValue = scope.$eval(attrs.zoom);
var lat = scope.$eval(attrs.lat);
var lng = scope.$eval(attrs.lng);
var myLatlng = new google.maps.LatLng(lat,lng),
mapOptions = {
zoom: zValue,
center: myLatlng
},
map = new google.maps.Map(element[0],mapOptions);
}
};
});
Add the following style to www/css/style.css to style the #map
div.
#map{
width:80%;
height:400px;
margin:10px auto;
box-shadow:0 3px 25px black;
}
Save the above changes and Google Maps will be viewable on the map page.

Let’s add a marker to the Google Maps.
marker = new google.maps.Marker({
position: myLatlng,
map: map,
draggable:true
})
The default position of the marker is set as the latitude and longitude passed as an attribute and the draggable option is set as true. Here is the modified directive:
.directive('map', function() {
return {
restrict: 'A',
link:function(scope, element, attrs){
var zValue = scope.$eval(attrs.zoom);
var lat = scope.$eval(attrs.lat);
var lng = scope.$eval(attrs.lng);
var myLatlng = new google.maps.LatLng(lat,lng),
mapOptions = {
zoom: zValue,
center: myLatlng
},
map = new google.maps.Map(element[0],mapOptions),
marker = new google.maps.Marker({
position: myLatlng,
map: map,
draggable:true
});
}
};
});
Save the above changes and there will be a draggable marker in the Google Maps.

Tracking Marker Position
Next we will attach a dragend
event to the Google Maps marker to track the position of the marker. Inside the directive, add the following code to attach a drag end event listener:
google.maps.event.addListener(marker, 'dragend', function(evt){
console.log('Current Latitude:',evt.latLng.lat(),'Current Longitude:',evt.latLng.lng());
});
Save the changes and try to drag the marker. Check the browser console and it should include the current latitude and longitude.
Saving the Details
Next we will define a $scope
variable called user
in MapCtrl
. It will contain the current position latitude, longitude and the description entered by the user.
$scope.user = {};
Create a function called saveDetails
in the MapCtrl
controller. This will make use of the $scope.user
variable to get the required data.
$scope.saveDetails = function(){
var lat = $scope.user.latitude;
var lgt = $scope.user.longitude;
var des = $scope.user.desc;
// Code to write to Firebase will be here
}
When the user drags the marker on the map, update the $scope.user.latitude
and $scope.user.longitude
variables in the dragend
event listener’s callback function.
google.maps.event.addListener(marker, 'dragend', function(evt){
scope.$parent.user.latitude = evt.latLng.lat();
scope.$parent.user.longitude = evt.latLng.lng();
scope.$apply();
});
scope.$apply
is called to update the model bindings. Attach a ngModel directive to the description input text box and a ngClick directive to the save button.
<label class="item item-input">
<input type="text" ng-model="user.desc" placeholder="Description">
</label>
<button class="button button-full button-positive" ng-click="saveDetails()">Save</button>
Next we will save the data to firebase. Register for a free account with firebase if you haven’t already. Once logged in you should have a unique firebase URL. For example, my firebase URL is:
https://blistering-heat-2473.firebaseio.com
Sign in to your Firebase account and click on the plus link next to the URL in the dashboard. Enter the name as MapDetails and value as 0 to create a sub URL, /MapDetails.

Include the following script references in the index.html to use firebase in the app.
<script src="https://cdn.firebase.com/js/client/2.0.4/firebase.js"></script>
<script src="https://cdn.firebase.com/libs/angularfire/0.9.0/angularfire.min.js"></script>
Inject firebase in the starter.controllers
module in controller.js.
angular.module('starter.controllers', ['ionic','firebase'])
Inject the $firebase
module into the MapCtrl
controller.
.controller('MapCtrl', ['$scope','$firebase', function($scope,$firebase)
Inside the MapCtrl
create a firebase object using the firebase URL.
var firebaseObj = new Firebase("https://blistering-heat-2473.firebaseio.com/MapDetails");
Using firebaseObj create an instance of $firebase
.
var fb = $firebase(firebaseObj);
Inside the saveDetails
function, make use of the firebase push API to save data to firebase.
fb.$push({
latitude: lat,
longitude: lgt,
description: des
}).then(function(ref) {
$scope.user = {};
}, function(error) {
console.log("Error:", error);
});
Save the above changes and refresh the app. Drag the marker to a preferred position, enter a description and click save. Check the firebase dashboard and the data should be there.
Once the data saves, include an alert to notify the user. Make use of the ionic popup to create it. Inject the $ionicPopup
into the MapCtrl
controller.
.controller('MapCtrl', ['$scope','$firebase','$ionicPopup', function($scope,$firebase,$ionicPopup)
Add a function called showAlert
in the MapCtrl
controller.
$scope.showAlert = function() {
$ionicPopup.alert({
title: 'iMapApp',
template: 'Your location has been saved!!'
});
};
The showAlert
function will call the $ionicPopup
service to show a pop up with a title and template. Call showAlert
in the success callback of push API call.
fb.$push({
latitude: lat,
longitude: lgt,
description: des
}).then(function(ref) {
$scope.user = {};
$scope.showAlert();
}, function(error) {
console.log("Error:", error);
});
Save the changes and try to save the details again. Once the details are saved in firebase there will be a pop up with a success message.
Conclusion
In this tutorial, I demonstrated how to use Google Maps in an IONIC mobile app, specifically to create a custom directive to integrate Google Maps. For an in depth info on using IONIC framework, I would recommend reading the official docs or further IONIC tutorials on SitePoint.
Please add your thoughts, suggestions and corrections in the comments below.