I've been trying to implement this easy feature in Angular, but without no success.
I have two controllers and one service. The service provides a shared methods to retrieve if user is logged in or not;
app.factory("shared",["$rootScope",function($rootScope){
var data = {};
data.val = false;
data.get = function() {
return data.val;
},
data.set = function(val) {
data.val = val;
};
return data;
}]);
Then I have a controller attached to login/logout bar where login and logout links should be changed with the help of ng-show and ng-hide. Here is a html code for this:
<ul class="reg" ng-controller="mainCntr">
<li ng-hide="shared.get()"> Signup </li>
<li ng-hide="shared.get()">Login</li>
<li ng-show="shared.get()">Logout</li>
</ul>
And mainCntr attached to this:
app.controller("mainCntr,["$scope","shared","$rootScope",function($scope,shared, $rootScope){
$scope.$watch("shared.get()",function(newval){
console.log(newval);
},true);
}]);
And lastly, I have login controller for login form rendered into when we are on /login page. I should notice that the above HTML for login/signup links is always on index.html page. So, loginCntr is below:
app.controller("loginCntr",["$scope","$http", "$window","$rootScope", "$location","shared",function($scope,$http, $window, $rootScope,$location,shared){
$scope.login = "";
$scope.password = "";
$scope.response = "";
$scope.loggedin = false;
$scope.submit = function(isValid) {
if (isValid) {
$http({
method: "POST",
url: "/authenticate",
data: $.param({login:$scope.login,password:$scope.password}),
headers: {"Content-Type": "application/x-www-form-urlencoded"}
}).success(function(data,status,headers,config){
$window.sessionStorage.token = data.token;
console.log($window.sessionStorage.token);
$scope.response = "You logged in successfully";
shared.set(true); /////// Here I set shared valued to true when the user is logged
}).error(function(data,status,error,config){
delete $window.sessionStorage.token;
$scope.response = data.response;
});
};
};
}]);
So, that's it, but it doesn't work. Being new to Angular, it is hard time for me to figure out what is wrong. May be it's because "shared" value is set in asynchronyous callback and thus never read, because the login section has been already loaded?
why you not try $emit event listener.
like :
///****mainCntr
app.controller("mainCntr,["$scope","shared","$rootScope",function($scope,shared, $rootScope){
$scope.$on("loginEvent", function($event, args){
alert(args.msg);
});//Listen $emit Event
}]);
///****loginCntr
app.controller("loginCntr",["$scope","$http", "$window","$rootScope", "$location","shared",function($scope,$http, $window, $rootScope,$location,shared){
$scope.login = "";
$scope.password = "";
$scope.response = "";
$scope.loggedin = false;
$scope.submit = function(isValid) {
if (isValid) {
$http({
method: "POST",
url: "/authenticate",
data: $.param({login:$scope.login,password:$scope.password}),
headers: {"Content-Type": "application/x-www-form-urlencoded"}
}).success(function(data,status,headers,config){
$window.sessionStorage.token = data.token;
console.log($window.sessionStorage.token);
$scope.response = "You logged in successfully";
$scope.$emit("loginEvent", {msg : "Login Successful..."}) /////// Emit an event
}).error(function(data,status,error,config){
delete $window.sessionStorage.token;
$scope.response = data.response;
});
};
};
}]);
Related
I am using Laravel angularjs
I am using this package https://github.com/andbet39/tokenAuth
it's working fine but my problem is without login i can go to any page also once i reload the page user name is disabled
I don't know what is the problem here
app.js
var app = angular.module('todoApp', ['ui.router', 'satellizer'])
.config(function($stateProvider, $urlRouterProvider, $authProvider,$provide) {
$authProvider.loginUrl = '/api/authenticate';
$urlRouterProvider.otherwise('/login');
$stateProvider
.state('login', {
url: '/login',
templateUrl: '/js/tpl/login.html',
controller: 'AuthController'
})
.state('register', {
url: '/register',
templateUrl: '/js/tpl/register.html',
controller: 'AuthController'
})
.state('todo', {
url: '/todo',
templateUrl: '/js/tpl/todo.html',
controller: 'TodoController'
});
function redirectWhenLoggedOut($q, $injector) {
return {
responseError: function (rejection) {
var $state = $injector.get('$state');
var rejectionReasons = ['token_not_provided', 'token_expired', 'token_absent', 'token_invalid'];
angular.forEach(rejectionReasons, function (value, key) {
if (rejection.data.error === value) {
localStorage.removeItem('user');
$state.go('login');
}
});
return $q.reject(rejection);
}
}
}
$provide.factory('redirectWhenLoggedOut', redirectWhenLoggedOut);
});
TodoController.js
app.controller('TodoController', function($state,$http,$rootScope, $scope,$auth) {
$scope.todos=[];
$scope.newTodo={};
$scope.init = function (){
$http.get('/api/todo').success(function(data){
$scope.todos=data;
})
};
$scope.save = function(){
$http.post('/api/todo',$scope.newTodo).success(function (data) {
$scope.todos.push(data);
$scope.newTodo={};
});
};
$scope.update = function(index){
$http.put('/api/todo/'+ $scope.todos[index].id,$scope.todos[index]);
};
$scope.delete = function(index){
$http.delete('/api/todo/'+ $scope.todos[index].id).success(function(){
$scope.todos.splice(index,1);
});
};
$scope.logout = function() {
$auth.logout().then(function() {
localStorage.removeItem('user');
$rootScope.authenticated = false;
$rootScope.currentUser = null;
});
}
$scope.init();
});
AuthController.js
app.controller('AuthController', function($auth, $state,$http,$rootScope, $scope) {
$scope.email='';
$scope.password='';
$scope.newUser={};
$scope.loginError=false;
$scope.loginErrorText='';
$scope.login = function() {
var credentials = {
email: $scope.email,
password: $scope.password
}
$auth.login(credentials).then(function() {
return $http.get('api/authenticate/user');
}, function(error) {
$scope.loginError = true;
$scope.loginErrorText = error.data.error;
}).then(function(response) {
// var user = JSON.stringify(response.data.user);
// localStorage.setItem('user', user);
$rootScope.authenticated = true;
$rootScope.currentUser = response.data.user;
$scope.loginError = false;
$scope.loginErrorText = '';
$state.go('todo');
});
}
$scope.register = function () {
$http.post('/api/register',$scope.newUser)
.success(function(data){
$scope.email=$scope.newUser.email;
$scope.password=$scope.newUser.password;
$scope.login();
})
};
});
I want to redirect to login page if authandicate is falied
How to fix this ?
In angularjs 1.4+ there is no
$http.get('/api/todo').success(function(data){
$scope.todos=data;
})
What you should do instead
$http.get('/api/todo').then(function(data){
$scope.todos=data;
})
And same with this $http.post which you have below.
Also after refreshing page rootScope is deleted and that is why nickname is blank after refresh.
You probably want to store nickname in localStorage or async promise based localForage.
If you chose async localForage on login you can emit custom event with rootScope and execute some function on this event which gather nickname from localForage. You might want to execute this function in some external controller which would wrap all app so when you assign $scope.nick you will have access to it across entire app. Same with $scope.auth = true, you will be able to build your app basing on this boolean for logged in using ng-if directive.
Inject $location to your controller as function parameter and try to redirect like so
$location.path('/todo' );
or
$location.url(YOUR_URL);
Also I don't really understand why you are doing two backend call for login, one inside another. You probably should do one $http.post which would return token in response. Then you could fix and simplify your function code to
$scope.login = function() {
var credentials = {
email: $scope.email,
password: $scope.password
}
$auth.login(credentials).then(function(response) {
$rootScope.authenticated = true;
$rootScope.currentUser = response.data.user;
$scope.loginError = false;
$scope.loginErrorText = '';
}, function(error) {
$scope.loginError = true;
$scope.loginErrorText = error.data.error;
$location.path('/todo' );
});
}
However I don't know your code from $auth service.
Remember to inject $location service.
redirectWhenLoggedOut seems to be an http interceptor.
I think the idea is that you redirect when the http call was not successful. So you need to add an http interceptor that catches the http error and redirects to the login page.
$httpProvider.interceptors.push('redirectWhenLoggedOut');
Don't forget to inject the $httpProvider;
I have the following Security Config with Spring 3.2
.formLogin()
.loginPage("/login.html")
.usernameParameter("username").passwordParameter("password")
.loginProcessingUrl("/perform_login")
.defaultSuccessUrl("/welcome", false)
.failureUrl("/notexistpage")
I got the conclusion that defaultSuccessUrl() and failureUrl() was performed, related page loaded but was ignored by Angularjs frontend with ng-route/$location.
I have a Single Page Application on index.html with has the following ng-view block, it is basically a bootstrap Nav panel with a login button:
<li class="nav-item" ng-show="!authenticated">
<a class="nav-link active" id="login-tab" data-toggle="tab" role="tab" aria-controls="login" aria-expanded="true" href="#!/login">Login</a>
</li>
<div ng-view class="container"></div>
A login page would be displayed on ng-view block, and I have ng-click() function which submit the form to javascript function following:
$http({
method: 'POST',
url: 'perform_login',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
transformRequest: function(obj) {
var str = [];
for(var p in obj)
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
return str.join("&");
},
data: credentials
}).then(function (response) {
$rootScope.authenticated = true;
callback && callback();
}, function (response) {
$rootScope.authenticated = false;
callback && callback();
});
The page would have different behaviour depending on success/failed login, but nonetheless return a status code of 200/302/304, which javascript considered as success. Also the login form would remain in the block, neglecting Java Success Handler. The page did requested the html defined in defaultSuccessUrl(), but it was not shown on the page.
I could only hard code $location.path("/") after login success ( first callback of javascript ), and force user to login before accessing any page.
I think you can't use Spring Securitys defaultSuccessUrl here.
The control of the webflow should be on the client. Take a look at the official example, they're doing the same.
angular.module('hello', [ 'ngRoute' ]) // ... omitted code
.controller('navigation',
function($rootScope, $http, $location) {
var self = this
var authenticate = function(credentials, callback) {
var headers = credentials ? {authorization : "Basic "
+ btoa(credentials.username + ":" + credentials.password)
} : {};
$http.get('user', {headers : headers}).then(function(response) {
if (response.data.name) {
$rootScope.authenticated = true;
} else {
$rootScope.authenticated = false;
}
callback && callback();
}, function() {
$rootScope.authenticated = false;
callback && callback();
});
}
authenticate();
self.credentials = {};
self.login = function() {
authenticate(self.credentials, function() {
if ($rootScope.authenticated) {
$location.path("/");
self.error = false;
} else {
$location.path("/login");
self.error = true;
}
});
};
});
I am calling a service method in my controller to return an object. I struggled a lot to figure out the exact implementation difference between a service and a factory.
According to what I learned service instantiates with new and returns this to the calling controller.
I am trying to accomplish the same here..calling a service function in controller and storing the returned object in controller's $scope.user variable as follows:
controller:
app.controller('LoginFormController',['$log','$scope','userAngService','userBean',function($log,$scope,userAngService,userBean){
$scope.user = {};
$scope.login = function(val){
userAngService.login(val);
$scope.user = userAngService.user; //accessing service's user variable
//$scope.user = userAngService.user;
console.log($scope.user); //empty object being logged in console
};
}]);
service:
app.service('userAngService',['$http','$log','$rootScope','$location',function($http,$log,$rootScope,$location){
this.user = {};
this.login = function(val){
console.log('from angular service');
$http({
method: 'GET',
url: 'http://localhost:7070/messenger/webapi/messages'
}).success(function(data){
user = data;
console.log(user); //successfully logging "user" in console
});
};
return this;
}]);
Above in the service the login function(val) is changing the user variable defined in the service itself.
I can make the function to return this.user but still am getting the same result.
What changes should I make here?
And here is what I am getting in console:
You are calling $http service internally on your userAngService service.
Its a asynchronous service call so, just return the promise object to the controller and then assign values to $scope.user.
Controller
app.controller('LoginFormController', ['$log','$scope','userAngService','userBean',function($log,$scope,userAngService,userBean){
$scope.login = function(val){
userAngService.login(val).then(function(data){
$scope.user = data;
console.log($scope.user);
});
};
}]);
Service
app.service('userAngService',['$http','$log','$rootScope','$location',function($http,$log,$rootScope,$location){
this.user = {};
this.login = function(val){
console.log('from angular service');
return $http({
method: 'GET',
url: 'http://localhost:7070/messenger/webapi/messages'
});
};
}]);
Service
app.service('userAngService',['$http','$log','$rootScope','$location',function($http,$log,$rootScope,$location){
this.user = {};
this.login = function(val){
console.log('from angular service');
// here you are returning Promise
return $http({
method: 'GET',
url: 'http://localhost:7070/messenger/webapi/messages'
}).success(function(data){
user = data;
console.log(user); //successfully logging "user" in console
return user;
});
};
}]);
The first thing is, you don't need to return anything from service. This is the one of the difference from factory. In factory you need to return an object, but no need to return any object in service
controller
app.controller('LoginFormController',['$log','$scope','userAngService','userBean',function($log,$scope,userAngService,userBean){
$scope.user = {};
$scope.login = function(val){
userAngService.login(val).then(userName){
$scope.user = userName;
console.log($scope.user);
};
};
}]);
Your controller is logging a blank object which is correct since it is a blank object. Can you try this?
app.service('userAngService',['$http','$log','$rootScope','$location',function($http,$log,$rootScope,$location){
var vc = this;
vc.user = {};
vc.login = function(val){
console.log('from angular service');
$http({
method: 'GET',
url: 'http://localhost:7070/messenger/webapi/messages'
}).success(function(data){
vc.user = data;
console.log(user); //successfully logging "user" in console
});
};
}]);
OR this...
app.service('userAngService',['$http','$log','$rootScope','$location',function($http,$log,$rootScope,$location){
var vc = this;
vc.user = {};
vc.login = function(val){
console.log('from angular service');
$http({
method: 'GET',
url: 'http://localhost:7070/messenger/webapi/messages'
}).success(function(data){
$rootScope.$apply(function(){vc.user = data;});
console.log(user); //successfully logging "user" in console
});
};
}]);
Your Service needs to return an abject. This should work:
app.service('userAngService',['$http','$log','$rootScope','$location',function($http,$log,$rootScope,$location){
this.user = {};
this.login = function(val){
console.log('from angular service');
$http({
method: 'GET',
url: 'http://localhost:7070/messenger/webapi/messages'
}).success(function(data){
user = data;
console.log(user); //successfully logging "user" in console
});
};
return this;
}]);
or if you want to use private and public funtions (module pattern) in javascript. Do:
app.service('userAngService',['$http','$log','$rootScope','$location',function($http,$log,$rootScope,$location){
var user = {};
var handleRequestData = function(data){
user = data;
console.log(user); //successfully logging "user" in console
};
var login = function(val){
console.log('from angular service');
$http({
method: 'GET',
url: 'http://localhost:7070/messenger/webapi/messages'
}).success(handleRequestData);
};
/* while you returning just the login function, user and handleRequestData stay private */
return {
login: login
};
}]);
You have to user promise in order to get data from a service via HTTP request because what happened when your http request give response back to controller there is no handler to receive it.
Controller should be :
app.controller('LoginFormController',['$log','$scope','userAngService','userBean',function($log,$scope,userAngService,userBean){
$scope.user = {};
$scope.login = function(val){
userAngService.login(val).then(function(res){
$scope.user = res;
//$scope.user = userAngService.user;
console.log($scope.user);
console.log(userAngService.user);
});
};
}]);
Service should be :
app.service('userAngService',['$http','$log','$rootScope','$location', '$q',function($http,$log,$rootScope,$location,$q){
this.user = {};
this.login = function(val){
var deferred = $q.defer();
var promise = deferred.promise;
console.log('from angular service');
$http({
method: 'GET',
url: 'http://localhost:7070/messenger/webapi/messages'
}).success(function(data){
user = data;
console.log(user); //successfully logging "user" in console
deferred.resolve(user);
});
return promise;
};
return this;
}]);
I have not tested this code and i think it will work for you but if not the case please let me know.
I'm using Angular in an application. After getting a specific object (a movie in my case), I'm assigning the object to $scope ($scope.movie = response), so that I can use it in the view. The problem is that my view seems not to display anything I use in $scope. I've tried deleting everything and doing a dummy test like $scope=name="whatever" and when I use something like {{name}} in the view nothing is rendered. Have anyone faced this problem ? I've already searched for this error, and it seems like it would be a good idea to use $apply(). I've tried that and it didn't work. The function that fetches the data is below:
var app = angular.module('movies');
app.factory('Films', ['$resource',function($resource){
return $resource('/films.json', {},{
query: { method: 'GET', isArray: true },
create: { method: 'POST' }
})
}]);
app.factory('Film', ['$resource', function($resource){
return $resource('films/:id.json', {}, {
show: {method: 'GET' },
update: { method: 'PUT', params: {id: '#id'} },
delete: { method: 'DELETE', params: {id: '#id'} }
});
}]);
app.controller('MoviesController', ['$scope', '$http', '$location', '$resource', '$routeParams', 'Films', 'Film', function($scope, $http, $location, $resource, $routeParams, Films, Film){
$scope.movies = Films.query();
$scope.user = document.getElementById('name').innerHTML; // Find a better way to interact with devise via angular
$scope.createMovie = function() {
$scope.movies = Films.query();
$http.get(
'/categories.json'
).success(function(data,status,headers,config){
$scope.categories = data;
}).error(function(data, status, headers, config){
alert("There was an error while fetching the categories on the database. Error " + status);
});
$location.path("/" + 'new').replace();
};
$scope.listMovies = function() {
$location.path("/").replace();
};
$scope.save = function(){
if($scope.form.$valid){
Films.create({film: $scope.movie}, function(){
$scope.form.$setPristine();
}, function(error){
alert("Movie not created");
});
}
};
$scope.deleteMovie = function(movie){
Film.delete(movie);
$scope.movies = Films.query();
};
$scope.viewDetails = function(movie){
$scope.name="ola";
alert(movie.id);
$location.path("/" + movie.id);
var Movie = $resource('films/:filmId'+'.json', {filmId: '#id'});
$scope.movie = Movie.get({filmId: movie.id});
$scope.movie.$promise.then(
function(response){
$scope.$apply();
$scope.movie = response;
console.log("filme e: " + response.name);
},
function(error){
console.log("request failed");
}
);
};
}]);
I had a look at your repository and I think where your problem is. You are trying to reuse the MoviesController in all of your routes. But AngularJS will create a new instance for every route and therefore you can't access your previous data because it will be destroyed.
So I would start by creating a separated controller for each view, so you can move the code of your viewDetails method to a new MovieDetailController. To have access to the movie id in this controller, you need to use the $routeParams service.
angular.module('movies').controller('MovieDetailController', MovieDetailController);
function MovieDetailController($scope, $resource, $routeParams) {
var Movie = $resource('films/:filmId'+'.json', {filmId: '#id'});
Movie.get({filmId: $routeParams.id}).then(
function(response) {
$scope.movie = response;
},
function(error){
console.log('request failed');
}
);
}
Change your route definition to use the new controller.
.when('/movies/:id', {
controller: 'MovieDetailController',
templateUrl: 'movie_details.html'
})
And now your viewDetails method in the MoviesController just need to redirect to the movie detail url.
$scope.viewDetails = function(movie) {
$location.path('/movies/' + movie.id);
}
I hope it works for you. Let me know when you try!
I have this template in my project:
<script type="text/ng-template" id="/challenges.html" ng-controller="ChallengeCtrl">
<main>
<h3 class="headingregister">Start challenge reeks</h3>
{{getUser()}}
<form name="startChallengesForm" ng-submit="getChallenges()">
<button type="submit" class="btn btn-primary">Doe de challenges!</button>
</form>
</main>
</script>
The getUser() function should display the current logged in users info.
This is the ChallengeCtrl:
app.controller('ChallengeCtrl', ['$scope', 'auth',
function($scope, auth) {
$scope.isLoggedIn = auth.isLoggedIn;
$scope.currentUser = auth.currentUser;
$scope.logOut = auth.logOut;
$scope.getUser = function(){
auth.getUser(auth.currentUser()).getValue(function(value){
return value;
});
};
}]);
this is auth.getUser:
auth.getUser = function(usr){
return{
getValue: function(callback){
$http({
method: 'POST',
url:'http://groep6api.herokuapp.com/user',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
transformRequest: function(obj) {
var str = [];
for(var p in obj)
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
return str.join("&");
},
data : {username: usr}
}).then(function (result) {
//return result.data;
callback(result.data);
});
}
}
}
the problem is when I run the page, I see in developer tools that the function is being called over and over again, it should only display the users info.
How can I accomplish this?
Function calls in templates get executed each digest cycle. Simply fetch the user once in your controller and assign the value to the scope
auth.getUser(auth.currentUser()).getValue(function(value){
$scope.user = value;
});
and in your template, instead of {{getUser()}}
{{user}}