AngularJS & Firebase auth ngShow and ngClick delay? - javascript

I'm just starting out with AngularJS and I'm running into an odd problem regarding Firebase authentication. I have a very simple body which shows to current user status (logged in, true or false) and a signIn and signOut option.
When I click the Log-in button the first time, nothing happens. When I click it a second time, the logged in status changes and the ng-show and ng-hide divs switch.
Same problem when clicking sign out.
Why does it only happen after the second click?
index.html:
<div class="container" ng-controller="userCtrl">
<h1>User logged in: {{loggedIn}}</h1>
<div ng-hide="loggedIn">
<div class="form-group">
<label for="email">Email:</label>
<input type="email" class="form-control" name="email" ng-model="user.email" />
</div>
<div class="form-group">
<label for="password">Password:</label>
<input type="password" class="form-control" name="password" ng-model="user.password" />
</div>
<button type="button" class="btn btn-lg btn-success" ng-click="signIn()">Sign in</button>
</div>
<div ng-show="loggedIn"><button type="button" class="btn btn-lg btn-danger" ng-click="signOut()">Sign out</button></div>
</div>
Controller:
var myApp = angular.module("tijdlozespelApp", ["firebase"]);
myApp.controller("userCtrl", function ($scope, $firebaseObject) {
$scope.loggedIn = false;
$scope.signIn = function() {
var email = $scope.user.email;
var password = $scope.user.password;
firebase.auth().signInWithEmailAndPassword(email, password).then(function(user) {
$scope.loggedIn = true;
}).catch(function(error) {
$scope.loggedIn = false;
});
}
$scope.signOut = function() {
firebase.auth().signOut().then(function() {
$scope.loggedIn = false;
});
}
});

Use $scope.$apply(function() {...}) to update your scope data when you use async handlers. Angular have problems with identifing scope changes for async operations.
$scope.$apply(function() {
$scope.loggedIn = true;
});

Related

AngularJS - submit form programmatically after validation

