Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Ionic and Telerik Backend Services: A Healthy Partnership

DZone's Guide to

Ionic and Telerik Backend Services: A Healthy Partnership

· Java Zone
Free Resource

The single app analytics solutions to take your web and mobile apps to the next level.  Try today!  Brought to you in partnership with CA Technologies

Written by Jen Looper for Telerik blog.



Building apps using hybrid mobile technologies has enabled developers to deploy beautiful products in record time. Recently I completed an entire app from sketch to launch in one week in order to catch holiday shoppers. Scaffolding a great UI using nice frontend development tools makes things easy from a visual perspective, but the real meat of an app is its data, so we need a quick way to create a frontend and a solid way to hook it up to a reliable backend. For my recent app project and for this tutorial, I used Ionic and Telerik’s Backend Services, which is part of the Telerik Platform and provides an extremely convenient data management solution tailor-made for the developer who needs speed, accuracy, security, and ease-of-deployment.

In this tutorial series, we’re going to create a project just in time to give back for the holidays – an app for use by a municipal food pantry or food bank.

Stay tuned later in the series for an announcement about this project! Spoiler alert: there’s a prize involved!

I’m going to call this app “Pntry” (because removing vowels makes things cool). The use case is to connect the needs of local food banks with donors and clients so that the food banks can more efficiently serve the needs of their clients and donors can learn what is needed and customize their donations.

This is a pretty involved project, so I’m going to break up these tutorials into a three-part series:

  1. In Part 1, we’ll get our work environment set up and complete the login and registration screens.
  2. In Part 2, we’ll create the the food bank’s forms via which they request donations of specific food items.
  3. In Part 3, we’ll make the app role-based, creating areas for use by a food bank and areas for use by a donor.

Let’s get started!

Note: For now, I’m using the Ionic CLI tools to scaffold an app locally, but in the next tutorial we’re going to port our codebase into Telerik’s Platform. This will speed up development even more and give access to the excellent suite of developer tools offered by the Platform. For now, however, we’re going to build locally to get our workflow set up.

It’s a snap to scaffold out a basic Ionic app and quickly skin it. Follow these directions to install Ionic. From the command-line, ensure that you are in the directory you want to work in and create an app by typing:

$ ionic start pntry sidemenu

This command kicks of Ionic’s scaffolding process:

scaffolding an Ionic app

Change directories to the pntry directory that was just created:

$cd pntry

…and type:

$ionic serve

If you have installed all of Ionic’s dependencies correctly, a browser window opens showing a basic app with some stubbed-out data:

a blank Ionic app

We’re going to alter the app so as to build something like this:

wireframes for Pntry

I have stripped down this Ionic project to the bare bones and created a basic app which is available to you in GitHub. This is just a skeleton app with a few colors and most of the pages we want to use pre-created and linked up to the side menu, to which I added icons because I’m a little crazy for icons. Download this code and replace the /www folder in the Ionic app you just created with the /www folder in this codebase. Restart Ionic by typing r in the terminal; you should see the new codebase.

Note: If you already have Ionic installed and are familiar with the setup process, you can simply quit any currently running Ionic processes, checkout this base project from github and unzip it, cd to the folder where the code resides, and type $ionic serve

The app should look like this:

closed app

…and with the side menu open:

open app


Structuring the app

Ionic apps are initially scaffolded to include a frontend View, a Controller, and a stubbed-out Model that is hard-coded in the Controller. We need a robust Service layer to handle our data manipulation. To do this, we are going to separate the controller from the data tier by creating a services.js file.

Below is a basic description of the various JavaScript files in the application:

  1. app.js includes the routes that the app needs to know to navigate from page to page
  2. controllers.js contains the functions that allow the user to interact with the frontend such that data will reach the backend
  3. service.js handles the interaction between the app and the backend and returns data back to the frontend where is it displayed without need for a page refresh due to Angular’s two-way data binding. It’s all pretty magical.

Go ahead and add a services.js file to the /www/js folder. We’ll flesh it out as we work through this tutorial. Add a reference to that file in index.html under the reference to controllers.js:

<script src="js/services.js"></script>

Add the following line to the services.js file:

angular.module('pntry.services', [])

Add a reference to this service file on the first line within app.js:

angular.module('pntry', ['ionic', 'pntry.controllers', 'pntry.services'])

Enabling Backend Services

