Codementor Events

An Angular Meteor & Ionic Framework Basic Accounts Demo

Published Dec 30, 2015Last updated Mar 07, 2017
An Angular Meteor & Ionic Framework Basic Accounts Demo

This tutorial shows the integration of angular-meteor with the Ionic Framework for creating user accounts and logging in with them. This can be the start of your full-stack, cross-platform mobile solution

Create a Blank Ionic Project

ionic start meteorIonic2 blank

Now let's modify the index.html to support the views we will be creating. Replace the entire body tag with the code below. This is where the templates identified in the ui-router will be rendered based on the specified route.

<body ng-app="starter">
  <ion-nav-view></ion-nav-view>
</body>

Create Login/Create Account Page

We are going to keep this simple with a basic login screen and a basic profile screen. The focus here is really on the integration with meteor and less on the specifics of ionic and html templates in AngularJS.

Create a new directory called views

Create a new file in the views directory named login.html; add the following code

<ion-view title="Login User" hide-back-button=true>
  <ion-content class="padding">
    <ion-item class="card padding">
      <div class="item-text-wrap padding">
        Enter User Name and Password to Login to Application
      </div>

      <label class="item item-input">
        <input type="text" ng-model="credentials.username" placeholder="UserName" />
      </label>
      <label class="item item-input">
        <input type="password" ng-model="credentials.password" placeholder="Password" />
      </label>
      <div class="padding">
        <button class="button button-block" ng-click="doLoginAction()">Login</button>
        <button class="button button-block" ng-click="doCreateAccountAction()">Create Account</button>
      </div>
    </ion-item>
  </ion-content>
</ion-view>

Once again for simplicity we are create and account unsing just a username and password. This way we can use just one html page and one controller to support the effort.

We have created the $scope object for holding the credentials and included functions to be called when the user clicks on the login or create account button. doLoginAction and doCreateAccountAction. You will see the code for those functions in the LoginCtrl which will be created in an upcoming section.

Create User Information Page

The home/user information page will be visible when the user is logged in only. It will show the user information and have a logout button for the user to end the current session

<!-- HTML FOR home template -->
<ion-view title="Ionic Meteor Sample 1" hide-back-button=true>
  <ion-nav-bar>
  <ion-nav-buttons side="secondary">
    <button class="button" ng-click="doLogoutAction()">
      Logout
    </button>
  </ion-nav-buttons>
</ion-nav-bar>
  <ion-content class="padding">
    <ion-item class="card">
      <div class="item-text-wrap padding">
        You Are Logged Into the Application
      </div>
    </ion-item>
  </ion-content>
</ion-view>

We have created the $scope object for holding the credentials and included functions to be called when the user clicks on the logout button, doLogoutAction.You will see the code for this function in the HomeCtrl which will be created in an upcoming section.

Create Basic Routing

Inside of the app.js file add the config block for routing. We have two states here, one for the login page and one for the home page. At this point there is no authentication just specific routes to access.

  .config(function($stateProvider, $urlRouterProvider) {

      // For any unmatched url, send to /login
      $urlRouterProvider.otherwise("/login");

      $stateProvider
        .state('login', {
          url: '/login',
          // loaded into ui-view of parent's template
          templateUrl: 'views/login.html',
          controller : 'LoginCtrl'
        })
        .state('home', {
          url: '/home',
          // loaded into ui-view of parent's template
          templateUrl: 'home.html',
          controller: 'HomeCtrl'
        })
    });

Create the Controllers

We will create a new file called controllers.js, in the file add the code for the two controllers specified above. To verify the application flow from the click events, we will make the buttons call the appropriate functions and alert the users to the behavior.

angular
  .module('starter')
  .controller('LoginCtrl', LoginCtrl)
  .controller('HomeCtrl', HomeCtrl);

function LoginCtrl($scope, $state) {

  $scope.doLoginAction = function($scope) {
    alert("in doLoginAction");
    setTimeout(function() {
      $state.go('home');
    }, 1000);
  }

  $scope.doCreateAccountAction = function() {
    alert("in doCreateAccountAction");
    setTimeout(function() {
      $state.go('home');
    }, 1000);
  }
}

function HomeCtrl($scope, $state) {
  $scope.doLogoutAction = function() {
    alert("in doLogoutAction");
    setTimeout(function() {
      $state.go('login');
    }, 1000);
  }

}

We have added simple alerts to indicate the appropriate function was called with a slight delay before transitioning to the correct state.

Now let's modify the index.html to support the controllers we just added. Include the new script tag to add the controllers to the application.

  <!-- your app's js -->
  <script src="js/app.js"></script>
  <script src="js/controllers.js"></script> // <== ADD THIS LINE

At this point the application should run and show the flow between the states. The next steps will start to add the Meteor user functionality to the application.

Create the Meteor Part of Application

Add the required meteor bower packages

bower install meteor-client-side angular-meteor accounts-base-client-side accounts-password-client-side --save-dev

Make the edits to the index.html to include the javascript library files from the angular-meteor and to support the accounts package for creating user accounts and logging into an application

  <!-- angular-meteor stuff -->
  <script src="lib/meteor-client-side/meteor-runtime-config.js"></script>
  <script src="lib/meteor-client-side/dist/meteor-client-side.bundle.min.js"></script>
  <script src="lib/angular-meteor/dist/angular-meteor.bundle.min.js"></script>
  <script src="lib/accounts-base-client-side/dist/accounts-base-client-side.bundle.js"></script>
  <script src="lib/accounts-password-client-side/dist/accounts-password-client-side.bundle.min.js"></script>

