August 15, 2014

How to Set Up HelloJS in Ionic Framework with Angular UI Router

UPDATE: I have written another article for Facebook Login with Ionic and Angular UI Router, and integrates with Parse.com as the backend. I believe it is more complete and more useful than this article. Please see How to set up Facebook Connect Plugin and Parse.com in Ionic / Phonegap.

I found HelloJS when I was looking for an easy way to implement Facebook login in Ionic for my Phonegap app. It was really easy to get it working despite my lack of experience with Ionic or Angularjs in general.

HelloJS takes all the heavy lifting away from us and helps us integrate our app easily with most of the popular social login platforms. On top of that, it also comes with some handy api functions to retrieve data from these social platforms, so I really recommend it to everyone out there.

Here, I am going to demonstrate on how you too can get it running within minutes in Ionic by using HelloJS with Angular UI Router.

If you would like to delve straight into the codes, you can have a look at the sample app at Github. I have purposely structured the commits in a step-by-step way for clarity.

Otherwise, let’s get started...


Start an Ionic App

Obvious first step. Let’s start an Ionic App called myapp. Type these into terminal:

$ ionic start myapp blank

Let’s tentatively add iOS support to our Ionic App:

$ ionic platform add ios

Set up the routes for Angular UI Router

We are going to set up 2 states in Angular UI Router. A state is the equivalent of a route for Angular UI Router. What we want to add here is a state for user to login, and a state that only logged in users can see. And if the user enters a unspecified state, he should be redirected to the login state.

For that we have /login for the login page, and the root path / for the logged in users. Add the following codes to the bottom of app.js at this point.

.config(function($stateProvider, $urlRouterProvider){
  $stateProvider
    .state('home', {
      url: '/',
      templateUrl: 'home.html',
      controller: 'homeCtrl',
      data: {
        authenticate: true
      }
    })
    .state('login', {
      url: '/login',
      templateUrl: 'login.html',
      controller: 'loginCtrl',
      data: {
        authenticate: false
      }
    });

  // Send to login if the URL was not found
  $urlRouterProvider.otherwise('/login');
})

You can see that we added 2 states named home and login respectively here with the following parameters:

Note that by default, ionic.bundle.js includes angular-ui-router.js. There is no need to include angular-ui-router.js as well as adding the angular-ui-router module into myapp.


Add the controllers

We have specified homeCtrl and loginCtrl to handle our states respectively. We need to add these into app.js as well. Add the following code at the bottom of app.js:

.controller('homeCtrl', function() {
})

.controller('loginCtrl', function() {
})

Add the views

Create a new file called home.html and add the following codes:

<ion-header-bar class="bar-stable">
  <h1 class="title">MyApp Home</h1>
</ion-header-bar>
<ion-content>
    <div style="padding: 10px;">
        Welcome to MyApp, {{user.name}}!
    <button class="button button-block button-positive">
      Log out from Facebook
    </button>
  </div>
</ion-content>

Create a new file called login.html and add the following codes:

<ion-header-bar class="bar-stable">
  <h1 class="title">MyApp Login</h1>
</ion-header-bar>
<ion-content>
  <div style="padding: 10px; height: 100%;">
    <button class="button button-block button-positive">
      Log in with Facebook
    </button>
  </div>
</ion-content>

Change index.html to work with Angular UI Router

Now for these views to work, we need to replace the following codes in index.html:

<ion-header-bar class="bar-stable">
	<h1 class="title">Ionic Blank Starter</h1>
</ion-header-bar>
<ion-content>
</ion-content>

with:

<div ui-view></div>

Basically what this does is that, UI Router would replace <div ui-view></div> with the content as loaded from the views.


Test Your Routes

By now, the routes should be working. Start the Ionic server in terminal:

$ ionic serve

This should automatically open a tab in your browser for http://localhost:8100/#/login. This would be the login state as defined earlier on. You can access the home state via http://localhost:8100/#/.

The reason why we ended up with login state is because UI Router does not recognize http://localhost:8100/, and such redirected to the login state as defined in our configurations in app.js earlier on at this line.

// Send to login if the URL was not found
$urlRouterProvider.otherwise('/login');

Note that Angular UI Router’s URL usually comes with a /#/ in between.


Download and include HelloJS

Go to http://adodson.com/hello.js/#install and download the hello.js.

Move hello.all.js or hello.all.min.js to myapp/www/js/hello.js.

Then include the javascript file into index.html just before app.js:

<!-- third party js -->
<script src="js/hello.js"></script>

<!-- your app's js -->
<script src="js/app.js"></script>

Add authentication checking to Ionic

Go into app.js, look for this line:

.run(function($ionicPlatform) {

Replace it with these:

.run(function($ionicPlatform, $rootScope, $state, userService) {

Here we are injecting $rootScope from AngularJS, $state from Angular UI Router, and userService which would be created by us later.

Next, let’s add the authentication checking to our .run() method.

.run(function($ionicPlatform, $rootScope, $state, userService) {
  $ionicPlatform.ready(function() {
    ...
  });

  // Add these
  // UI Router Authentication Check
  $rootScope.$on("$stateChangeStart", function(event, toState, toParams, fromState, fromParams){
    if (toState.data.authenticate && !userService.isLoggedIn()){
      // User isn’t authenticated
      $state.transitionTo("login");
      event.preventDefault();
    }
  });
})

So what we are checking here are 2 conditions. First, we see if the toState, aka the state we will be going to has data.authenticate defined as true. If it is true, then we need to check if user is logged in via userService.isLoggedIn() method. If user is not logged in, we will redirect him to the login state.


Add HelloJS to Ionic

Go to app.js and add the following codes:

.factory('userService', ['$rootScope', '$state', function($rootScope, $state) {

  // Hello.js Functions
  hello.init({
      // Change this to your own facebook app id
    facebook : '1234567890'
  }, {
      redirect_uri:'https://www.facebook.com/connect/login_success.html'
  });

  var service = {
    isLoggedIn: function() {
      return $rootScope.userStatus;
    },
    login: function() {
      hello('facebook').login( function() {
        hello( 'facebook' ).api( '/me' ).success(function(json) {
          console.log(json);
          $rootScope.user = json;
          $rootScope.$apply($rootScope.user);
          $rootScope.userStatus = true;

          $state.go('home');
        });
      });
    },
    logout: function() {
      hello('facebook').logout( function() {
        $rootScope.userStatus = false;
        $rootScope.user = null;

        $state.go('login');
      });
    }
  }

  return service;
}])

In-depth look into the codes (optional)

Let’s de-construct the codes and see what we have added. If you are not interested in explanation, feel free to skip this and move onto the next section.

First we are adding a factory method called userService that is called in our .run() method as part of authentication checking. It has access to $rootScope so that we can access user details everywhere in our app once user is authenticated, and $state so that we can redirect user with UI Router once user is authenticated.

.factory('userService', ['$rootScope', '$state', function($rootScope, $state) {

Next, we initialize HelloJS with our Facebook App Id. This is required so that HelloJS knows which Facebook App it should shake hand with. If you do not yet have your Facebook App created yet, go to Facebook Developers > Apps to create one for free.

hello.init({
  // Change this to your own facebook app id
  facebook : '354940007993444'
});

Then we have a service{} object that will be returned each time userService() is called. Within service{}, we have 3 methods defined. isLoggedIn is a method that checks if user is logged in as the name suggests. login is a method to register user information to our system once user is authenticated, and logout is to clear user credentials from our system when user logs out.

var service = {
  isLoggedIn: function() {},
  login: function() {},
  logout: function() {}
}

return service;

Taking a closer look into the methods within service{}, we can see that isLoggedIn() is a very simple method that returns the $rootScope.userStatus variable whenever it is called. So where is this variable defined?

isLoggedIn: function() {
  return $rootScope.userStatus;
},

A quick look at login() will reveal the answer. But $rootScope.userStatus is only set after we do a few things.

First, when the login() method is called, we will use methods provided by HelloJS, hello().login() to log in the user to Facebook.

When the login is successful, we would like more details about the user such as his name, email etc right? That’s when we call the hello().api('/me').success() method. User information will be returned in a json format, and we will promptly save it to our $rootScope.user. At the same time, we will notify all the view files that are using $rootScope.user that there is an update, and that they should display the latest value.

Then we set $rootScope.userStatus to true so that our isLoggedIn() method would return the correct value to UI Router for authentication checking.

Finally, when everything is done, we redirect user to the home state.

login: function() {
  hello('facebook').login( function() {
    hello( 'facebook' ).api( '/me' ).success(function(json) {
      console.log(json);
      $rootScope.user = json;
      $rootScope.$apply($rootScope.user);
      $rootScope.userStatus = true;

      $state.go('home');
    });
  });
},

And now to the final service{} method, logout(). Again, we utilize a HelloJS method, hello().logout() to end user’s Facebook session in our app. At the same time, we need to make sure that $rootScope.userStatus is set to false so that our authentication checking will function correctly, and that user will be redirected based on the right condition.

Finally, we also reset user’s credentials in our app by clearing off the data in $rootScope.user, the variable which we used to hold all user’s information.

logout: function() {
  hello('facebook').logout( function() {
    $rootScope.userStatus = false;
    $rootScope.user = null;

     $state.go('login');
  });
}

And the last missing piece

Now that we have all the back-end codes in place, all we still need to do is tie the back-end codes to our front-end view.

To do this, first let’s add a ng-click parameter to our buttons at login.html and home.html.

In login.html:

<button class="button button-block button-positive" ng-click="login()">

In home.html:

<button class="button button-block button-positive" ng-click="logout()">

For the login() and logout() functions to work, we will have to add them in our controllers. So let’s do that in app.js. Your new controllers should now be replaced and look like these:

.controller('homeCtrl', ['$scope', 'userService',function($scope, userService) {
  $scope.logout = userService.logout;
}])

.controller('loginCtrl', ['$scope', 'userService', function($scope, userService) {
  $scope.login = userService.login;
}]);

What we have here is that we injected $scope and userService into our controllers. This is so that when the buttons in our views are clicked/tapped on, our controllers would translate that into calling the methods in userService and perform all the relevant authentication actions.


Final verdict

Now let’s test that the authentication is in fact working. First, make sure that ionic serve is still running in your terminal.

Then go to your browser and open http://localhost:8100/. You should be redirected to the login state at http://localhost:8100/#/login since you have not authenticated yourself.

Now login at the login state. Does it redirect you to the home state and showing you the right page? If it does, congrats!

Finally, click the logout button. You should be redirected back to the login state. Now try to go back to http://localhost:8100 again and see if our authentication checking system still work.

With that, you now have a functioning authenticating system that utilizes Facebook Login, by using HelloJS together with Angular UI Router in your Ionic app!

If you are starting on a new Ionic app, feel free to fork or clone my codes in Github as your base app to get started with. May save some time. 🙂

***

Hello! My name is Jian Jye and I work on Laravel projects as my main job. If my article was helpful to you, a shoutout on Twitter would be awesome! I'm also available for hire if you need any help with Laravel. Contact me.