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>
Related
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;
});
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;
});
}
};
})
I have modal code (below) wrapped in nav.html and it works as expected (login, logout...works).
<div class="modal fade" id="authModal" role="dialog">
<!-- Modal content-->
<div class="modal-content">
<div class="modal-body">
<form class="form-signin" role="form">
<input ng-model="user.email" type="email" class="form-control" placeholder="Email address" required="" autofocus="">
<input ng-model="user.password" type="password" class="form-control" placeholder="Password" required="">
<div class="checkbox checkbox-success">
<input ng-model="checkbox.signup" ng-init="checkbox.signup=false" type="checkbox">
<label> Sign Up for first-timer </label>
</div>
<div class="text-center">
<button ng-click="login($event)" class="btn btn-lg btn-primary" type="button">Sign In</button>
</div>
</form>
</div>
</div>
But when I move all modal content to a file named md.html and include it to nav.html via
<div class="navbar-header" ng-controller="MainCtrl">
<div ng-include="'views/modals/md.html'"></div>
</div>
It is absolute that I have it included in the ng-controller div.
On testing, I got error of unable to reference to user.password for the Controller. The controller works fine previously and I didn't change anything on it. For this question, I m posting a simplified version of modal and controller code.
$scope.login = function($event){ $event.preventDefault();
// console.log("cond ", cond, ".checkbox.signup ", $scope.checkbox.signup);
if (!$scope.logged)
fn.login($scope.user, function(){
if ($scope.checkbox.signup) fn.signup($scope.user);
});
};
var fn = {
login: function(user, cb){
if (Auth.authData) return;
if (!user.password) {
fn.alert("please type password");
return;
}
if (fn.valid_email(user.email))
Auth.ref_ds1.authWithPassword(user, function(error, authData) {
if (error) {
fn.alert(error);
cb();
} else {
authData.email = $scope.user.email;
console.log("Authenticated successfully on:", authData.email);
fn.greet("Hello " + authData.email.split("#")[0]);
$scope.logged = true;
window.location.href = "/";
}
});
}
}
How to reference them correctly?
It looks like you could be seeing issues with your ng-include creating another scope and you're not able to reference the previously defined user object.
One way I avoid confusion with scopes is using "Controller as" syntax to reference a scope specifically (see http://www.johnpapa.net/angularjss-controller-as-and-the-vm-variable/). Here's a tiny example:
// in the controller
app.controller('MainController', function() {
var vm = this;
this.somevalue = 'something';
})
// markup
<div ng-controller="MainCtrl as ctrl">
{{ ctrl.somevalue }}
<div ng-controller="SecondCtrl as secondCtrl">
{{ ctrl.somevalue }}
{{ secondCtrl.anothervalue }}
</div>
</div>
Using "Controller as" will really help unwind scope problems you're having, but it would take some re-tooling of your original controller.
I am trying nodeJS, SailsJS & Angular. I want use this loading bar:
http://chieffancypants.github.io/angular-loading-bar/
https://github.com/chieffancypants/angular-loading-bar
I read the loading bar handle http request are automatically.
so i do this :
npm install angular-loading-bar.
I put the jss and css in assets.
Here my LoginModule :
var LoginModule = angular.module('LoginModule', ['angular-loading-bar']);
And my LoginController :
angular.module('LoginModule')
.controller('LoginController', LoginController);
LoginController.$inject = ['$scope', '$http', 'cfpLoadingBar'];
function LoginController($scope, $http, cfpLoadingBar) {
// Get CSRF token and set as header
$http.defaults.headers.post['X-CSRF-Token'] = document.getElementsByName('_csrf')[0].value;
$scope.submitLoginForm = function() {
$scope.start();
$http.post('/login', {
identifiant: $scope.login.identifiant,
password: $scope.login.password
})
.then(function onSuccess() {
window.location = '/home/';
})
.catch(function onError(response) {
console.log(response);
})
}
$scope.start = function() {
cfpLoadingBar.start();
};
$scope.complete = function() {
cfpLoadingBar.complete();
};
}
But when i login on my form :
<div class="container-fluid" ng-app="LoginModule" ng-controller="LoginController" ng-cloak>
<div class=" col-lg-offset-4 col-lg-4">
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">Connexion</h3>
</div>
<div class="panel-body">
<h5 class="col-lg-12" style="text-align: center">Admin !</h5>
<form ng-submit="submitLoginForm()" class="col-lg-offset-3 col-lg-6 form-group" name="loginForm">
<input type="text" class="form-control" id="inputLogin" name="inputLogin" placeholder="Identifiant" ng-model="login.identifiant">
<input type="password" class="form-control" id="inputPassword" name="inputPassword" placeholder="Mot de passe" ng-model="login.password">
<br>
<button type="submit" class="btn btn-primary" style="float: right">Connexion</button>
<input type="hidden" name="_csrf" value="<%= _csrf %>">
</form>
</div>
</div>
</div>
</div>
I don't see the loading bar when i submit the form (form working) :( why ? Also when i try to load ngAnimate in LoginModule i have an error
I tried also the function start() on element HTML but nothing..
You Must include angular-animate.min.js to make it work
as also include in git repository example. when I removed this line
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.0/angular-animate.min.js"></script>
it stopped working please make sure you include animation file. that's why ngAnimate also giving error
I'm new to using AngularJS. However, why isn't this working?
Upon loading the webpage, I get in the console Uncaught ReferenceError: $scope is not defined on Line 81 which is $scope.processForm = function() { Help?
// define angular module/app
var formApp = angular.module('formApp', []);
// create angular controller and pass in $scope and $http
function formController($scope, $http) {
// create a blank object to hold our form information
// $scope will allow this to pass between controller and view
$scope.formData = {};
// process the form
$scope.processForm = function() {};
}
// process the form
$scope.processForm = function() {
$http({
method: 'POST',
url: 'process.php',
data: $.param($scope.formData), // pass in data as strings
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
} // set the headers so angular passing info as form data (not request payload)
})
.success(function(data) {
console.log(data);
if (!data.success) {
// if not successful, bind errors to error variables
$scope.errorName = data.errors.name;
$scope.errorSuperhero = data.errors.superheroAlias;
} else {
// if successful, bind success message to message
$scope.message = data.message;
}
});
};
<!-- LOAD ANGULAR -->
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.0/angular.min.js"></script>
<!-- LOAD BOOTSTRAP CSS -->
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.2/css/bootstrap.min.css">
<!-- LOAD JQUERY -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<body ng-app="formApp" ng-controller="formController">
<div class="container">
<div class="col-sm-6 col-sm-offset-3">
<!-- PAGE TITLE -->
<div class="page-header">
<h1><span class="glyphicon glyphicon-tower"></span> Submitting Forms with Angular</h1>
</div>
<!-- SHOW ERROR/SUCCESS MESSAGES -->
<div id="messages" ng-show="message">{{ message }}</div>
<!-- FORM -->
<form ng-submit="processForm()">
<!-- NAME -->
<div id="name-group" class="form-group" ng-class="{ 'has-error' : errorName }">
<label>Name</label>
<input type="text" name="name" class="form-control" placeholder="Bruce Wayne" ng-model="formData.name">
<span class="help-block" ng-show="errorName">{{ errorName }}</span>
</div>
<!-- SUPERHERO NAME -->
<div id="superhero-group" class="form-group" ng-class="{ 'has-error' : errorSuperhero }">
<label>Superhero Alias</label>
<input type="text" name="superheroAlias" class="form-control" placeholder="Caped Crusader" ng-model="formData.superheroAlias">
<span class="help-block" ng-show="errorSuperhero">{{ errorSuperhero }}</span>
</div>
<!-- SUBMIT BUTTON -->
<button type="submit" class="btn btn-success btn-lg btn-block" ng-model="formData.XAlias">
<span class="glyphicon glyphicon-flash"></span> Submit!
</button>
</form>
</div>
</div>
<!-- SHOW DATA FROM INPUTS AS THEY ARE BEING TYPED -->
<pre>{{formData}}</pre>
Replace the empty $scope.processForm inside your controller (function formController($scope, $http)) with the one that's currently outside.
Inside the controller it'll have access to the $scope which you injected in.