OK so I've tried Googling for it and have read several of the various questions posted on here, but none seem to address the issue I have currently.
I'm working with angular and using the angular-bootstrap ui. When the user clicks on the login button on the homepage a modal pops up giving them options. I can sign in through Facebook, Twitter, etc just fine. But when the new user welcome page the scope for the modal instance is still there. This isn't a huge issue but annoying and I'd rather not have unnecessary scope creep slowing down my client side.
Here a walk through of my issue using pictures. So the user hits the homepage as seen below.(ok I couldn't post images because my reputation isn't a 10 so here's a link to them.)
https://scontent-b.xx.fbcdn.net/hphotos-prn2/t1.0-9/1538681_313638755452846_5694320198249084494_n.jpg
As the photo shows the scope is limited to that of the controller for the view. Then the user clicks on the Login button and the modal pops up. (As can be seen in the following link)
so it wouldn't let me post more than 2 links because my reputation isn't a 10 so I had to disguise this one.
https (remove me)://fbcdn-sphotos-b-a.akamaihd.net/hphotos-ak-frc1/t1.0-9/1926680_313638758786179_2230027955019442405_n.jpg
If you look at the scope you'll see that I've circled in red the newly added scope for the modal. When the user selects a provider to log-in with he/she is taken to the new user page as can be seen in the following link.
https://scontent-b.xx.fbcdn.net/hphotos-prn2/t1.0-9/10155397_313638762119512_900295349985269066_n.jpg
A portion of the modal scope is still present, even though it's not in use anymore. I don't know how to remove it. True if I reload the page the scope will disappear but I don't want to force a reload simple to remove scope. Especially when it's not currently noticeable or slowing it down. Is there a way to remove this scope without a reload?
Here's my current code.
The Controller:
'use strict';
angular.module('triviumApp')
.controller('HomepageCtrl', function ($log, $modal, $scope, UserFactory, $location) {
$scope.user = UserFactory;
$scope.awesomeThings = [
'HTML5 Boilerplate',
'AngularJS',
'Karma'
];
$scope.items = ['item1', 'item2', 'item3'];
$scope.close = function (){
$modal.close();
};
$scope.login = function (type){
console.log("hi");
$scope.user.login(type);
};
$scope.open = function () {
var modalInstance = $modal.open({
templateUrl: 'LoginModalContent.html',
controller: ModalInstanceCtrl,
resolve: {
user: function () {
return $scope.user;
}
}
});
modalInstance.result.then(function (provider) {
//They logged in
$scope.user.login(provider);
}, function (event) {
//the Modal was dismissed so the user didn't log in.
$log.info('Modal dismissed at: ' + new Date() + event);
});
};
}
);
// Please note that $modalInstance represents a modal window (instance) dependency.
// It is not the same as the $modal service used above.
var ModalInstanceCtrl = function ($scope, $modalInstance) {
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
$scope.login = function (provider){
$modalInstance.close(provider);
};
};
Here's the View:
<div class="header">
<ul class="nav nav-pills pull-right">
<li class="active"><a ng-href="#">Home</a></li>
<li><a ng-href="#">About</a></li>
<li><a ng-href="#">Contact</a></li>
<li ng-show="user.getLoginStatus">Login</li>
<!--<li ng-show="user.getLoginStatus">Logout</li>-->
</ul>
<h3 class="text-muted">Temp Title</h3>
</div>
<script type="text/ng-template" id="LoginModalContent.html">
<div class="modal-body">
<button type="button" class="close pull-right" aria-hidden="true" ng-click="cancel()">×</button>
<fieldset>
<legend class="bold">Login/Sign-up</legend>
<div class="login-icons center-block">
<span class="facebook icon"></span>
<span><span class="github icon"></span></span>
<!--<span><span class="google icon"></span></span>-->
<span><span class="persona icon"></span></span>
<span><span class="twitter icon"></span></span>
</div>
</fieldset>
</div>
</script>
Here's the User Factory -- (Note I removed my firebase key so it won't have access to my firebase account so if you test it you'll need to add your key and setup your Firebase account to work with Facebook, or twitter.
'use strict';
var User = angular.module('UserApp', ['firebase']);
User.factory('UserFactory', ['$firebase', '$window', function ($firebase, $window){
//Firebase Variables
var user = {
'name': null,
'id': null,
'address': null,
'email': null,
'gender': null,
'loggedIn': false
};
var firebase = new Firebase("YOUR FIREBASE KEY HERE");
var auth = new FirebaseSimpleLogin(firebase, function(error, response) {
if (error) {
// an error occurred while attempting login
} else if (user) {
// user authenticated with Firebase
console.log('Auth', response);
if(response !== null) {
getUser(response);
}
} else {
// user is logged out
}
});
function getUser(firebaseInfo){
if(firebaseInfo !== null && firebaseInfo.firebaseAuthToken !== null){
user.id = firebaseInfo.firebaseAuthToken.split('.')[0];
firebase.child(user.id).once('value', function(snap){
if(snap.val() === null){
populateUser(firebaseInfo, snap, true);
}
else{
//Check to see if user used a different provider
populateUser(firebaseInfo, snap, false);
}
});
}
}
function populateUser (firebaseInfo, snap, newUser){
var provider = firebaseInfo.provider;
//Create the user in the firebase table
firebase.child(user.id);
if(provider === 'facebook'){
if(newUser){
console.log('Here');
facebookUser(firebaseInfo);
$window.location.href ='/#/new-user';
} else{
if(!snap.val().Facebook_ID){
facebookUser(firebaseInfo);
}
}
} else if(provider === 'github'){
if(newUser){
githubUser(firebaseInfo);
$window.location.href('/new-user');
} else{
if(!snap.val().GitHub_ID){
githubUser(firebaseInfo);
}
}
} else if(provider === 'persona'){
if(newUser){
personaUser(firebaseInfo);
$window.location.href('/new-user');
} else{
if(!snap.val().Persona_ID){
personaUser(firebaseInfo);
}
}
}else if(provider === 'twitter'){
if(newUser){
twitterUser(firebaseInfo);
$window.location.href('/new-user');
} else{
if(!snap.val().Twitter_ID){
twitterUser(firebaseInfo);
}
}
}
}
function facebookUser (firebaseInfo){
firebase.child(user.id).update({'Facebook_ID': firebaseInfo.id});
firebase.child(user.id).update({'Facebook_Link': firebaseInfo.thirdPartyUserData.link});
user.name = firebaseInfo.name.split(' ');
user.gender = firebaseInfo.thirdPartyUserData.gender;
if(user.name[0] && user.name[1]) {
firebase.child(user.id).update({'Name-First': user.name[0], 'Name-Last': user.name[1]});
}
if(user.gender){
firebase.child(user.id).update({'Gender': firebaseInfo.thirdPartyUserData.gender});
}
}
function githubUser (firebaseInfo){
firebase.child(user.id).update({'GitHub_ID': firebaseInfo.id});
firebase.child(user.id).update({'GitHub_Link': firebaseInfo.thirdPartyUserData.html_url});
firebase.child(user.id).update({'Email': firebaseInfo.thirdPartyUserData.emails[0].email});
}
function personaUser (firebaseInfo){
firebase.child(user.id).update({'Persona_ID': firebaseInfo.id});
}
function twitterUser (firebaseInfo){
firebase.child(user.id).update({'Twitter_ID': firebaseInfo.id});
user.name = firebaseInfo.name.split(' ');
if(user.name[0] && user.name[1]) {
firebase.child(user.id).update({'Name-First': user.name[0], 'Name-Last': user.name[1]});
}
}
return {
login: function (provider){
if(provider !== undefined && provider !== '' && provider !== null){
auth.login(provider);
}
},
logout: function (){
auth.logout();
},
getLoginStatus: user,
getMyAddress: function (){
return user.address;
},
getMyEmail: function (){
return user.email;
},
getMyName: function (){
return user.name;
},
updateMyName: function (first, last){
if(user.id != null){
firebase.child(user.id).update({'First_Name': first});
firebase.child(user.id).update({'Last_Name': last});
}
},
updateMyEmail: function (email){
if(user.id != null){
firebase.child(user.id).update({'Email': email});
}
},
updateMyAddress: function (address){
if(user.id != null){
firebase.child(user.id).update({'Address': address});
}
}
}
}]);
Thanks for the help. :-)
Related
I gather several possible solutions none of them work for me. Can someone tell which part has error.
This is the logout function I am working on, the icon should disappear if user click logout and back to login page, but now it only lead to login page. Icon is still there because the function didn't trigger.
Everything works only AFTER I click the button in login page,
navigation.service.js
(function() {
angular
.module('TheApp')
.service('authentication', ['$window', function($window) {
var saveToken = function(token) {
$window.localStorage['auth-token'] = token;
}
var getToken = function() {
return $window.localStorage['auth-token'];
}
var logout = function() {
$window.localStorage.removeItem('auth-token');
//delete $window.localStorage['auth-token'];
//$window.localStorage.clear();
}
var isLoggedIn = function() {
var token = getToken();
//Get token from storage If token exists get payload, decode it, and parse it to JSON
if (token) {
let payload = JSON.parse($window.atob(token.split('.')[1]));
return payload.exp > Date.now() / 1000;
} else {
return false;
}
}
return {
saveToken: saveToken,
getToken: getToken,
isLoggedIn: isLoggedIn,
logout: logout
};
}])})();
navigation.directive.js
(function() {
angular
.module('TheApp')
.directive('navigation', navigation);
function navigation() {
return {
restrict: 'EA',
templateUrl: './common/directives/navigation/navigation.template.html',
controller: 'navigationCtrl as navvm'
};
}})();
navigation.controller.js
(function() {
angular
.module('TheApp')
.controller('navigationCtrl', ['$location', 'authentication', '$window', function($location, authentication, $window) {
console.log("enters navigation controller");
var vm = this;
vm.isLoggedIn = authentication.isLoggedIn();
vm.logout = function() {
console.log('test if the link work');
authentication.logout();
$location.path('/login');
};
}]);})();
navigation.template.html
<ul class="nav navbar-nav navbar-right">
<li ng-show="navvm.isLoggedIn">
<a href="" ng-click="navvm.logout()">
<span aria-hidden="true" class="glyphicon glyphicon-log-out"></span></a></li>
</ul>
Thanks.
Solution(UPDATE):
A careless situation, I omit to put the controller link inside index.html (main template page). So if anyone working on the similar project, the code above should works without errors.
I am currently using $rootScope to store user information and whether or not the user is logged in. I have tried using $window.localStorage, but with no success. My goal is to have items in my navbar appear through an ng-show once a user is logged on, have their username appear in the navbar, individual user profile view, all users view, etc. I need a persistent login. I have the navbar working with $rootscope, but whenever I try and transition over to $window.localStorage, it fails. Here is the code using $rootScope:
mainModule
angular.module('mainModule', [
'ui.router',
...
])
.config(configFunction)
.run(['$rootScope', '$state', 'Auth', function($rootScope, $state, Auth) {
$rootScope.$on('$stateChangeStart', function(event, next) {
if (next.requireAuth && !Auth.getAuthStatus()) {
console.log('DENY');
event.preventDefault();
$state.go('login');
} else if (Auth.getAuthStatus() || !Auth.getAuthStatus()) {
console.log('ALLOW');
}
});
}]);
Auth Factory
angular.module('authModule').factory('Auth', ['$http', '$state', function authFactory($http, $state) {
var factory = {};
var loggedIn = false;
var userData = {};
factory.getAuthStatus = function() {
$http.get('/api/v1/auth')
.success(function(data) {
if (data.status == true) {
loggedIn = true;
} else {
loggedIn = false;
}
})
.error(function(error) {
console.log(error);
loggedIn = false;
});
return loggedIn;
}
return factory;
}]);
Login Controller
function SigninController($scope, $rootScope, $http, $state) {
$scope.userData = {};
$scope.loginUser = function() {
$http.post('api/v1/login', $scope.userData)
.success((data) => {
$scope.userData = data.data;
$rootScope.loggedIn = true;
$rootScope.userData = data;
$state.go('home');
})
.error((error) => {
console.log('Error: ' + error);
});
};
}
Nav Controller
function NavbarController($scope, Auth) {
$scope.loggedIn = Auth.getAuthStatus();
}
EDIT EDIT EDIT
Here is how I am using local storage. These are the only things that changed.
Login Controller
function SigninController($scope, $window, $http, $state) {
$scope.userData = {};
$scope.loginUser = function() {
$http.post('api/v1/login', $scope.userData)
.success((data) => {
$scope.userData = data.data;
$window.localStorage.setItem('userData', angular.toJson(data));
$window.localStorage.setItem('loggedIn', true);
$state.go('home');
})
.error((error) => {
console.log('Error: ' + error);
});
};
}
Auth Factory
angular
.module('authModule')
.factory('Auth', ['$http', '$window', '$state', function authFactory($http, $window, $state) {
var factory = {};
factory.getAuthStatus = function() {
$http.get('/api/v1/auth')
.success(function(data) {
if (data.status == true) {
$window.localStorage.setItem('loggedIn', true);
} else {
$window.localStorage.setItem('loggedIn', false);
}
})
.error(function(error) {
console.log(error);
$window.localStorage.setItem('loggedIn', false);
});
return $window.localStorage.getItem('loggedIn');
}
return factory;
}]);
I see a potential problem with your use of localStorage.getItem('loggedIn').
Because localStorage only stores strings, what you get back is actually a stringified version of the boolean that you put in. If the string 'false' gets returned, your check of !Auth.getAuthStatus() in main module for example will always evaluate to boolean false because any non-empty string in JavaScript is "truthy".
i.e. !'false' === false (the same as !true === false)
You can get over this by using JSON.parse on the value in localStorage. e.g. JSON.parse(localStorage.getItem('loggedIn')) would parse the string 'false' to the Boolean false.
Simply replace $window.localStorage with window.localStorage and you should be fine.
For example:
function SigninController($scope, $window, $http, $state) {
$scope.userData = {};
$scope.loginUser = function() {
$http.post('api/v1/login', $scope.userData)
.success((data) => {
$scope.userData = data.data;
window.localStorage.setItem('userData', angular.toJson(data));
window.localStorage.setItem('loggedIn', true);
$state.go('home');
})
.error((error) => {
console.log('Error: ' + error);
});
};
}
This being said, storing authenticated status in localStorage (or sessionStorage) is not a good path to go down. Both key/value pairs can be read in the developer pane and then altered (aka spoofed) via the console. A better solution is to return a unique value (GUID) after a successful login and store it in a cookie (set to expire in a short amount of time, like 20 minutes) that can be read on the server and verified there. You can and should use $cookie for this. Your user login state should be controlled server-side, never client-side. The client should always have to prove that it is authenticated.
To persist login, create a service that handles your visitor and let that service handle the login/logout and provide the proof of being logged in. That proof of being logged in should always be a private value that is held internally by the service and not accessible outside of it.
(function () {
'use strict';
var visitorModelService = ['$http', function ($http) {
var loggedIn = false,
visitorModel = {
login:function(){
//do login stuff with $http here
//set loggedIn to true upon success
},
loggedIn:function(){
return loggedIn;
},
logout:function(){
//do logout stuff with $http here
//no matter what, set loggedIn to false
}
};
return visitorModel;
}];
var module = angular.module('models.VisitorModel', []);
module.factory('VisitorModel', visitorModelService);
}());
Doing this, you can simply check for visitor.loggedIn in your ng-show and have everything centralized. Such as:
<a ng-click='visitor.logout' ng-show='visitor.loggedIn'>Log Out</a>
Better yet, put the elements that are only visible to authenticated users in a div tag and hide/show them en-mass.
I'm trying to display an avatar of an user once the user logs in:
<img src="{{(API_PROVIDER.domain + user.avatar.small_thumb.url)}}" alt="" class="img-circle size-30x30">
But the above code only works if I reload the page after login. How can I get it to work without having to programmatically reload the page?
PS: The above resolves to something like this: www.example.com/api/something.jpg
EDIT:
I have tried using ng-src instead of src and it didn't work. As to the other comment whether my variables were in scope, yes, the avatar link is only defined when the user signs in. Then I use $state.go('somewhere') to change the template, in which case I'd image the variable should be updated.
Here's my main controller:
(function() {
'use strict';
angular
.module('admin')
.controller('MainController', MainController);
/** #ngInject */
function MainController($timeout, webDevTec, toastr, $scope, $http, authenticatedUser, Session, $anchorScroll, API_PROVIDER) {
...
$scope.session = Session;
$scope.user = Session.user;
$scope.API_PROVIDER = API_PROVIDER;
...
}
})();
Here ar the components of my Session (reduced for brevity):
...
this.create = function(user) {
this.user = user;
this.role = user._role;
this.token = user.auth_token;
this.userRole = user._role;
};
return this;
...
And how the session is saved for later retrieval:
...
$window.sessionStorage["userInfo"] = JSON.stringify(loginData);
...
Do I need to use $apply() in this case? If yes, how so?
EDIT 3: Here's how I'm setting my Session object
authService.login = function(user, success, error, $state) {
$http.post(API_PROVIDER.full_path + 'signin', user).success(function(data) {
if(data.success){
var user = data.user;
var loginData = user;
$window.sessionStorage["userInfo"] = JSON.stringify(loginData);
delete loginData.password;
Session.create(loginData);
$rootScope.$broadcast(AUTH_EVENTS.loginSuccess);
success(loginData);
} else {
$rootScope.$broadcast(AUTH_EVENTS.loginFailed);
error();
}
});
};
Force reload images
https://stackoverflow.com/a/21731946/2906183
Apply time stamp and call $scope.$appy()
Use fall back
HTML:
<img fallback-src="http://google.com/favicon.ico" ng-src="{{image}}"/>
JS:
myApp.directive('fallbackSrc', function () {
var fallbackSrc = {
link: function postLink(scope, iElement, iAttrs) {
iElement.bind('error', function() {
angular.element(this).attr("src", iAttrs.fallbackSrc);
});
}
}
return fallbackSrc;
});
I am trying to build a dynamic sidebar, which displays elements depending on the current user state. So only some elements should be displayed, if the user is logged in and some others, if the the user isn´t.
The current state is saved in the userService. There also is a method, which return the current state:
angular.module('app').service('userService', ['$http', 'base64', function ($http, base64) {
var user = {
auth: "",
ask: "",
mandant: ""
};
...
this.isloggedIn = function(){
if(user.auth != "" && user.ask != "" && user.mandant != "")
{
return true;
}
else
{
return false;
}
...
}]);
Now I created a small controller:
angular.module('app')
.controller('sidebarController',['$scope', 'userService', function($scope, userService) {
$scope.loggedIn = userService.isloggedIn();
}]);
My sidebar looks like this:
<div class="scrollable" ng-controller="sidebarController">
<h1 class="scrollable-header app-name">Navigation</h1>
<div class="scrollable-content">
<div class="list-group" ui-turn-off='uiSidebarLeft'>
<a class="list-group-item green" href="#/login" ng-hide="loggedIn">Login</a>
<a class="list-group-item red" href="#/logout" ng-show="loggedIn">Logout</a>
</div>
</div>
</div>
The Goal : The sidebar should only display the login element, if the user is logged out and only the logout element, if the user is logged in.
After runing this, the sidebar only displays the login element, but after login the sidebar do not change. How can i force the sidebar to refresh?
Your problem is that
$scope.loggedIn = userService.isloggedIn();
is only being called once: on initiating the controller. What you want is to create a function which is being called whenever the state changes (e.g. after login):
angular.module('app').controller('sidebarController', ['$scope', 'userService', function($scope, userService) {
function refreshState() {
$scope.loggedIn = userService.isloggedIn();
}
refreshState();
$scope.login = function() {
// do login
// call refreshState() afterwards
refreshState();
}
}]);
I am trying to get a header to update after login. I have used both $on and $watch in this effort to no avail. When I refresh it works correctly. Code is as follows below.
header.html (missing excess nav bar code for simplicity)
<li><a ng-href="#/login" ng-hide="showMenu">Login</a></li>
<li><a ng-href="#/signup" ng-hide="showMenu">Signup</a></li>
<li>Logout</li>
app.js
$stateProvider
.state('app', {
url: '',
views: {
'header': {
templateUrl: 'views/partials/_header.html',
controller: 'HeaderCtrl'
}
}
})
header.js (The broadcast fires correctly as demonstrated by the console.logs)
angular.module('urbinsight')
.controller('HeaderCtrl', function ($scope, $rootScope, $state, $location, UserAuthFactory, AuthFactory) {
$scope.logout = function () {
UserAuthFactory.logout();
$rootScope.$broadcast('loginStateChange');
$location.path('/');
};
$scope.showMenu = AuthFactory.loggedStatus();
$rootScope.$on('loginStateChange', function(){
console.log($scope.showMenu)
$scope.showMenu = AuthFactory.loggedStatus();
console.log($scope.showMenu)
})
})
authService
angular.module('urbinsight.services')
.factory('AuthFactory', function ($window) {
var isLogged = false;
return {
check: function() {
if ($window.sessionStorage.token && $window.sessionStorage.user) {
isLogged = true;
} else {
isLogged = false;
delete this.user;
}
},
loggedStatus: function() {
return isLogged;
},
changeLoggedStatus: function() {
isLogged = !(isLogged);
}
};
})
login function + broadcast
login.submit = function () {
var username = user.username,
password = user.password;
if (username !== undefined && password !== undefined) {
UserAuthFactory.login(username, password).success(function(data) {
$rootScope.showMenu = true
// AuthFactory.isLogged = true;
AuthFactory.changeLoggedStatus();
AuthFactory.user = data.user.username;
AuthFactory.userRole = data.user.role;
$rootScope.$broadcast('loginStateChange');
$window.sessionStorage.token = data.token;
$window.sessionStorage.user = data.user.username;
$window.sessionStorage.userRole = data.user.role;
$location.path('/');
}).error(function(status) {
$window.alert('Oops something went wrong!');
});
} else {
$window.alert('Invalid credentials');
}
};
Please tell me what I am doing wrong.
You have set the "$rootScope.showMenu = true" in your login controller. But in your header controller you also have "$scope.showMenu = AuthFactory.loggedStatus();"
So i would remove this line from your header controller
$scope.showMenu = AuthFactory.loggedStatus();
Since you want your header html to react to $rootscope showMenu variable directly