AngularJS (Restangular): Making a promise block? Need to use it for validating a token - angularjs

I have stumbled upon Restangular for making calls to a rest service. It works great and returns a promise. I need to be able to have the call block. The reason for this is on a fresh page reload I am technically not loggged in but I may have a token stored in a cookie. i would like to validate this token against a rest service. Problem is that I need it to block.
If a timeout occurs or if its not valid that i can treat teh user as not authenticated.
This is the reason for wanting to block is that i would like to redirect them using $location.path to a new URL it not a valid token.
This doesn't happen on a specific route so i can't use resolve which is blocking. It technically happens on every route - I use the $on.$routeChangeStart and check an internal variable got LoggedIn or not, if not logged in i check for the stored token.
This happens on each Page refresh but not while navigating inside the application.
The affect I am trying to get is how Gmail works.
Look forward to any insight anyone has on this
Thanks

Basically you need to ensure that some asynchronous action occurs prior to any route change occurring, and in this case the action is authenticating a user.
What you can do is use the $routeChangeStart event that's emitted in order to add a property to the resolve object on the route like so:
function authenticate() {
if ( user.isAuthenticated ) {
return;
}
// Just fake it, but in a real app this might be an ajax call or something
return $timeout(function() {
user.isAuthenticated = true;
}, 3000);
}
$rootScope.$on( "$routeChangeStart", function( e, next ) {
console.log( "$routeChangeStart" );
next.resolve = angular.extend( next.resolve || {}, {
__authenticating__: authenticate
});
});
Since angular will wait for any promises in the resolve object to be fulfilled before proceeding, you can just use a pseudo dependency as in the example. Using something like that, you should be able to guarantee that your user is authenticating prior to any routes successfully executing.
Example: http://jsfiddle.net/hLddM/

I think the best way to do this might be to push the user around with $location.path, You can use .then() to effectively force a wait by leaving the user on a loading page.
var currentPath = $location.path();
$location.path(loadingScreen);
//Assuming you have some sort of login function for ease.
Restangular.login(token).then(
function(result) {
$location.path(currentPath)
},
function(error) {
$location.path(logInScreen)
}
);

If you're using ui-router, you could move to another state with the same URL, where you'd use that Restangular.login with the then, and in case of success go back to the "logged in" state, otherwise, go to the "log in" state where the user must enter his username and password.
If you're not using ui-router, you could implement something like that with some ng-switch.
So, upon arrival to the screen, you do that Restangular.login and by default you show loading page by setting some boolean to true. Then, if it doesn't succedd, you send him to the login, otherwise, you set loading to false and show page.
Anyway, I'd strongly recommend using ui-router, it rocks :)
Hope this works!

Related

How to reactively get currently logged-in user in angular-meteor and ionic?

I want $scope.me to be reactively represent the currently logged-in user, so that when a user logs out and logs back in as another, this variable is updated. Right now when a user logs out and logs back in as another user, the old value of $state.me persists. Once the page is reloaded this value is corrected. How do I fix this?
Here is my working, crappy solution in the controller:
$scope.$on('$ionicView.enter', function(e) {
if($rootScope.currentUser)
$scope.me = $rootScope.currentUser;
});
This worked, but reset the variable every time the user transitioned to this state... an ugly non-meteor solution.
Here is my current attempt:
$scope.me = ($scope.$meteorCollection(function(){
return Meteor.users.find({_id: Meteor.userId()});
}))[0];
This should work, since Meteor.userId() is reactive, and should force it's parent function to re-run when it changes, thus correcting $scope.me in realtime... but it doesn't.
Instead $scope.me is updated to the sanitized profile of the old user... meaning nothing but their _id and profile are visible. This is telling me that $scope.$meteorCollection is rerunning, but with an old value of Meteor.userId().
What am I missing? Thank you!
* EDIT *
Here's a twist
$scope.me = ($scope.$meteorCollection(function(){
console.log("$scope.me reset to "+Meteor.userId());
return Meteor.users.find({_id: Meteor.userId()});
}))[0];
prints the NEW user's ID to console when the user switches, but still returns the old user even though the query is re-run with correct values.
Have you tried using $rootScope.currentUser instead of trying to bake your own solution? Angular-Meteor automatically creates this $rootScope object for you, and the docs say that it is reactive.
Another possible solution would be to use the $meteor.autorun convenience method, but instead of autorunning based on a change in a $scope variable, you could have something like if(Meteor.userId()) or if(Meteor.user()).
As a matter of fact, this is what Angular-Meteor is doing under the covers anyway if you look at the source code.
From the docs:
// Updated to check for deep equality on the getReactively() call
$meteor.autorun($scope, function() {
$scope.userForScore = $scope.$meteorObject(Userinfo,
{user_id: $scope.getReactively('currentUser', true)._id}
);// Ultimately searches up scope chain for $rootScope.currentUser._id
});
I have found a solution:
$meteor.autorun($scope, function(){
var user = (Meteor.users.find({_id: Meteor.userId()}).fetch())[0];
if( user != null ){
$scope.me = user;
}
});
$meteor.autorun automatically reruns reactive dependencies included within the function. In this case, Meteor.userId(). Thus whenever that is changed by meteor, the function body re-runs, setting $scope.me to the current user if one is logged in.
Thank you to JacobWuzHere for the tips!

