Ionic Login/Signup using Firebase : Part 1

This is my first tutorial in the long tutorial series on “Getting started with Firebase & Ionic“. I have integrated crosswalk and Ionic Materiel, to bring a significant improvement in performance and UI (Will discuss in detail in the upcoming tutorials) . Please make sure to read the complete series and follow me on git for the latest updates.

So lets get started.

Firebase:

  • Firebase is a real-time database service, i.e your modification are updated in real time and you don’t need to refresh a page, to see the changes.
  • Firebase uses NoSQL. So leave aside some of the old school SQL concepts.
  • Firebase is not suitable for all occasions. Here I have used it basically for the reason that, I need a Real time Cart  and a Real time Order Tracking.

Index: 

index.html [The container]  

This is the main container that holds Sliding Drawer and Navigation Bar. We need to hide some of this segments before login. You can find the respective sections in the below code.
#1. The custom headers for Ionic Material and Firebase
#2. To obtain your Firebase configuration part follow this.
#3. $root.extras is used for hiding user features, ie cart icon, menu drawer, navigation icon etc.
We initialize $root.extras to false in the app.js

.run(function($ionicPlatform,$rootScope) {
$rootScope.extras = false;
$ionicPlatform.ready(function() {

#4.get_total() returns the total number of cart items.
#5.In-order to access $root.extras and get_total(), we have added an additional controller i.e indexCtrl. Currently we wont be looking into it, as it will only come after login.
































Menu Offers My Cart Orders Favourite Settings Support Logout

 

Login:
login.html [The login page]

Find following sections in the below code
#1. style attribute for adding full screen background
#2. Will be implementing FB login in a while.

 



FORGOT PASSWORD

controllers.js [ loginCtrl ]
/*
ionicSideMenuDelegate : used to access the slidable functionality of the menu drawer 
*/
.controller('loginCtrl', function($scope,$rootScope,$ionicHistory,sharedUtils,$state,$ionicSideMenuDelegate) {
$rootScope.extras = false;  // For hiding the side bar and nav icon
// When the user logs out and reaches login page,
// we clear all the history and cache to prevent back link
$scope.$on('$ionicView.enter', function(ev) {
if(ev.targetScope !== $scope){
$ionicHistory.clearHistory();
$ionicHistory.clearCache();
}
});
//Check if user already logged in
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
//Removes back link to login page
$ionicHistory.nextViewOptions({
historyRoot: true
});
$ionicSideMenuDelegate.canDragContent(true);  // Sets up the sideMenu dragable
$rootScope.extras = true;
sharedUtils.hideLoading();
$state.go('menu2', {}, {location: "replace"});
}
});
$scope.loginEmail = function(formName,cred) {
if(formName.$valid) {  // Check if the form data is valid or not
sharedUtils.showLoading(); //starts the loading popup
//Email Login via Firebase
firebase.auth().signInWithEmailAndPassword(cred.email,cred.password).then(function(result) {
// You dont need to save the users session in your local session or cookies. Firebase handles it.
// You only need to :
// 1. clear the login page history from the history stack so that you cant come back
// 2. Set rootScope.extra;
// 3. Turn off the loading
// 4. Got to menu page
$ionicHistory.nextViewOptions({
historyRoot: true   //1
});
$rootScope.extras = true;  //2
sharedUtils.hideLoading();  //3
$state.go('menu2', {}, {location: "replace"}); //4
},
function(error) {
sharedUtils.hideLoading();
sharedUtils.showAlert("Please note","Authentication Error");
}
);
}else{
sharedUtils.showAlert("Please note","Entered data is not valid");
}
};
$scope.loginFb = function(){
//Facebook Login
};
$scope.loginGmail = function(){
//Gmail Login
};
})

services.js [ sharedUtils and fireBaseData ]

sharedUtils : Contains commonly used functions like show_loading_popup, show_alert etc.
fireBaseDate : Contains the Reference URL of all the tables in your firebase database.
.factory('fireBaseData', function($firebase) {
var ref = new Firebase("https://projectId.firebaseio.com/"),
refCart = new Firebase("https://projectId.firebaseio.com/cart"),
refUser = new Firebase("https://projectId.firebaseio.com/users"),
refCategory = new Firebase("https://projectId.firebaseio.com/category"),
refOrder = new Firebase("https://projectId.firebaseio.com/orders"),
refFeatured = new Firebase("https://projectId.firebaseio.com/featured"),
refMenu = new Firebase("https://projectId.firebaseio.com/menu");
return {
ref: function() {
return ref;
},
refCart: function() {
return refCart;
},
refUser: function() {
return refUser;
},
refCategory: function() {
return refCategory;
},
refOrder: function() {
return refOrder;
},
refFeatured: function() {
return refFeatured;
},
refMenu: function() {
return refMenu;
}
}
})
.factory('sharedUtils',['$ionicLoading','$ionicPopup', function($ionicLoading,$ionicPopup){
var functionObj={};
functionObj.showLoading=function(){
$ionicLoading.show({
content: ' ', // The text to display in the loading indicator
animation: 'fade-in', // The animation to use
showBackdrop: true, // Will a dark overlay or backdrop cover the entire view
maxWidth: 200, // The maximum width of the loading indicator. Text will be wrapped if longer than maxWidth
showDelay: 0 // The delay in showing the indicator
});
};
functionObj.hideLoading=function(){
$ionicLoading.hide();
};
functionObj.showAlert = function(title,message) {
var alertPopup = $ionicPopup.alert({
title: title,
template: message
});
};
return functionObj;
}])

Register :

signup.html [The Signup page]  





controllers.js [The Signup page]
.controller('signupCtrl', function($scope,$rootScope,sharedUtils,$ionicSideMenuDelegate,
$state,fireBaseData,$ionicHistory) {
$rootScope.extras = false; // For hiding the side bar and nav icon
$scope.signupEmail = function (formName, cred) {
if (formName.$valid) {  // Check if the form data is valid or not
sharedUtils.showLoading();
//Main Firebase Authentication part
firebase.auth().createUserWithEmailAndPassword(cred.email, cred.password).then(function (result) {
//Add name and default dp to the Autherisation table
result.updateProfile({
displayName: cred.name,
photoURL: "default_dp"
}).then(function() {}, function(error) {});
//We create a user table to store users additional information.Here, telephone
//Add phone number to the user table
fireBaseData.refUser().child(result.uid).set({
telephone: cred.phone
});
//Registered OK
$ionicHistory.nextViewOptions({
historyRoot: true
});
$ionicSideMenuDelegate.canDragContent(true);  // Sets up the sideMenu dragable
$rootScope.extras = true;
sharedUtils.hideLoading();
$state.go('menu2', {}, {location: "replace"});
}, function (error) {
sharedUtils.hideLoading();
sharedUtils.showAlert("Please note","Sign up Error");
});
}else{
sharedUtils.showAlert("Please note","Entered data is not valid");
}
}
})

Profile :

settings.html [The Profile Page] 





{{user_info.displayName}}

{{item.nickname}}
{{item.address}} Pin: {{item.pin}} Tel: {{item.phone}}
controllers.js [ settingsCtrl ]
/*
firebaseArray: for accessing synchronized array of objects
firebaseObject: for accessing single synchronized object 
*/
.controller('settingsCtrl', function($scope,$rootScope,fireBaseData,$firebaseObject,
$ionicPopup,$state,$window,$firebaseArray,
sharedUtils) {
//Bugs are most prevailing here
$rootScope.extras=true;
//Shows loading bar
sharedUtils.showLoading();
//Check if user already logged in
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
//Accessing an array of objects using firebaseObject, does not give you the $id , so use firebase array to get $id
$scope.addresses= $firebaseArray(fireBaseData.refUser().child(user.uid).child("address"));
// firebaseObject is good for accessing single objects for eg:- telephone. Don't use it for array of objects
$scope.user_extras= $firebaseObject(fireBaseData.refUser().child(user.uid));
$scope.user_info=user; //Saves data to user_info
//NOTE: $scope.user_info is not writable ie you can't use it inside ng-model of 
//You have to create a local variable for storing emails
$scope.data_editable={};
$scope.data_editable.email=$scope.user_info.email;  // For editing store it in local variable
$scope.data_editable.password="";
$scope.$apply();
sharedUtils.hideLoading();
}
});
//Function 1
$scope.addManipulation = function(edit_val) {  // Takes care of address add and edit ie Address Manipulator
if(edit_val!=null) {
$scope.data = edit_val; // For editing address
var title="Edit Address";
var sub_title="Edit your address";
}
else {
$scope.data = {};    // For adding new address
var title="Add Address";
var sub_title="Add your new address";
}
// An elaborate, custom popup
var addressPopup = $ionicPopup.show({
template: ' 
 ' +
' 
 ' +
' 
 ' +
'',
title: title,
subTitle: sub_title,
scope: $scope,
buttons: [
{ text: 'Close' },
{
text: 'Save',
type: 'button-positive',
onTap: function(e) {
if (!$scope.data.nickname || !$scope.data.address || !$scope.data.pin || !$scope.data.phone ) {
e.preventDefault(); //don't allow the user to submit unless he enters full details
} else {
return $scope.data;
}
}
}
]
});
addressPopup.then(function(res) {
if(edit_val!=null) {
//Update  address
fireBaseData.refUser().child($scope.user_info.uid).child("address").child(edit_val.$id).update({    // update
nickname: res.nickname,
address: res.address,
pin: res.pin,
phone: res.phone
});
}else{
//Add new address
fireBaseData.refUser().child($scope.user_info.uid).child("address").push({    // push
nickname: res.nickname,
address: res.address,
pin: res.pin,
phone: res.phone
});
}
});
};
// A confirm dialog for deleting address
$scope.deleteAddress = function(del_id) {
var confirmPopup = $ionicPopup.confirm({
title: 'Delete Address',
template: 'Are you sure you want to delete this address',
buttons: [
{ text: 'No' , type: 'button-stable' },
{ text: 'Yes', type: 'button-assertive' , onTap: function(){return del_id;} }
]
});
confirmPopup.then(function(res) {
if(res) {
fireBaseData.refUser().child($scope.user_info.uid).child("address").child(res).remove();
}
});
};
//Function 2
$scope.save= function (extras,editable) {
//1. Edit Telephone doesnt show popup 2. Using extras and editable  // Bugs
if(extras.telephone!="" && extras.telephone!=null ){
//Update  Telephone
fireBaseData.refUser().child($scope.user_info.uid).update({    // set
telephone: extras.telephone
});
}
//Edit Password
if(editable.password!="" && editable.password!=null  ){
//Update Password in UserAuthentication Table
firebase.auth().currentUser.updatePassword(editable.password).then(function(ok) {}, function(error) {});
sharedUtils.showAlert("Account","Password Updated");
}
//Edit Email
if(editable.email!="" && editable.email!=null  && editable.email!=$scope.user_info.email){
//Update Email/Username in UserAuthentication Table
firebase.auth().currentUser.updateEmail(editable.email).then(function(ok) {
$window.location.reload(true);
//sharedUtils.showAlert("Account","Email Updated");
}, function(error) {
sharedUtils.showAlert("ERROR",error);
});
}
};
$scope.cancel=function(){
// Simple Reload
$window.location.reload(true);
console.log("CANCEL");
}
})

