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

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();
}

Related

Keycloak js not executing THEN after init

I am trying to integrate Keycloak login into my React app and I'm trying to get the JWT from keycloak. Here is the code:
const [keycloakState, setKeycloakState] = useState<any>();
const login = () => {
const keycloak = Keycloak("/keycloak.json");
keycloak.init({onLoad: 'login-required'}).then(authenticated => {
console.log('kk', keycloak)
console.log('at', authenticated)
setKeycloakState({ keycloak: keycloak, authenticated: authenticated });
}).catch(err => {
alert(err);
});
console.log('log after')
}
The login function is triggered when a button is clicked. It redirects properly to keycloak, I can log in and I am properly redirected to the app. The problem is that after the redirect back to the app with proper login the code in the then part of the chain is not executed, and even the 'log after' does not appear in the logs. The catch error part works fine.
Why might this be happening? I have keycloak-js added to my project.
I used to face this problem before. The way that I passed is separating the "init" function and then invoke it later.
Here is my example on jsFiddle: 'https://jsfiddle.net/gzq6j3yu/1/'
Our solution was to use the functions onAuthSuccess and onAuthError avaliable on the KeycloakInstance keycloak-js provides. (The documentation around this is a little shaky but you can find them if you check out the source code.) As the names imply these functions get called when an auth attempt is successful or unsuccessful, respectively.
note: in the following snippets this._instance replaces OP's keycloak constant.
Basic code snippet:
import Keycloak from 'keycloak-js';
...
// pulled from a class's init function from a custom Keycloak helper class so won't translate one for one but you get the point.
this._instance = Keycloak(configObject);
this._instance.onAuthSuccess = () => {
// code to execute on auth success
};
this._instance.onAuthError = () => {
// code to execute on auth error
};
this._instance.init(initOptions)
...
We also had a getter to get the token on the KeycloakInstance (or empty string) on the same class. This is an easy way to refer to the token in your code to check if it actually exists, etc. Here's what that'd look like inside the class.
get token() {
return this._instance ? this._instance.token : '';
}
Hope this can help out some folks.
I think the reason your fulfilled callback is not executed is the way your app interacts with Keycloak. You initialize the Keycloak-Adapter with onLoad: 'login-required' which will redirect the user to Keycloak - which means the Javascript execution is interrupted at this point. Keycloak will redirect the user back to your app and since you wrapped the Keycloak-Adapter in a function which is only executed when a button is clicked, the promise callback is not executed.
Simple example:
// do this on page load
keycloak.init({onLoad: 'login-required'}).then((authenticated) => {
console.log('authenticated', authenticated)
})
You will not see a "authenticated", false in your console when you open up your app. If the user is not authenticated, he will be redirected (so no chance to execute that callback). If he then comes back and is authenticated, the callback will be executed and authenticated should be true.
If you want the user to click a button, a setup like this should work:
// do this somewhere early in your App or main.js and in a way that this code is executed on page load
const keycloak = new Keycloak(configuration);
keycloak.init({onLoad: 'check-sso'}).then((authenticated) => {
if (authenticated) {
// do what needs to be done if sign in was successful, for example store an access token
} else {
// handle failed authentication
}
}).catch(err => {
alert(err);
});
const login = () => { // this could be an on-click event handler
keycloak.login();
};
check-sso won't redirect the user to Keycloak if unauthenticated, so the user can trigger the login when needed.
Keep in mind that your JavaScript code will run twice and you have to cover both cases, first the user is not authenticated and needs to be redirected to Keycloak and a second time once the user comes back from Keycloak (then we should get the information that the user is authenticated in .then().

Angular ngResource $save Method Clears $resource Object

Using Angular 1.5.5 here:
Is there any way to tell Angular to ignore response body for particular requests (such as $save)? It drives me crazy that after I call $save, angular updates the model with the object returned by a server, which initially was supposed to be used to distinguish between different resolutions of the request. It results in unwanted form clear. Interestingly enough, this behaviour remains even if I send a 400 or 500 http status code.
In case you need more info, relevant code is below.
Controller:
'use strict';
angular
.module('app.operators')
.controller('OperatorNewController', OperatorNewController);
OperatorNewController.$inject = ['operatorsService', 'notify'];
function OperatorNewController(operatorsService, notify) {
var vm = this;
vm.done = done;
activate();
function activate() {
vm.operator = new operatorsService();
}
function done(form) {
if (form.$invalid) {
// do stuff
return false;
}
vm.operator.$save(function(response) {
if (response.success && response._id) {
$state.go('app.operators.details', {id: response._id}, { reload: true });
} else if (response.inactive) {
// do stuff
} else {
// do other stuff
}
}, function (error) {
// do other stuff
});
}
}
Service:
'use strict';
angular
.module('app.operators')
.service('operatorsService', operatorsService);
operatorsService.$inject = ['$resource'];
function operatorsService($resource) {
return $resource('/operators/:id/', {id: '#_id'}, {
'update': { method: 'PUT' }
});
}
Server request handler is also fairly simple:
.post('/', function (req, res) {
if (!req.operator.active) {
return res.status(500).json({ inactive: true, success: false });
}
// do stuff
return res.json({ success: true });
});
In either way I don't like the idea of having to send the entire object from server (particularily when it's a failed request), and even if I have to, I still need a way to send some extra data that will be ignored by Angular.
Your help is very much appreciated!
The $save method of the resource object empties and replaces the object with the results of the XHR POST results. To avoid this, use the .save method of the operatorsService:
//vm.operator.$save(function(response) {
vm.newOperator = operatorsService.save(vm.operator, function(response),
if (response.success && response._id) {
$state.go('app.operators.details', {id: response._id}, { reload: true });
} else if (response.inactive) {
// do stuff
} else {
// do other stuff
}
}, function (error) {
// do other stuff
});
UPDATE
It results in unwanted form clear. Interestingly enough, this behaviour remains even if I send a 400 or 500 http status code.
This behavior is NOT VERIFIED.
I created a PLNKR to attempt to verify this behavior and found that the $save method does not replace the resource object if the server returns a status of 400 or 500. However it does empty and replace the resource object if the XHR status code is 200 (OK).
The DEMO on PLNKR
It drives me crazy that after I call $save, angular updates the model with the object returned by a server
It helps to understand how browsers handle traditional submits from forms.
The default operation for a submit button uses method=get. The browser appends the form inputs to the URL as query parameters and executes an HTTP GET operation with that URL. The browser then clears the window or frame and loads the results from the server.
The default operation for method=post is to serializes the inputs and place them in the body of an HTTP POST. The browser then clears the window or frame and loads the results from the server.
In AngularJS the form directive cancels the browser default operation and executes the Angular Expression set by either the ng-submit or ng-click directive. All $resource instance methods including $get and $save, empty and replace the resource object with XHR results from the server if the XHR is successful. This is consistent with the way browsers traditionally handle forms.
In RESTful APIs, HTTP GET operations return the state of a server resource without changing it. HTTP POST operations add a new resource state to the server. APIs usually return the new resource state, with additional information such as ID, Location, timestamps, etc. Some RESTful APIs return a redirect (status 302 or 303) in which case browsers transparently do an HTTP GET using the new location. (This helps to Solve the Double Submission Problem.)
When designing RESTful APIs, it is important to understand how traditional browsers behave and the expectations of RESTful clients such as AngularJS ngResource.

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();