Create the Meteor Server

create the meteor server directory by running the command in the project root directory

meteor create server
cd server
del server.*

Changes to the Client Application

Add the directive to app.js

angular.module('starter', ['ionic','angular-meteor'])

Now let's inject the $meteor object into the controllers, add to start of LoginCtrl function

  LoginCtrl.$inject = ['$scope', '$state', '$meteor'];

then add to start of HomeCtrl function

  HomeCtrl.$inject = ['$scope', '$state', '$meteor'];

Load All of the Meteor Client Side Packages

Change back to the server directory and run the following command

meteor add accounts-base accounts-password

Now the supported packages are added to your server, you can restart your server by running the meteor command

meteor

Revisiting the Router for Authentication Checks

What we will need to do is check for a valid user object when attempting to access a specified route. We can use the routers resolve functionality here to check if a user exists. If a user does not exist, a stateChangeError is thrown and we can redirect to the login state.

Add the user check on the route using the requireUser method from the angular-meteor API. Change the home state to look like this

.state('home', {
  url: '/home',
  // loaded into ui-view of parent's template
  templateUrl: 'views/home.html',
  controller: 'HomeCtrl',
  resolve: {
    "currentUser": function($meteor) {
      return $meteor.requireUser();
    }
  }
})

When this code is ran, if there is no logged in user, the $meteor.requireUser() call will reject the promise and the stateChangeError will be thrown. We can catch the error by adding the following code to the run function in app.js.

First update the function to include the $rootScope

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

Then add the listener for the $stateChangeError

// checking for errors in state change
$rootScope.$on('$stateChangeError',
  function(event, toState, toParams, fromState, fromParams, error) {
    // We can catch when the $requireUser promise is rejected and redirect to login state
    if (error === 'AUTH_REQUIRED') {
      event.preventDefault();
      console.log("no user");
      $state.go('login');
    }
  });

This should force the user to the login state when there is no user; now lets use meteor and the accounts package to add a user.
Meteor Documentation on creating a user

The source code to add to the doCreateAccountAction in the LoginCtrl will call the meteor function to create a user and then redirect back to the home state. If this all worked out fine, a user should be present and the home state should be rendered.

  $scope.doCreateAccountAction = function() {
    alert("in doCreateAccountAction");
    $meteor.createUser({
      username: $scope.credentials.username,
      email: $scope.credentials.username,
      password: $scope.credentials.password,
      profile: {
        createdOn: new Date()
      }
    }).then(function(_response) {
      console.log('doCreateAccountAction success');
      alert("user created: " + $scope.credentials.username );
      $state.go('home');
    }, function(_error) {
      console.log('Login error - ', _error);
      alert("Error: " + _error.reason);
    });
    return false;
  }

Now to login a created user, let's add the following source code to the doLoginAction in the LoginCtrl will call the meteor function to login a user and then redirect back to the home state. If this all worked out fine, a user should be present and the home state should be rendered.

Meteor documentation for logging in user with password

$scope.doLoginAction = function() {
  $meteor.loginWithPassword($scope.credentials.username, $scope.credentials.password)
    .then(function() {
      console.log('Login success ');
      alert("logged in: " + $scope.credentials.username);
      $state.go('home');
    }, function(_error) {
      console.log('Login error - ', _error);
      alert("Error: " + _error.reason);
    });
  return false;
}

Let's update the home.html to show the information on the user that is created or logged in

<!-- HTML FOR home template -->
<ion-view title="Ionic Meteor Sample 1" hide-back-button=true>
  <ion-nav-bar>
    <ion-nav-buttons side="secondary">
      <button class="button" ng-click="doLogoutAction()">
        Logout
      </button>
    </ion-nav-buttons>
  </ion-nav-bar>
  <ion-content class="padding">
    <ion-item class="card">
      <div class="item-text-wrap padding">
        <h2>You Are Logged Into the Application:</h2>
        <p>
          <!-- the currentUser is added to the $rootScope -->
          <pre style="font-size:smaller"> {{currentUser | json}} </pre>
        </p>
      </div>
    </ion-item>
  </ion-content>
</ion-view>

Now that we have meteor integrated and we can login users, let's finish up the logout function for the HomeCtrl.

Meteor Documentation on Logout

Here is the code to add to the HomeCtrl in the file controllers.js

  $scope.doLogoutAction = function() {
    alert("in doLogoutAction");
    $meteor.logout().then(function(_response) {
      $state.go('login');
    });
  };
Discover and read more posts from Aaron Saunders
get started
post comments2Replies
ZiLang
9 years ago

After I run meteor server the socket error gone. but the meteor-client-side error is still there

ZiLang
9 years ago

I got the following error message after I followed your steps and run “ionic serve”:

> Failed to load resource: net::ERR_CONNECTION_REFUSED

> http://localhost:3000/sockjs/info?cb=c0mal5hf4x Failed to load resource: net::ERR_CONNECTION_REFUSED
> http://localhost:3000/sockjs/info?cb=_lxvwtvg8v Failed to load resource: net::ERR_CONNECTION_REFUSED

> angular-meteor.bundle.min.js:6 [angular-meteor.$rootScope.currentUser/loggingIn] Please note that this functionality has migrated to a separate package and will be deprecated in 1.4.0. For more info: http://www.angular-meteor.c…

> angular-meteor.bundle.min.js:6 Uncaught Error: argument 2 must be an object

Could it be angular version conflict? Do you have source code in github? Thanks.