Its almost done. Now you only need to configure some database elements.

CONFIGURE DATABASE:
1. Open up your project db.

2. Import the following JSON data to the Foodkart db
{
  "category" : {
    "cat01" : "Burger",
    "cat02" : "Drinks"
  },
  "featured" : {
    "item1" : "slide_1",
    "item2" : "slide_2"
  },
  "menu" : {
    "item1" : {
      "available" : true,
      "category" : "cat01",
      "description" : "item1 description",
      "image" : "chicken_maharaja",
      "name" : "New Chicken Maharaja",
      "price" : 130,
      "stock" : 100
    },
    "item2" : {
      "available" : true,
      "category" : "cat01",
      "description" : "item2 description",
      "image" : "big_spicy_chicken_wrap",
      "name" : "Big Spicy Chicken Wrap",
      "price" : 120,
      "stock" : 100
    },
    "item3" : {
      "available" : true,
      "category" : "cat02",
      "description" : "item3 description",
      "image" : "thumsup",
      "name" : "Thumsup 100ml",
      "price" : 40,
      "stock" : 100
    },
    "item4" : {
      "available" : true,
      "category" : "cat02",
      "description" : "item4 description",
      "image" : "mccafe_ice_coffee",
      "name" : "Ice Coffee",
      "price" : 140,
      "stock" : 100
    },
    "item5" : {
      "available" : true,
      "category" : "cat01",
      "description" : "item5 description",
      "image" : "mc_chicken",
      "name" : "MC Chicken",
      "price" : 190,
      "stock" : 100
    },
    "item6" : {
      "available" : true,
      "category" : "cat02",
      "description" : "item6 description",
      "image" : "Smoothie",
      "name" : "Smoothie",
      "price" : 70,
      "stock" : 100
    },
    "item8" : {
      "available" : true,
      "category" : "cat01",
      "description" : "item8 description",
      "image" : "salad_wrap",
      "name" : "Salad Wrap",
      "price" : 150,
      "stock" : 100
    }
  }
}

Note: Rest of the databases are automatically created, when making the database calls.
3. Add the following permissions to the database rules .
{
"rules": {
".read": true,
".write": true
}
Note: When you are publishing the app, make sure to update these permissions to a more constrained version. This concludes the part for Login, Register and Profile Setting for FoodKart using Firebase.
Share on Google Plus

About admin

0 komentar:

Posting Komentar

Terimakasih Sudah Berkomentar dengan Sopan