passport.authenticate() is not being executed via $http.get

I have the following angular controller
angular.module('app').controller('mvNavBarLoginCtrl', function($scope, $http){
$scope.login = function(){
$http.get('/auth/twitter').then(function(response){
if(response.data.success)
{
alert(response);
}
});
}
});
My routes.js has the following:
app.get('/auth/twitter', passport.authenticate('twitter'));
I can verify that I am getting here, however it doesn't seem the passport.authenticate is being executed.
More over if I try to hit the same route from html markup using (href="/auth/twitter" target="_self") everything works great.
Any idea what I am missing?
just looking at your code without checking passport api. I think passport. authenticate return 302 redirect as response. 302 redirect has empty body. your callback function will not follow 302. when you do this in a link, the browser do follow 302.

Backbone.js model.save() redirecting when save fails

I am currently working on a small Restful CRUD app, the frontend for which is written using the backbone.js framework. I understand most of the internals of how this application is working and its MVC structure, however I have several models, which when they are being edited, redirect unnecessarily. Perhaps a code example will help:
updateUser: function () {
this.model.save(null, {
success: function (model) {
app.navigate('userlist', {trigger: true, replace: true});
utils.showSuccessAutoClose("User", "The user has been updated.");
},
error : function (model, fail, xhr) {
utils.showCommsError(fail.status, fail.responseText);
}
});
}
The issue that I have is when this update method fails (when the server returns a 500 server for whatever reason), the page redirects from the current page
/pages/admin.jsp#useradd to /pages/admin.jsp for no reason, even if all of the code in the error handler commented out.
What is causing this redirect to happen?

Categories

Resources