I have recently started working on AngularJS 1.6.
I am trying to submit a form programmatically. The reason is I want to validate a few fields (required field validation). I have spent a lot of efforts (probably 3-4 hours) trying to make this work but none of the existing answers on stack overflow or AngularJS docs seems to be working for me today (strange), hence I am posting this as last resort.
Below is my html
<form method="post" id="loginform" name="loginform" ng-submit="loginUser()" novalidate>
<div>
{{message}}
</div>
<div>
<label>User Name</label>
<input type="text" id="txtUserName" ng-model="user.UserName" name="user.UserName" />
</div>
<div>
<label>Password</label>
<input type="text" id="txtPassword" ng-model="user.Password" name="user.Password" />
</div>
<div>
<input type="submit" id="btnLogin" title="Save" name="btnLogin" value="Login" />
</div>
</form>
My angular code
var demoApp = angular.module('demoApp', []);
demoApp.controller("homeController", ["$scope", "$timeout", function ($scope, $timeout) {
$scope.loginUser = function () {
var form = document.getElementById("loginform");
//var form = $scope.loginform; - tried this here...
//var form = $scope["#loginform"]; tried this
//var form = angular.element(event.target); - tried this...
// tried a lot of other combinations as well...
form.attr("method", "post");
form.attr("action", "Home/Index");
form.append("UserName", $scope.user.UserName);
form.append("Password", $scope.user.Password);
form.append("RememberMe", false);
form.submit();
};
}]);
I keep on getting error 'attr' is not a function.
All I need is submit a form using post method, with values. Just before that I am trying to intercept the submit call and check for validations.
I am open to try any other approach as well. Such as changing the input type from submit to button. Putting the input outside the form. I would be more than happy if validations and submit both can happen any which way. I just want it to post back the values after validating on the client side and then the server will take care of the redirect.
Note: I want the form to do a full postback so that I can get it to redirect to another form. (I know I could use Ajax, but some other day, may be!)
1st of all avoid doing var form = document.getElementById("loginform");. Instead of using form.submit you can use the following code. Do it the angular way cheers :D
$scope.loginUser = function () {
if($scope.loginform.$valid){
user.rememberme=false;
$http({
url: 'Home/Index',
method: "POST",
data: user
})
.then(function(response) {
// success
},
function(response) { // optional
// failed
});
}
};
this is a code to validation if validation not complate button is not enable
<form method="post" id="loginform" name="loginform" ng-submit="loginUser()" novalidate>
<div>
{{message}}
</div>
<div>
<label>User Name</label>
<input type="text" id="txtUserName" required ng-model="user.UserName" name="UserName" />
</div>
<div>
<label>Password</label>
<input type="text" id="txtPassword" ng-model="Password" name="user.Password"required />
</div>
<div>
<input type="submit" ng-disabled="myForm.UserName.$invalid || myForm.Password.$invalid" id="btnLogin" title="Save" name="btnLogin" value="Login" />
</div>
</form>
You should use $scope when trying to access the form, something like $scope.loginform. But......
Take a look at ng-messages. Heres an example using ng-messages with your form:
<form id="loginform" name="loginform" ng-submit="loginUser()">
<div>
{{message}}
</div>
<div>
<label>User Name</label>
<input type="text" id="txtUserName" ng-model="user.UserName" name="user.UserName" required/>
<div class="help-block" ng-messages="loginform.txtUserName.$error" ng-show="loginform.txtUserName.$touched">
<p ng-message="required">Username is required.</p>
</div>
</div>
<div>
<label>Password</label>
<input type="text" id="txtPassword" ng-model="user.Password" name="user.Password" required/>
<div class="help-block" ng-messages="loginform.txtPassword.$error" ng-show="loginform.txtPassword.$touched">
<p ng-message="required">Password is required.</p>
</div>
</div>
<div>
<input type="submit" id="btnLogin" title="Save" name="btnLogin" value="Login" ng-click="loginUser()" />
</div>
</form>
Add ngMessages:
var demoApp = angular.module('demoApp', ['ngMessages']);
demoApp.controller("homeController", ["$scope", "$timeout", function ($scope, $timeout) {
$scope.loginUser = function () {
if($scope.loginform.$valid){
//Code to run before submitting (but not validation checks)
} else{
return false;
}
};
}]);
Don't forget to include ngMessages in your app declaration and include the ngMessages.js script file. Note how you can simply use HTML5 validators.
I found the thing I was looking for. In the end I had to create a directive for validating and then submitting. So I am posting it here as a whole answer.
My HTML
<div ng-controller="homeController" ng-init="construct()">
<form method="post" action="Index" role="form" id="loginform" name="loginform" ng-form-commit novalidate class="ng-pristine ng-invalid ng-invalid-required">
<div class="form-group">
<label for="UserName">User ID</label>
<input autocomplete="off" class="form-control ng-valid ng-touched ng-pristine ng-untouched ng-not-empty"
id="UserName" name="UserName" ng-model="user.UserName" type="text" value=""
ng-change="userNameValidation = user.UserName.length == 0">
<span class="field-validation-error text-danger" ng-show="userNameValidation">The User ID field is required.</span>
</div>
<div class="form-group">
<label for="Password">Password</label>
<input autocomplete="off" class="form-control ng-valid ng-touched ng-pristine ng-untouched ng-not-empty"
id="Password" name="Password" ng-model="user.Password" type="password" value=""
ng-change="passwordValidation = user.Password.length == 0">
<span class="field-validation-error text-danger" ng-show="passwordValidation">The Password field is required.</span>
</div>
<div>
<input type="button" id="btnLogin" title="Login" name="btnLogin" value="Login" ng-click="validateUser(loginform)" />
</div>
</form>
</div>
Look for ng-form-commit on the form element. It is the directive that I created.
My Angular code
var demoApp = angular.module('demoApp', []);
demoApp.factory("commonService", function () {
return {
isNullOrEmptyOrUndefined: function (value) {
return !value;
}
};
});
//This is the directive that helps posting the form back...
demoApp.directive("ngFormCommit", [function () {
return {
require: "form",
link: function ($scope, $el, $attr, $form) {
$form.commit = function () {
$el[0].submit();
};
}
};
}]);
demoApp.controller("homeController", ["$scope", "commonService", function ($scope, commonService) {
$scope.construct = function construct() {
$scope.user = { UserName: "", Password: "" };
};
$scope.userNameValidation = false;
$scope.passwordValidation = false;
$scope.isFormValid = false;
$scope.validateUser = function ($form) {
$scope.isFormValid = true;
$scope.userNameValidation = commonService.isNullOrEmptyOrUndefined($scope.user.UserName);
$scope.passwordValidation = commonService.isNullOrEmptyOrUndefined($scope.user.Password);
$scope.isFormValid = !($scope.userNameValidation || $scope.passwordValidation);
if ($scope.isFormValid === true) {
$scope.loginUser($form);
}
};
$scope.loginUser = function ($form) {
$form.commit();
};
}]);
I found the directive here
Example using Angular 1.5 components.
(function(angular) {
'use strict';
function DemoFormCtrl($timeout, $sce) {
var ctrl = this;
this.$onInit = function() {
this.url = $sce.trustAsResourceUrl(this.url);
/*$timeout(function() {
ctrl.form.$$element[0].submit();
});*/
};
this.validate = function(ev) {
console.log('Running validation.');
if (!this.form) {
return false;
}
};
}
angular.module('app', [])
.component('demoForm', {
template: `
<p>To run this demo allow pop-ups from https://plnkr.co</p>
<hr>
<p>AngularJS - submit form programmatically after validation</p>
<form name="$ctrl.form" method="get" target="blank" action="{{::$ctrl.url}}" novalidate
ng-submit="$ctrl.validate($event)">
<input type='hidden' name='q' ng-value='::$ctrl.value'>
<input type='hidden' name='oq' ng-value='::$ctrl.value'>
<input type="submit" value="submit...">
</form>`,
controller: DemoFormCtrl,
bindings: {
url: '<',
value: '<'
}
});
})(window.angular);
https://plnkr.co/edit/rrruj6vlWrxpN3od9YAj?p=preview