Binding to {{}} with firebase simple login auth

I'm using email/password for an app. When I register a user, they choose a username and it binds to a $rootScope variable called authUser.
I then use it in a divbar at the top that indicates that they're logged in as so:
<span>welcome {{authUser}}</span>
This works fine and their chosen username binds to it after they register, as I bind their username to the variable in a callback.
However, when I hard reload the page, it doesn't bind anymore. It seems like it takes a moment for the page to recognize that the user is logged in,
And by that time, angular has already run through its $apply cycle and the only thing I see on the navbar is "welcome".
Is there a way to know at what point (after a hard page reload) the logged in user is recognized? If possible, I'd like to chain some sort of callback or .then() function to bind or apply $rootScope.authUser then.
I guess this may be somewhat related to an asynchronous call, where the data must be used with a promise to guarantee success.
Any tips on this would be appreciated. Ill have access to my source code in about an hour if you need specific details but I think this problem may be more conceptual than about actual code implementation.
Thanks a bunch SO!
If you want to get notified when the user logs in you can register an event listener like this:
$rootScope.$on("$firebaseSimpleLogin:login", function(e, user) {
});
If you just want to know whether the login state has been initialized then you can use $getCurrentUser():
var auth = $firebaseSimpleLogin(this.dbRef);
$scope.loginStateDetermined = false;
auth.$getCurrentUser().then(function (user) {
$scope.loginStateDetermined = true;
});
Checkout the docs for more info:
login-related-events
$getCurrentUser()

How to use $resource in AngularJS properly for building a client app?

