I am trying to use ng-switch with ng-include below. The problem is with ng-init and the whole controller block getting re-rendered on any ng-includes change.
In the login_form.html, when a user logins, I set the isLoggedIn = true, in the LoginCtrl. However this causes the re-rendering of the full html below, which causes the ng-init again.
How do I avoid this cycle?
<div ng-controller="LoginCtrl" ng-init="isLoggedIn = false" class="span4 pull-right">
<div ng-switch on="isLoggedIn">
<div ng-switch-when="false" ng-include src="'login_form.html'"></div>
<div ng-switch-when="true" ng-include src="'profile_links.html'"></div>
</div>
</div>
Below is the HTML for the login form -
<form class="form-inline">
<input type="text" placeholder="Email" ng-model="userEmail" class="input-small"/>
<input type="password" placeholder="Password" ng-model="userPassword" class="input-small"/>
<button type="submit" ng-click="login(userEmail, userPassword)" class="btn">Sign In</button>
</form>
Below is the controller -
angularApp.controller('LoginCtrl', function($scope, currentUser){
$scope.loginStatus = function(){
return currentUser.isLoggedIn();
};
/* $scope.$on('login', function(event, args) {
$scope.userName = args.name;
});
$scope.$on('logout', function(event, args) {
$scope.isLoggedIn = false;
});*/
$scope.login = function(email, password){
currentUser.login(email, password);
};
$scope.logout = function(){
currentUser.logout();
};
});
Blow is the service -
angularApp.factory('currentUser', function($rootScope) {
// Service logic
// ...
//
var allUsers = {"[email protected]": {name: "Robert Patterson", role: "Admin", email: "[email protected]", password: "rob"},
"[email protected]":{name: "Steve Sheldon", role: "User", email: "[email protected]", password: "steve"}}
var isUserLoggedIn = false;
// Public API here
return {
login: function(email, password){
var user = allUsers[email];
var storedPass = user.password;
if(storedPass === password){
isUserLoggedIn = true;
return true;
}
else
{
return false;
}
},
logout: function(){
$rootScope.$broadcast('logout');
isUserLoggedIn = false;
},
isLoggedIn: function(){
return isUserLoggedIn;
}
};
});
I believe your problem is a result of the way prototypal inheritance works. ng-include creates its own child scope. Assigning a primitive value in a child scope creates a new property on that scope that shadows/hides the parent property.
I'm guessing that in login_form.html you do something like the following when a user logs in:
<a ng-click="isLoggedIn=true">login</a>
Before isLoggedIn is set to true, this is what your scopes look like:
After isLoggedIn is set to true, this is what your scopes look like:
Hopefully the pictures make it clear why this is causing you problems.
For more information about why prototypal inheritance works this way with primitives, please see What are the nuances of scope prototypal / prototypical inheritance in AngularJS?
As the above link explains, you have three solutions:
<a ng-click="$parent.isLoggedIn=true">login</a>
Update: in reviewing your HTML, you may actually have two levels of child scopes, since ng-switch and ng-include each create their own scopes. So, the pictures would need a grandchild scope, but the three solutions are the same... except for #2, where you would need to use $parent.$parent.isLoggedIn -- ugly. So I suggest option 1 or 3.
Update2: @murtaza52 added some code to the question... Remove ng-init="isLoggedIn = false"
from your controller (your service is managing the login state via its isUserLoggedIn
variable) and switch on loginStatus() in your controller: <div ng-switch on="loginStatus()">
.
Here is a working fiddle.
I've have a working example. The trick is that the scope variable to be evaluated has to be an object, not a primitive type. It looks like $scope.$watch()
is not watching primitive types properly (which is a bug). The jsFiddle has a parent controller with two child controllers, but it would also work with only one (parent) controller.
HTML:
<div ng-controller="LoginCheckCtrl">
<div ng-switch on="user.login">
<div ng-switch-when="false" ng-include="'login'"></div>
<div ng-switch-when="true" ng-include="'logout'"></div>
</div>
</div>
Controller:
function LoginCheckCtrl($scope) {
$scope.user = {
login: false
};
}
Note: this will also work with only one controller:
function LoginCheckCtrl($scope) {
$scope.user = {
login: false
};
$scope.login = function() {
console.log($scope.user.login ? 'logged in' : 'not logged in');
$scope.user.login = true;
};
$scope.logout = function() {
console.log($scope.user.login ? 'logged in' : 'not logged in');
$scope.user.login = false;
};
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With