AngularJS ng-hide element based on Firebase mode

Referring to this documentation here, I am trying to hide my element if the mode in the controller function is verifyEmail. No success. Could somebody help to see why it doesn't work?
My HTML is like:
<div class="box-forgot" ng-controller="ResetPass">
<form class="form-forgot" name="resetpassword" ng-hide="mode == 'verifyEmail'">
<fieldset>
<legend>
Reset Password:
</legend>
<div class="form-group">
<span class="input-icon">
<input type="password" class="form-control" id="password" ng-model="user.password" name="password" placeholder="Password" required="">
<i class="fa fa-lock"></i> </span>
</div>
<div class="form-actions">
<button type="submit" class="btn btn-primary pull-right" ng-click="resetMe()">
Reset
</button>
</div>
</fieldset>
</form>
</div>
And the corresponding controller is:
app.controller("ResetPass", ["$scope","firebase", "$location",
function ($scope,firebase, $location) {
$scope.resetMe = function () {
var newPassword = $scope.user.password;
var actionCode = $location.search().oobCode;
var mode = $location.search().mode;
firebase.auth().confirmPasswordReset(actionCode, newPassword)
.then(function (resp) {
console.log("reset pass, done");
$location.path('/login.signin');
}).catch(function (error) {
$scope.errMsg = true;
$scope.errorMessage = error.message;
});
}
}]);
Try to bind mode with $scope object instead of var
Instead of
var mode = $location.search().mode;
Do this
$scope.mode = $location.search().mode;
The variable you're trying to reference in your HTML is not visible because it's not in the controller's scope.
You have to change this:
var mode = $location.search().mode;
to this:
var mode = $scope.mode = $location.search().mode;
// or
$scope.mode = $location.search().mode;

How to change the div after user login