Now it’s time to hook up Telerik Backend Services to our frontend so that we can get these login and registration routines to actually do something. Backend Services is a cloud-based solution to store your app’s data. To access it, we are going to use the JavaScript SDK (though not pertinent to our app, there are plenty of other integrations available – you can learn more by reading the documentation.

Include the Backend Service file in your codebase. The easiest way to do this is to install it via Bower (note that it uses the former name of Backend Services, Everlive):

$bower install everlive

If you don’t have Bower installed, you can install it easily via npm using npm install -g bower

Bower installs files into the bower_components folder in the root. Move the everlive.all.min.js file that is installed by Bower to a new www/lib/everlive folder and reference it in index.html beneath the line referring to ionic.bundle.js:

<script src="lib/everlive/min/everlive.all.min.js"></script>

Go to Telerik’s Platform and login.

Don’t worry if you don’t have a Telerik Platform account; you can try it for free for 30 days.

Create a new workspace named Pntry, and click the green button to Create a Backend Services project.

backend

You are going to start from scratch in this project.

Users

Backend Services comes pre-built with some very nice features, foremost of which, for our purposes, is the Users object, which is like a database table that will be populated by our registered users.

Users

We are going to capture a displayname, username, password, email, and food bank name in our registration form and store this data in the Users object.

When the user successfully fills out the form, in addition, an email is generated automatically from the Backend to welcome the user to the network. The Backend comes preconfigured with four email templates for common registration and password management use cases.

Grab the API key from the API Key area of the Platform.

API Key

Now we can start fleshing out our Service layer. In the services.js file, add our first Factory object and add your own API Key:


angular.module('pntry.services', [])

.factory('API', function() {

  var api_key = 'your-api-key';

    return api_key;

});

Note: best practices dictate that you don’t store your API key in your app, but rather create a restful endpoint from which you can deliver the key…but this is a topic for another tutorial!

Our services layer will contain all the calls to the backend. Other service functions will be able to access that API key by invoking the factory.


Complete the Registration form

While we’re in the Backend Services area, let’s add one more field that we want to capture on registration. This will be the Organization Name so that we can identify the name of the food bank. In the Users area, click on the data structure icon on the right:

data icon

Add an item to the Users table called OrganizationName and click ‘add’ and ‘save’.

add item

Add a field to /www/templates/register.html under the opening card-list div so a new input field appears at the top of the registration form:


<label class="item item-input">
<span class="input-label">Organization Name</span>
<input type="text" name="displayname" ng-model="registerData.organizationname"></label>


Complete the Registration functions

Now, we have to handle the registration form submission. We’ll begin by creating the User factory in services.js under the API factory:


.factory('User', function (API) {

var el = new Everlive({
        apiKey: API,
        url: '//api.everlive.com/v1/',
        scheme: 'https'
     });
  return {
    register: function(registerData){          
        return el.Users.register(
            registerData.username, 
            registerData.password, 
              { 
                Email: registerData.email, 
                DispayName: registerData.displayname,
                OrganizationName: registerData.organizationname
               })
            .then(function (data) {
                return data;
            },
            function(error) {
                return error;
            });               
    }
  }
});

Next, enable controller.js to use that new factory by adding its reference to the top of the that file. We are also using some extra goodies provided by Ionic for UI magic, so make sure the top of controller.js resembles this line:

.controller('AppCtrl', function($state, $scope, $ionicModal, $ionicPopup, User) {...

Including $state allows us to navigate to a new page after registration and login. $ionicPopup is a clean little popup that is useful in displaying registration errors generated from the backend. And User is the name of the factory you created in the services tier. In your controller, reference a factory like User.register in order to pass data back and forth from and to the service.

Finally, flesh out the doRegister() function in the controller:

// Perform the register action when the user submits the registration form
$scope.doRegister = function() {

    User.register($scope.registerData).then(function(data){
      if(data.result){
        //log me in
        $scope.loginData.username = $scope.registerData.username;
        $scope.loginData.password = $scope.registerData.password;
        $scope.doLogin();
        $scope.closeRegister();
        $state.go("app.inventory");
    }
    else{
      $ionicPopup.alert({
        title: status.data.message,
        template: 'Please try again!'
        });
      }
    });
}

Now, you should be able to open the registration form in your app, fill it in, click register, and view your data in Backend Services in the User table.


Building out Login and Authentication

By default, the Ionic project we generated comes prebuilt with a login screen. We are going to add to it a registration screen so that a food pantry can create a profile and own their own data. Most of the app needs to be password-protected, so we are going to store a token that is generated by Backend Services to help with our authentication.

Add a login function in services.js to the User factory beneath the register function:

login: function(loginData) {                
    return el.Users.login(
        loginData.username,
        loginData.password)
        .then(function (data) {
            return data;
        },
        function(error) {
            return error;
        });
},

Now, in the controller, things start to get interesting. We are going to use localStorage to save a token that the Backend Service sends back so that we can reuse it in future interactions.

Overwrite the current doLogin function with this code:

// Perform the login action when the user submits the login form
$scope.doLogin = function() {
    User.login($scope.loginData).then(function(data){
      if(data.result){
        localStorage.setItem("token",data.result.access_token);
        $state.go("app.inventory");
        $scope.loginmodal.hide();
      }
      else{
        $ionicPopup.alert({
            title: data.message,
            template: 'Please try again!'
          });
      }
    });
};

Finally, we can add a test to make sure that we have a token available and that it is valid; if it is not, we will push the user back to the home page; if so, we allow access to a given area. Add this code snippet above the previous code:

$scope.testLoginStatus = function() {

    var token = localStorage.getItem("token");        
     User.me(token).then(function(data){
      console.log(data)
      if(!data.result){
        $ionicPopup.alert({
            title: 'Your session has expired',
            template: 'Please login!'
          });
        //go home
        $state.go("app.home");          
        }
    });    
};

Now we can build out the service to check the User’s status and make sure the authentication token has been set during login:

//am I logged in with a valid token?
me: function(token) {
    return el.Users.currentUser(
        {headers: {'Authorization':'Bearer '+token}})
    .then(function (data) {
        return data;
    },
    function(error) {
        return error;
    });
}

Add the token test on click ( ng-click="testLoginStatus()") to each element of the menu in /www/templates/menu.html:

<ion-list>
    <ion-item nav-clear menu-close ng-click="testLoginStatus()" href="#/app/donationrequests">
      <i class="icon ion-help-buoy"></i> Donation Requests
    </ion-item>
    <ion-item nav-clear menu-close ng-click="testLoginStatus()" href="#/app/requestdonation">
      <i class="icon ion-speakerphone"></i> Request A Donation
    </ion-item>
    <ion-item nav-clear menu-close ng-click="testLoginStatus()" href="#/app/inventory">
     <i class="icon ion-clipboard"></i> Inventory
   </ion-item>
    <ion-item nav-clear menu-close ng-click="testLoginStatus()" href="#/app/needs">
      <i class="icon ion-compose"></i> Current Needs
   </ion-item>
</ion-list>

Reality check

The entire controllers.js file should now look like this:

angular.module('pntry.controllers',[])

.controller('AppCtrl', function($state, $scope, $ionicModal, $ionicPopup, User) {
  // Form data for the login modal
  $scope.loginData = {
    username: null,
    password: null
  };

  $scope.registerData = {
    username: null,
    password: null,
    email: null,
    displayname: null,
    organizationname: null
  };


  // Create the login modal that we will use later
  $ionicModal.fromTemplateUrl('templates/login.html', {
    scope: $scope
  }).then(function(loginmodal) {
    $scope.loginmodal = loginmodal;
  });

  // Create the login modal that we will use later
  $ionicModal.fromTemplateUrl('templates/register.html', {
    scope: $scope
  }).then(function(registermodal) {
    $scope.registermodal = registermodal;
  });

  $scope.testLoginStatus = function() {

    var token = localStorage.getItem("token");        
     User.me(token).then(function(data){
      console.log(data)
      if(!data.result){
        $ionicPopup.alert({
            title: 'Your session has expired',
            template: 'Please login!'
          });
        //go home
        $state.go("app.home");          
        }
    });    
  };

  //open/close routines
  $scope.openLogin = function() {
    $scope.loginmodal.show();
  };
  $scope.closeLogin = function(){
    $scope.loginmodal.hide();
  };
  $scope.openRegister = function() {
    $scope.registermodal.show();
  };
  $scope.closeRegister = function(){
    $scope.registermodal.hide();
  };

  // Perform the login action when the user submits the login form
  $scope.doLogin = function() {
    User.login($scope.loginData).then(function(data){
      if(data.result){
        localStorage.setItem("token",data.result.access_token);
        $state.go("app.inventory");
        $scope.loginmodal.hide();
      }
      else{
        $ionicPopup.alert({
            title: data.message,
            template: 'Please try again!'
          });
        }
    });
  };

  // Perform the register action when the user submits the registration form
  $scope.doRegister = function() {

    User.register($scope.registerData).then(function(data){
      if(data.result){
        //log me in
        $scope.loginData.username = $scope.registerData.username;
        $scope.loginData.password = $scope.registerData.password;
        $scope.doLogin();
        $scope.closeRegister();
        $state.go("app.inventory");
    }
    else{
      $ionicPopup.alert({
        title: status.data.message,
        template: 'Please try again!'
        });
      }
    });
  }
});
```
and the entire services.js file looks like this:
```
angular.module('pntry.services', [])

.factory('API', function() {

  var api_key = 'your-api-key';

    return api_key;


})

.factory('User', function (API) {

var el = new Everlive({
        apiKey: API,
            url: '//api.everlive.com/v1/',
            scheme: 'https'
        });
  return {
    register: function(registerData){          
        return el.Users.register(
            registerData.username, 
            registerData.password, 
              { 
                Email: registerData.email, 
                DispayName: registerData.displayname,
                OrganizationName: registerData.organizationname
               })
            .then(function (data) {
                return data;
            },
            function(error) {
                return error;
            });               
    },
    login: function(loginData) {                
        return el.Users.login(
            loginData.username,
            loginData.password)
            .then(function (data) {
                return data;
            },
            function(error) {
                return error;
            });
    },
    me: function(token) {
        return el.Users.currentUser(
            {headers: {'Authorization':'Bearer '+token}})
            .then(function (data) {
                return data;
            },
            function(error) {
                return error;
            });
    }
  }
});

Hooray!

We have successfully created a frontend for our app with basic security, including register and login routines, connecting an Ionic frontend with Telerik Backend Services. In the process, we created a solid structure for our app with a services layer abstracted away from the controller and view, just like Mom taught us. In the next tutorial, we’ll build the four screens that a food bank can use to request donations and manage inventory.



Header image courtesy of WEBN-TV


CA App Experience Analytics, a whole new level of visibility. Learn more. Brought to you in partnership with CA Technologies.

Topics:

Published at DZone with permission of Burke Holland, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}