$location.path() from within a promise - javascript

I am trying to change the location after a promise is returned, however, the location will not change.
function register() {
firebase.auth().createUserWithEmailAndPassword($scope.email, $scope.password)
.then(function (user) {
console.log(user);
UserService.setUser(user);
$location.path('/home');
})
.catch(function (error) {
$scope.message = error.message;
alert($scope.message);
});
}
I'm assuming this is because it's within a promise's .then function, but I don't see documentation on $location that says it can't be used within a promise.
The console shows the user that was created and there are no errors.

From the AngularJS Documentation
When should I use $location?
Any time your application needs to react to a change in the current URL or if you want to change the current URL in the browser.
What does it not do?
It does not cause a full page reload when the browser URL is changed. To reload the page after changing the URL, use the lower-level API, $window.location.href.

The issue is related with the angular digest cycles.
To solve it, you can do:
$location.path('/home');
$rootScope.$apply();

Related

PasswordCredential does not store the data

I'm using the PasswordCredential API in Google Chrome and Edge to store authentication credentials, however this data is not saved.
I'm using the code below, and I only fire it if my AJAX login is successful.
var cred = new PasswordCredential({
name: account,
id: email,
password: password,
iconURL: 'https://example.com/favicon.ico'
});
navigator.credentials.store(cred).then(() => {
if (redirect !== undefined) {
$window.location.href = 'dashboard.html';
}
});
If inside the then function of the .store I put it to pull the saved data, null data is displayed.
navigator.credentials.store(cred).then(() => {
navigator.credentials.get({ password: true }).then(function (auth) {
console.log(auth); //Return NULL
console.log(auth.password); //Returns that does not exist
console.log(auth.id); //Returns that does not exist
console.log(auth.name); //Returns that does not exist
});
});
What did I do wrong in my code?
Edit
I believe I found the problem, as the user chooses and not saving the credentials in the browser the promise is pending.
Is there any way to do this automatically without the user having to manually save?
And how do I handle the promise when it completes?
As per W3C it store api will return the promise to make sure it has raised proper request to browser or not, it will not tell if use save or not. If you want to achieve this, you may create setInterval where you can regularly check by calling get from store.
In Reference to save, promise already shows fulfilled as below:

Initialize Current User Service on Application Start in AngularJS

I’m developing a Single Page Application with AngularJS.
When a user successfully logs in, a security token is stored in a cookie. Now, when he refreshes the page, the token will be sent to the backend, which returns a JSON object "currentUser" containing all the relevant information about the current user (as name, access-groups, profile picture, etc.).
The problem is, this is an asynchronous process of course, so when the controller starts another operation, say, just alerting the user’s name, this value will be undefined at that time.
Of course, I could set a timeout but is there a better solution?
I thought about a "currentUserService", which initializes first (sending the cookie and filling the user information with the backend response) and can only be processed after this initialization is completed.
But how can this be done? Or are there any other possibilities?
edit:
Hi guys,
thanks for the input!
Both of your suggestions seem to be very promising for asynchronous requests in general, but I think they might not fit perfectly for my concern:
The information about the current user only have to be requested once, so I would like to store them for the whole application (e.g. in the rootScope or a service) accessible from any controller without having to request them again in every controller (as in the callback or resolve-solution) but make sure that there won’t be any „timeout“ problems. Do you have any ideas?
You can resolve the user's data before the view loads either with ng-route or ui-router:
This example is written for ui-router:
.state('profile', {
url: '/profile',
controller: 'profileCtrl as vm',
resolve: {
user: function(AuthService) {
//Return a promise or an object to be resolved.
return AuthService.getUserFromToken(); //Say this is asynchronous and returns a promise
}
}
});
//In controller:
.controller('profileCtrl', function(... , user) {
//User data available here
this.user = user;
});
Please note if any errors arise during the resolve stage the state will not be loaded so you'll have to take care of the errors!
If a user refreshes you have to initialize everything. I assume the token is stored in localstorage or something and I assume this is angular 1.*. To do this I think you should call user-related functions from your http call-callback:
$scope.user = {};
$scope.getUser = function(){
$http({
method: 'GET',
url: '/someUrl'
}).then(function (res) {
$scope.user = res.data; //or whatever the response is
$scope.handleUserRelatedThings();
}).catch(function(err) {
//handle error
})
}
$scope.handleUserRelatedThings = function(){
//do things with $scope.user
alert($scope.user.name);
}
//on init
$scope.getUser();

ngSatellizer and Hapi/Bell not playing well together. Promise will not resolve for Twitter

I've been looking into possible solutions for this problem everywhere and can't seem to come up with anything in particular. From modifying my callback to various other tinkering in Hapi, I cannot get the promise to resolve on the Angular side. Strangely enough, the Twitter popup window will not close. If I close it manually, then the promise is rejected.
So, I've had to configure this to work around a rejected promise and it's just nasty...
//Angular Method
$scope.addTwitter = function(){
$auth.authenticate('twitter').then(function(res){
// success
}, function(res){
// failure
});
};
//Hapi Route
//Handles both POST and GET and will successfully authenticate for Twitter on /auth/twitter
var User = require('../../models/user');
module.exports = {
description: 'Twitter oAuth for the front-end.',
tags:['twitter'],
auth: {
strategies: ['twitter'],
mode: 'try'
},
handler: function(request, reply){
if (!request.auth.isAuthenticated){
return reply('Authentication failed due to: ' + request.auth.error.message).code(400);
}
if(request.auth.isAuthenticated){
User.addTwitter(request.state['hapi-cookie'].id, request.auth.credentials, function(err, results){
//Twitter Object
//console.log(request.auth.credentials)
reply().code(200);
//return reply.redirect('/#/thanks');
});
}
}
};
I have tried many different things over the last few days to get this working correctly. Alas I am here. Any help is appreciated!
For anyone in the future...
I ended up creating popups with the window object and long polling for changes in the session to notify a successful/failed attempt. This is easiest to do if you are using Angular with Hapi.

