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()
Related
If an user is coming from an specific page i need to do get some values out of a cookie and change what the users sees.
Now, the issue is that i cannot find a way to view what page the user is coming from.
EDIT: This is intended to capture when the users clicks back in a page and save the state of the previous page.
Any ideas?
Thanks in advance
Solved. Every time i load a page i'm saving the url, so when i get to this page i just have to read it to tell. Thanks!
You can use browser history in our javascript or you can write your last page in cookies and get the last link then update it
Using cookies will indeed fix this for you. So when a user goes to a new page - set a cookie like:
app.controller('myController',['$scope', '$location', $cookies], function($scope, $location, $cookies){
if($cookies.get('page') == '/index'){
//do stuff if user came from index
}
$scope.pageChanged = function(value){
$cookies.put('page', value);
$location.path('/index');
}
}
just make sure you use the pageChanged function to set your page every time user changes pages.
Using the $routeProvider you can use the resolve function to detect when a new route has been loaded.
Another way would be to listen for the event $routeChangeSuccessor $routeChangeError and get the information needed from the service $location or $route.
If you want a sample just ask me, I'll try to post one as soon as I have free time.
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!
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!
I'm pretty new to Angular and I'm using firebase as my backend. I was hoping someone could debug this issue. When I first go to my page www.mywebsite.com/#defaultHash the data doesn't load into the DOM, it does after visiting another hash link and coming back though.
My controller is like this:
/* initialize data */
var fb = new Firebase('https://asdf.firebaseio.com/');
/* set data to automatically update on change */
fb.on('value', function(snapshot) {
var data = snapshot.val();
$scope.propertyConfiguration = data.products;
console.log($scope.propertyConfiguration);
console.log("Data retrieved");
});
/* save data on button submit */
$scope.saveConfigs = function(){
var setFBref = new Firebase('https://asdf.firebaseio.com/products');
setFBref.update($scope.propertyConfiguration);
console.log("configurations saved!");
};
I have 3 hash routes say "Shared", "Registration", and "Home" with otherwise.redirectTo set to "Shared".(They all use this controller) Here's the error that occurs: (all "links" are href="#hashWhereever")
1) Go to website.com/#Shared or just refresh. Console logs $scope.propertyConfiguration and "Data Retrieved". DOM shows nothing.
2) Click to website.com/#Registration, console logs $scope data properly, DOM is loaded correctly.
3) Click back to website.com/#Shared, console logs $scope data properly yet this time DOM loads correctly.
4) Refresh currently correctly loaded website.com/#Shared. DOM elements disappear.
Since $scope.data is correct in all the cases here, shouldn't Angular make sure the DOM reflects the model properly? Why is it that the DOM loads correctly only when I am clicking to the page from another link.
I can "fix" it by adding window.location.hash = "Shared" but it throws a huge amount of errors in the console.
FIXED:(sorta)
The function $scope.$apply() forces the view to sync with the model. I'd answer this question myself and close it but I'm still wondering why the view doesn't load correctly when I correctly assign a value to $scope. If Angular's "dirty checking" checks whenever there is a possibility the model has changed, doesn't assigning a value to $scope overqualify?
Angular has no way to know you've assigned a value to $scope.variable. There's no magic here. When you run a directive (ng-click/ng-submit) or Angular internal functions, they all call $apply() and trigger a digest (a check of the dirty flags and update routine).
A possibly safer approach than $apply would be to use $timeout. Currently, if you call a write op in Firebase, it could synchronously trigger an event listener (child_added, child_changed, value, etc). This could cause you to call $apply while still within a $apply scope. If you do this, an Error is thrown. $timeout bypasses this.
See this SO Question for a bit more on the topic of digest and $timeout.
This doc in the Angular Developer Guide covers how compile works; very great background read for any serious Angular dev.
Also, you can save yourself a good deal of energy by using the official Firebase bindings for Angular, which already take all of these implementation details into account.
Vaguely Related Note: In the not-too-distant future, Angular will be able to take advantage of Object.observe magic to handle these updates.
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!