I've been following this tutorial http://draptik.github.io/blog/2013/07/28/restful-crud-with-angularjs/. I implemented a Grails backend with it instead of the Java one in the tutorial.
I've got the data coming back and forth, with one issue. If I create/update/delete a user, I don't see the changes reflected on my user list when I am redirected back. I have to refresh the page to see the updates.
Looking at the network traffic for an edit, it looks like it does a PUT and fires off the GET before the PUT is complete. Assuming this is because $resource returns a promise so things can be done asynchronously. So how do I handle this so that when $location redirects me, my list is up to date?
I'm guessing the options are to wait for the PUT to complete before redirecting/querying for the list, or to somehow manually manage the $scope.users to match the request?
Or maybe this tutorial is just a bad example? Maybe there is a better way to do it (still using $resource)?
Note: I've seen Restangular out there, and I've seen $http with success callbacks, but I would like to understand the situation above.
One way to overcome this issue would be to not redirect to the list page, till you get a callback, and then do a redirect. You can show some busy indicator till that time. The resource call looks like this.
resource.update(config,data,function() { //gets called on success},
function(error) { //gets called on failure});
In real life scenario waiting for the response of update makes sense as you want to handle the error and success scenarios on the same page.
I don't see your code anywhere so i'm just assuming (based on what you wrote and your current problem)
You are probably doing a full (or partial) get each time you changed a user and (re)binding the result to your scope. Doing this in the callback of the resource should actually start the digest cycle angular does to update modified objects. If you had been doing the fetching outside $resource - for example with custom/jquery ajax you would need to execute $scope.$apply()
What i really don't understand you would need to wait for the callback. You already know you added/modified a user. Instead of 'detaching' that user from your scope, modify it, post it to your rest server, then wait for callback, and reinserting it into the scope - why not modify it directly in the list/array you put on your scope?
var users = Users.get(function () {
$scope.users = users.record; // bind the resulting records to the scope
});
$scope.updateUser = function (user) {
resource.update(...); //pseudo
};
Then in your html, you will keep a reference to the currentUser and the div-list will update automaticly.
<div ng-repeat="user in users" ng-click="currentUser=user">{{user.Name}}</div>
<input ng-model="currentUser.Name">
<button ng-click="updateUser(currentUser);">Update</button>
If you don't want to see the update in the list while you type, but only once your callback fires or when you hit the button, would would instead use another ng-model for your input like this:
<input ng-model="tempUser.Name">
And you would then copy the value other in either the updateUser method or in the resource callback like this:
$scope.updateUser = function (user) {
user.Name = $scope.tempUser.Name; // should update automaticly
resource.update(...) // pseudo
}
Hope it helped!

Infinite Loop on ui-router's $stateChangeStart

Ramping up on angular, and ui-router
And struggling with redirecting to a different state if a precondition is not met:
I tried using an interceptor: (How do I preform a redirect in an angular interceptor).
But someone mentioned that handling $stateChangeState would be more appropriate. But I am still running into an infinite loop:
/**
* Check here for preconditions of state transitions
*/
$rootScope.$on('$stateChangeStart', function(event, toState) {
// which states in accounts to be selected
var accountRequiredStates = ['user.list', 'user.new'];
if(_.contains(accountRequiredStates, toState.name)){
event.preventDefault();
ApiAccount.customGET('get_current').then(function(resp){
// if I have a selected account, go about your business
if(resp.hasOwnProperty('id')){
$state.go(toState.name);
} else { // prompt user to select account
$state.go('user.select_account');
}
})
}
});
Can anyone suggest a better pattern (one that works)
Thanks!
Note: Similar problem different approach here: How do I preform a redirect in an angular interceptor
I don't think there's anything wrong with the general way you're trying to do this, though I'm not an expert. I do see a flaw in the implementation which looks like it could cause an infinite loop. Let's say the user tries to go to 'user.new' state. Your $stateChangeStart listener intercepts that, cancels it, does your customGET; then if the inner if condition is true (resp.hasOwnProperty('id')), you try to send the user to the same 'user.new' state. At which point, your $stateChangeStart listener intercepts it, cancels it, etc., over and over.
The way I avoid this problem in my code is to have a variable (in the service where I declare the listener) to help me bypass that check:
var middleOfRedirecting = false; Inside your inner if block within the resp.hasOwnProperty('id') check, set middleOfRedirecting to true; add a condition at the start of your $stateChangeStart listener to only call event.preventDefault() and redirect if middleOfRedirecting is false. You also would need a $stateChangeSuccess listener to set middleOfRedirecting back to false, resetting it for the next state change. (I feel like there should be a better way than this, but it at least works.)

close/leave page event with AngularJS

I am trying to implement a single page application that starts with a login form. For front-end I'm using AngularJS.
In the login's controller I check the authentication data, and if it is ok, I set a cookie (using $cookieStore service) with some user data and route to another view. Here, in the controller of the second view I check if the user from the cookie is empty and if yes, I redirect to the login view.
What I want to do is when the user close the browser, or if leaves the page the cookie to be removed.
I have tried to use:
$scope.$on("$locationChangeStart", function(){
$cookieStore.remove('user');
});
or
$scope.$on("$destroy", function(){
$cookieStore.remove('user');
});
but it does not work.
I want to avoid the scenario when a user log in (the cookie is set) and is redirected successfully to the second view, and after this close the browser/leaves the page (the cookie is still there). Another user write the url of the second view and because the cookie with the data of the first user is still there, he succeed to authenticate.
Can anyone help me with this?
plunker link
I am not sure. But maybe the browser closes before you even do anything in your logic.
Try this :
$scope.$on("$locationChangeStart", function(event){
event.preventDefault()
$cookieStore.remove('user');
});
try this code, it will works
$scope.$on('$locationChangeStart',function(event) {
if($scope.formsubmitted && $scope.myForm.$dirty){
var answer = confirm("Are you sure you want to leave this page?")
if (!answer) {
event.preventDefault();
}else{
$cookieStore.remove('user');
}
}
});

Resources