I am building a website and i wish to make a single page application. Iam using nodejs as a backend and angular as a frontend. The thing iam stuck up is i want to show a particular div when user is not logged in, on the event of logging in the other div should be shown. What is the best way to make it happen.
As per my knowledge i have used ng-if as the attribute of both the div which i want to replace each other. I had a angular function for verifying the logged in sesssion with a name isloggedin().
so i used <div ng-if="!checkLoggedin()"> in one div and <div ng-if="checkLoggedin()"> in other div.
So on the first request the page is not logged in and the conditions works as it should. But after i logged in the from the second is not showing up.
Is it something i wrongly expect to happen or is there any other to make this happen. I had check the value of the function and it has data in one condition and 0 in other condition. Am i wrong somewhere.
Added the conditional code.
var checkLoggedin = function ($q, $timeout, $http, $location, $rootScope) {
var deferred = $q.defer();
$http({
method: 'GET',
url: "http://localhost:3000/loggedin"
}).success(function (user) {
if (user !== '0') {
$rootScope.message = 'You are log in.';
$timeout(deferred.resolve, 0);
deferred.resolve();
$location.url('/home');
} else {
$rootScope.message = 'You need to log in.';
$timeout(function () {
deferred.reject();
}, 0);
deferred.reject();
$location.url('/login');
};
});
Here is the form code.
<form action="/#/home" ng-submit="login(user)">
<div class="form-group">
<div class="input-group input-group-in ui-no-corner no-border bordered-bottom bg-none">
<div class="input-group-addon"><i class="fa fa-envelope text-muted"></i></div>
<input class="form-control" placeholder="email" ng-model="user.email">
</div>
</div><!-- /.form-group -->
<div class="form-group">
<div class="input-group input-group-in ui-no-corner no-border bordered-bottom bg-none">
<div class="input-group-addon"><i class="fa fa-lock text-muted"></i></div>
<input type="password" class="form-control" placeholder="Password" ng-model="user.password">
<div class="input-group-addon"><small>Forgot?</small></div>
</div>
</div><!-- /.form-group -->
<div class="form-group">
<div class="row">
<div class="col-md-6">
<div class="nice-checkbox nice-checkbox-inline">
<input type="checkbox" name="rememberSignIn1" id="rememberSignIn">
<label for="rememberSignIn1">Remember</label>
</div>
</div><!-- /.cols -->
<div class="col-md-6">
<button type="submit" class="btn btn-sm btn-block btn-info" style="margin-top:5px" >SUBMIT</button>
</div><!-- /.cols -->
</div><!-- /.row -->
</div><!-- /.form-group -->
</form><!-- /form -->
</div><!-- /.panel-body -->
As I see, the blocks
<div ng-if="!checkLoggedin()">
and
<div ng-if="checkLoggedin()">
will be executed on DOM load (page load). So there is no chance for the model to update. The right approach here will be to use a scope variable in the if blocks as
<div ng-if="isLoggedIn"> ... <div ng-if="!isLoggedIn">
and to update the variable's value in the success handler of the service call, say,
var checkLoggedin = function ($q, $timeout, $http, $location, $rootScope) {
var deferred = $q.defer();
$http({
method: 'GET',
url: "http://localhost:3000/loggedin"
}).success(function (user) {
if (user !== '0') {
// set value here
$rootScope.isLoggedIn = true;
$rootScope.message = 'You are log in.';
$timeout(deferred.resolve, 0);
deferred.resolve();
$location.url('/home');
} else {
$rootScope.message = 'You need to log in.';
$timeout(function () {
deferred.reject();
}, 0);
deferred.reject();
$location.url('/login');
};
});
This way we can be sure that the model has updated values and the right if block will be added to DOM.
Your approach is correct, but may be you have not defined checkLoggedin() properly or may be you used in wrong way.
You can approach it with different way also,
Apply ng-if condition on ng-model variable,
<label> User Name </label>
<input ng-model="username" />
So here you can add condition on username, like:-
<div ng-if="username !== 'null' || 'undefined'"> If username fielld is touched </div>
<div ng-if="username === 'null' || 'undefined'"> If username field is not touched </div>

Searching through JSON Object with ng-disabled (AngularJS)

One of the features of my web application is the possibility to add new users (username + password) through a form. Thereby, I have one JSON object (l_usernames) defined in a controller (UsersController) with all the usernames already chosen by users to avoid the repetition of usernames (it's a unique key).
Sample of my data (fetched-data.json) - format of object "usernames" (l_usernames):
[{"0":"default","USERNAME":"default"},{"0":"user1","USERNAME":"user1"},{"0":"user2","USERNAME":"user2"},{"0":"user3","USERNAME":"user3"}]
There is a sample of the form to add new users (add-user.html):
<div class="row" ng-controller="UsersController">
<div class="col-md-12">
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">Add New User</h3>
</div>
<div class="panel-body">
<form class="form-horizontal" role="form">
<div class="form-group">
<label for="inputUserUsername" class="col-sm-2 control-label"><i class="icon fa fa-user"></i> USERNAME</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="inputUserUsername" placeholder="Username" ng-model="user_username">
</div>
</div>
<div class="form-group">
<label for="inputUserPassword" class="col-sm-2 control-label"><i class="icon fa fa-key"></i> PASSWORD</label>
<div class="col-sm-10">
<input type="password" class="form-control" id="inputUserPassword" placeholder="Password" ng-model="user_password">
</div>
</div>
</form>
</div>
</div>
<form class="form-inline">
<div class="span7 text-center">
<button type="submit" class="btn btn-success" ng-click="addUser()" ng-disabled="(!user_username || !user_password)">Save</button>
</div>
</form>
</div>
</div>
Sample of my controller (userscontroller.js):
var app = angular.module('myApp');
app.controller('UsersController', ['$scope', 'services', function($scope, services) {
services.getData().then(function(data){
$scope.l_usernames = data.data;
});
}])
.factory('services', ['$http', function($http){
var serviceBase = 'services/'
var object = {};
object.getData = function(){
return $http.get('fetched-data.json');
};
return object;
}]);
I would like to know how it is possible to not allow the insert of new users if the username is already chosen - searching through the JSON object l_usernames - with ng-disabled (by disabling the "Save" button). I also want to print a simple message - "Username already chosen" - if such situation occurs. Thank you.
Add a watch on the user_username scope variable. Whenever it changes search through the JSON object, you can use lodash or underscorejs to search through l_usernames to see if the username already exists. If it exists then set a variable in the scope to false. Bind the ng-disabled of the save button to this variable. Use debounce on the user_username for better performance.
Take a look at this fiddle here
Controller
function UsersController($scope) {
$scope.name = 'Superhero';
$scope.l_username = [{"0":"default","USERNAME":"default"},{"0":"user1","USERNAME":"user1"},{"0":"user2","USERNAME":"user2"},{"0":"user3","USERNAME":"user3"}];
$scope.allowSave = true;
$scope.$watch('user_username', function(value) {
if (_.findWhere($scope.l_username, {"USERNAME": value}) !== undefined)
$scope.allowSave = false;
else
$scope.allowSave = true;
})
}
HTML
<button type="submit" class="btn btn-success" ng-click="addUser()" ng-disabled="!allowSave">Save</button>
Whenever the entered username is found in the array, the allowSave variable is changed which disables the 'save' button.
Note: I have used underscore.js to search through the list. You can use you custom method as well.
I have added the warning message and debounced the model for better performance.
I would make a validation directive.
HTML:
<input username-exists type="text" ng-model="userName" ng-model-options="{updateOn: 'default blur', debounce: { 'default': 700, 'blur': 0 }}" />
<div ng-if="myFormName.$error.usernameExists">Username exists!</div>
<button type="button" ng-disabled="myFormName.$invalid">
The ng-model-options is so that your model doesn't go crazy and update always (it delays the validation).
Javascript:
app.directive('usernameExists', function() {
return {
restrict: 'A', //match attributes only
require: 'ngModel',
scope: {
l_usernames: '='
},
link: function(scope, elem, attrs, ctrl) {
ctrl.$parsers.unshift(function(viewValue) {
//first, assume the model is valid until proven otherwise
ctrl.$setValidity('usernameExists', true);
if(viewValue.length > 0 && !ctrl.$error.usernameExists) {
for(var i = 0; i < scope.l_usernames.length; ++i) {
if(scope.l_usernames[i].USERNAME === viewValue) {
//username exists, so set valididty to false
//the form is not valid
ctrl.$setValidity('usernameExists', false);
break; //found match
}
}
}
return viewValue;
});
}
};
})

Error msg: "post https //api.parse.com/1/logout 400 (bad request)" when try to log into Parse

Every time I tried to log in use the following angular js code, it always returns this error "post https //api.parse.com/1/logout 400 (bad request)". And I checked the session and it looked right, just won't redirect. But if I click the submit again, it will log in and redirect, but on a different session (of course).
Please help. Thanks a lot.
My view (logIn.html) looks like this:
<form name="logInForm" class="form-horizontal">
<div class="control-group">
<label class="control-label" for="logInUsername">Username</label>
<div class="controls">
<input type="text" ng-model="user.username" id="logInUsername"/>
</div>
</div>
<div class="control-group">
<label class="control-label" for="logInPassword">Password</label>
<div class="controls">
<input type="password" ng-model="user.password" id="logInPassword"/>
</div>
</div>
<br/>
<div>
<button class="btn btn-primary" ng-click="logMeIn(user)">Log In</button>
Sign Up
</div>
</form>
My controller (logInController.js) looks like this:
(function() {
Parse.initialize("applicationId", "key");
var logInController = function ($scope, $location, $window) {
$scope.logMeIn = function(form) {
Parse.User.logIn(form.username, form.password, {
success: function(user) {
$window.alert("Yay, logged in!");
var sessionToken = Parse.User.current()._sessionToken;
return $location.path("/landing");
},
error: function(user, error) {
$window.alert("Oh, NO");
}
});
};
};
logInController.$inject = ['$scope', '$location', '$window'];
angular.module('customerApp')
.controller('logInController', logInController);
}());

Categories

Resources