Defer creation of controllers/services on app run angularjs

every time the route change, I check if is set a variable with the current logged user details, if this variable is not set, I simply redirect to the login page.
Now I'm trying to achieve some kind of "remember me" functionality, so every time the route changes, if the variable user doesn't exist instead of redirecting to the login page, I check the local storage for an "authtoken", if is set I call a check function passing the authtoken to the server, that returns the user and the app will works the same way as after the manual login (that returns the same user as the check function).
I'm pretty sure this is not the best way to do that.
If I reload the page, first thing I run the check function that sends the authtoken to the server and wait for a response, if the user exists that value is assigned to a variable in the rootscope.
I have different services that use the variable in the rootscope, for example
angular.module('myApp')
.service('AdminService', function AdminService($rootScope, $http, StorageService) {
var authtoken = StorageService.get('authtoken');
var clientId = $rootScope.loggedUser.id;
and of course when the check function runs it waits for the response, but the service is being instantiated and $rootScope.loggedUser.id does not exists.
How can I tell the app to wait until the check function receive the response?
This is my code
....
}).run(function($rootScope, $location, AuthService, StorageService) {
var intended = $location.path();
AuthService.check().success(function(data) {
if(data.user) {
$rootScope.loggedUser = data.user;
$location.path(intended);
} else $location.path('login');
});
$rootScope.$on('$routeChangeStart', function() {
if($rootScope.loggedUser) {
....
For example if the user bookmarks the page "myapp.com/#/admin/users", I don't want to redirect to the login, if I have in local storage the authtoken, but this causes the controller to be instantiated, that uses the service, that needs the $rootScope.loggedUser.id that is not yet populated.
And I want to run the function check only when the page (re)loads (not every time the user change route).
I would advise to re-examine your design if you need a service call to check your auth token. Auth tokens typically have expiration time stored in them, so you could just check whether you are within the expiration period without calling the server. You are not compromising security, since auth tokens are signed by the server, and validated when you make server calls to do anything useful. So, under normal circumstances, no separate check call is needed.
But, if you insist, this use case is best handled with the resolve property of the route. This means, however, that every route that cares about the user's logged-in state would have to have a resolve property defined. This does not mean that you have to call the service on each route change.
Instead of using $rootScope.loggedUser to store the user, have it be cached by the AuthService and injected via the resolve parameter.
So, you could do the following:
$routeProvider
.when("some/secure/route", {
controller: "SecureCtrl",
resolve: {
user: AuthService.checkUser
}
});
And in your AuthService:
...
checkUser: function(){
var deferred = $q.defer();
if (cachedUser){
deferred.resolve(cachedUser);
} else {
AuthService.check().success(
function(data){
// cachedUseris defined at AuthService's scope
cachedUser = data.user;
deferred.resolve(data.user);
});
}
return deferred.promise;
}
Then, in your controllers:
.controller("SecureCtrl", function(user){
$scope.userId = user.id;
}

Calling Angular service from outside JS via .scope().call() hangs request

I'm working on adding a Google+ signin button to my Angular app and most of it is working, except for the handling of the callback result. The callback from the G+ signin is an outside JS function called signinCallback with looks like so:
//Handling the Google+ Signin right here
function signinCallback(authResult) {
angular.element($("#btnGooglePlus")).scope().handleGoogleSignin(authResult);
}
The only way I could figure out how to pass the authResult back into the controller was to call a controller method via element.scope(). handleGoogleSignin is called fine, and inside that function there is a http.get service call that looks like:
User.getSocialKey(key).then(function(data) {
console.log(data);
});
User is a service, and getSocialKey looks like:
getSocialKey: function(etag) {
console.log("Hit the social key service with the etag: " + etag);
return $http({
url: '/api/user/social',
method: 'post',
data: {etag:etag}
}).then(function(result) {
console.log("Returning promise from social service");
return result.data;
});
},
The first log statement gets hit fine, then nothing. Request is never sent. Now, if I go and click something on the page that has an ng-model attribute (example, a checkbox), the request is then sent and received just fine. So my question: Why is my Angular service call being suspended until I click on something? Why isn't it going through right away?
I've tried replacing getSocialKey with working service calls, same thing. I believe the issue comes down to calling the function with angular.element($("#btnGooglePlus")).scope().handleGoogleSignin(authResult); but I'm not sure. Anyone seen this before?
Sorry I can't test but I think you should call .$apply() since the action is triggered outside the AngularJS's scope.
function signinCallback(authResult) {
angular.element($("#btnGooglePlus")).scope().handleGoogleSignin(authResult);
angular.element($("#btnGooglePlus")).scope().$apply();
}

Categories

Resources