I have an app that works fine in Firefox, IE, Safari and Chrome. But in Chrome it stops working when the contoller below runs. By not working I mean that after $auth.login(user_info) executes the code inside the promise fails. For example if the call returns succesfully the app stops working. Nothing happens when you click on any links. $location.path(lpath) should go to the home page but it doesn't and toastr.success("Login Success"); should display a message but just shows a white popup.
If I run Chrome using Chrome.exe --disable-web-security then everything works. Does anyone have any suggestions?
angular.module("MyApp")
.controller("LoginCtrl", function($rootScope, $scope, $location, $auth, toastr) {
var lpath = "/home";
$scope.login = function() {
var user_info = { email:$scope.user.email, password:hex_sha512($scope.user.password)} ;
$auth.login(user_info)
.then(function(response)
{
if(response.data.rtn == "true")
{
$location.path(lpath);
toastr.success("Login Success");
}
else
{
toastr.error(response.data.msg);
}
})
.catch(function(response)
{
toastr.error("Host Login Error!");
});
};
});
$auth.login = function(user, opts) {
opts = opts || {};
opts.url = config.baseUrl ? utils.joinUrl(config.baseUrl, config.loginUrl) : config.loginUrl;
opts.data = user || opts.data;
opts.method = opts.method || 'POST';
return $http(opts).then(function(response) {
shared.setToken(response);
return response;
});
};
Shared.setToken = function(response) {
var accessToken = response && response.access_token;
var token;
if (accessToken) {
if (angular.isObject(accessToken) && angular.isObject(accessToken.data)) {
response = accessToken;
} else if (angular.isString(accessToken)) {
token = accessToken;
}
}
if (!token && response) {
var tokenRootData = config.tokenRoot && config.tokenRoot.split('.').reduce(function(o, x) { return o[x]; }, response.data);
token = tokenRootData ? tokenRootData[config.tokenName] : response.data[config.tokenName];
}
if (!token) {
var tokenPath = config.tokenRoot ? config.tokenRoot + '.' + config.tokenName : config.tokenName;
throw new Error('Expecting a token named "' + tokenPath + '" but instead got: ' + JSON.stringify(response.data));
}
storage.set(tokenName, token);
storage.set(emailName, response.data.email);
storage.set(permsName, response.data.permissions);
};
Posting as an answer because I don't have the rep.
I would say it is not in this code but in your controller for /home. Did you try removing code until it works?
Are you sure it did pass and tokens are valid? Try clearing the cache CTRL-SHIFT-END. You probably hit some code that wants a valid token and can't continue because you didn't catch failed case.
ANSWER (from Tony): Seems that the periods in the string cause the problem. If I remove the periods it works fine.
Related
I have a problem with getting request in my api from Angular.js. After i loged in to my app I keep the username in $rootScope.currentuser,
$rootScope.$on('$routeChangeStart', function () {
if (Auth.isLoggedIn()) {
app.isLoggedIn = true;
Auth.getUser().then(function (data) {
$rootScope.currentuser = data.data.username;
app.username = data.data.username;
app.useremail = data.data.email;
app.loadme = true;
});
} else {
app.isLoggedIn = false;
app.username = '';
$rootScope.currentuser = '';
app.loadme = true;
}
});
then I call the middleware:
$http.get('/api/costs/' + $rootScope.currentuser).then(function (data) {
$scope.costs = data.data;
});
to get specific costs for current user, unfortunetelly this get request doesn't work, I checked $rootScope.currentuser right after login and this value is assigned properly. What is curious, when I manualy assigned in user name middleware
$http.get('/api/costs/' + 'test').then(function (data) {
$scope.costs = data.data;
});
the date is populated.
In my Angular controller I have a http call to a REST service that returns data in a database. This data are shown in a table in the partial view.
It happens random that the render of the html view is done before to get the callback with data, so I see a void table.
Here the code in the controller (I use services for some business logic and to implement the http call):
commonServices.find(vm.modelUri, null, vm.filter, function (err, msg, data) {
if (err || !data.length) {
$scope.noResults = true;
return;
}
$scope.docs = data; //docs is bind in the view
return;
});
Here the service for the http call:
function _commonServices(config, $http, $rootScope, $cookies) {
return {
find: function _find(modelUri, id, filter, callback) {
var url = config.servicesUri + '/' + modelUri;
if (id) {
url += '/' + id;
}
if (filter) {
if (typeof filter !== 'string') {
filter = JSON.stringify(filter);
}
url += '?filter=' + filter;
if (document.cookie) {
url += '&' + accessToken;
}
} else {
if (document.cookie) {
url += '?' + accessToken;
}
}
$http.get(url)
.then(function (data) {
//success
return callback(null, null, data.data);
},
function (data) {
//error
var err = true;
return callback(err, data.data.error.message);
});
}
}
}
The find service is used in other controllers and it seems it works good. I would know if it is possible to defer the render of the html table until the data are ready to be shown.
I would suggest the use of Angular's ng-cloak. It is a directive to prevent the html from being displayed until your angular app is finished loading. Check out the documentation here: ng-cloak
I have a JavaScript client that works in Chrome and Firefox, but fails in IE. Looking at the network trace in the IE debugger it shows that multiple of the AJAX calls have been aborted.
I've been able to get around it by setting the timeout to 0. I'd like to know if this is the correct way to handle my requests being aborted? Basically what could go wrong?
My initial thought was that I should capture and resend on error, and if multiple resubmits do not result in a completed request, finally alert the user. I'd still like to know how to do this even if the setTimeout is the proper way to address my immediate issue.
Also the application will process an excel workbook of addresses, call a web service to add some data to them and then allow the user to download the enhanced file.
This is what I have so far, first in the app.js
var requestWithFeedback = function (args) {
$(".loader").removeClass('hidden');
var oldConfig = args.config || function () { };
args.config = function (xhr) {
xhr.setRequestHeader("Authorization", "Bearer " + localStorage.token);
oldConfig(xhr);
extract: extract;
};
var deferred = m.deferred();
setTimeout(function () { // <== This solved in IE, but is this the way to handle this?
m.request(args).then(deferred.resolve, function(err){
if (err === "Invalid token!"){
m.route('/');
}
})}, 0);
$(".loader").addClass('hidden');
return deferred.promise;
}
From the model.js
app.MarkedAddresses.ProcessAddressBatch = function () {
var requestData = {
Addresses: app.MarkedAddresses.vm.addresses
}
return requestWithFeedback({
method: "POST"
, url: "API/server.ashx"
, data: requestData
, deserialize: function (value) { return value; }
})
.then(function (value) {
var responseJSON = $.parseJSON(value);
$.merge(app.MarkedAddresses.vm.results, responseJSON)
app.MarkedAddresses.vm.currentRecord(app.MarkedAddresses.vm.results.length);
app.MarkedAddresses.vm.progress(Math.max(app.MarkedAddresses.vm.progress(), ~~(app.MarkedAddresses.vm.currentRecord() / app.MarkedAddresses.vm.totalRecords() * 100)));
m.redraw(); //Force redraw for progress bar
return value;
},
function (error) { console.log(error) } // <== I thought error would show up here, but I never hit a breakpoint here.
);
}
Added loops
function process_wb(wb) {
app.MarkedAddresses.vm.results.length = 0;
$('.descending').removeClass("descending");
$('.ascending').removeClass("ascending");
app.MarkedAddresses.vm.progress(.1);
m.redraw();
var header = mapHeader(wb);
var addressJSON = to_json(wb, header);
app.MarkedAddresses.vm.totalRecords(addressJSON.length);
for (var i = 0; (i < addressJSON.length + 1) ; i += 1000) {
app.MarkedAddresses.vm.addresses = addressJSON.slice(i, Math.min(((i) + 1000), addressJSON.length));
app.MarkedAddresses.vm.response(new app.MarkedAddresses.vm.processAddressBatch());
}
}
Why isn't the error triggered in the section of the code?
It seems like I should add a deferred section here, but anything I've tried has been a syntax error.
I have the following controller :
app.controller('ListeSASController', function($scope, $rootScope, $routeParams, $location, userService, RefreshSASServices, $timeout){
this.IsUserLogged = function()
{
return userService.user().isLogged;
};
var promise = $timeout(RefreshSASServices.RafraichirSAS(), 100);
this.getSAS = function(){
return RefreshSASServices.getSAS();
};
$scope.$on('$locationChangeStart', function(){
RefreshSASServices.ArreterLesRafraichissements();
});
});
with the following service :
app.service('RefreshSASServices', function($http, userService, serverConfigService, $q, $timeout, $translate, constantsServices) {
var listeSAS = [];
var $this = this;
var promiseRefreshSAS;
// Getters
this.getSAS = function()
{
return listeSAS;
};
//Setters
this.clearDatas = function()
{
listeSAS = [];
};
// Communication with the server
$this.getServerUri = function()
{
return serverConfigService.getServerUri()+"majsvc/";
};
// Fonctions de rafraichissement
$this.ArreterLesRafraichissements = function()
{
if(promiseRefreshSAS !== undefined)
$timeout.cancel(promiseRefreshSAS);
};
$this.GetSASFromServer = function()
{
var promises;
if(userService.user().isLogged)
{
var uri = $this.getServerUri() + "getAllSAS/"+userService.user().UserObject._id;
promises = $http.get(uri)
.success(function(data, status, headers, config) {
// this callback will be called asynchronously
// when the response is available
return data;
}).
error(function(data, status, headers, config) {
// called asynchronously if an error occurs
// or server returns response with an error status.
return "";
});
}else{
promises = $q.when(!userService.user().isLogged)
}
return promises;
};
$this.RafraichirSAS = function () {
// functions that call
$this.GetSASFromServer()
.then(function(promise){
if(promise !== undefined && promise.data !== undefined)
{
listeSAS = promise.data;
//alert('refreshing the SAS list:' + JSON.stringify(listeSAS));
}else listeSAS = [];
promiseRefreshSAS = $timeout($this.RafraichirSAS, 3000);
})
.catch(function(error)
{
console.error("Error :", error);
promiseRefreshSAS = $timeout($this.RafraichirSAS, 7000);
});
};
});
When I load my page using routes :
.when('/listeSAS', {
templateUrl : './includes/sas/liste_sas.html',
controller : 'ListeSASController',
controllerAs : 'controller'
})
everything works fine, if my data changes on the server it gets updated on the UI, My UI is also displaying what I want. Everything is OK except that when the pages loads I get the following error :
TypeError: undefined is not a function
at file:///includes/libs/angular.js:14305:28
at completeOutstandingRequest (file:///includes/libs/angular.js:4397:10)
at file:////includes/libs/angular.js:4705:7
which is the function "timeout" of angular, and the line 14305 is :
try {
deferred.resolve(fn());
} catch(e) {
deferred.reject(e);
$exceptionHandler(e);
}
finally {
delete deferreds[promise.$$timeoutId];
}
Why angular is throwing this exception ? What did I do wrong ?
To be known :
On my login page I set 2 timeouts which I don't stop because they refresh "global" variables such as the number of private messages. Despite the error both timeout are still working.
I use node webkit with my application and it crashes maybe one in three times when I open this route (after 5-10 seconds).
Thank you for your help.
Is it that you're calling RafraichirSAS(), which returns undefined instead of passing in the function?
E.g, instead of
$timeout(RefreshSASServices.RafraichirSAS(), 100);
Do
$timeout(RefreshSASServices.RafraichirSAS, 100);
I'm really struggling with this because it should be very simple. I have a route with a controller defined called login. In my template I have the following data binding {{error}} which is defined in my controller after executing a method from a custom service, and resolving the returned promise.
Controller
app.controller("login", ['$scope','XMLMC', 'ManageSettings', function ($scope,api,ManageSettings) {
$scope.error = 'test';
$scope.login = function() {
var params = {
selfServiceInstance: "selfservice",
customerId: $scope.username,
password: $scope.password
};
var authenticated = api.request("session","selfServiceLogon",params).then(function(response) {
ManageSettings.set("session",response, $scope);
if(response.status === "ok") {
window.location.href = 'portal';
} else {
$scope.error = response["ERROR"];
console.log($scope.error);
}
});
};
}]);
The console shows Customer not registered. Showing that $scope.error has been updated appropriately, but the view never gets updated. My service is below, and please note that I am doing nothing "outside" of angular and so I should not have to $apply() anything manually.
app.factory("XMLMC", ['$http', '$q', function ($http, $q) {
function XMLMC($http, $q) {
$http.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8';
var that= this;
this.prepareForPost = function(pkg) {
return JSON.stringify(pkg);
};
this.request = function(service, request, params, host, newsession) {
var def = $q.defer();
var P = def.promise;
if(request === "analystLogon") {
newsession = true;
}
var call = {
service: service,
method: request,
params: params
};
if(host) {
call.host = host;
} else {
call.host = "localhost";
}
if(newsession) {
call.newsession = "true";
}
var pkg = {
contents: this.prepareForPost(call)
};
$http.post('php/XMLMC/api.php', jQuery.param(pkg)).success(function (response,status) {
that.consume(response, def);
}).error(function (response,status) {
def.reject(response,status);
});
return P;
};
this.consume = function(response, defer) {
console.log(response);
var resp = response[0],
digested = {},
i;
digested.status = resp["attrs"]["STATUS"];
var params = resp["children"][0]["children"];
for(i=0; i < params.length; i++) {
var key = params[i]["name"];
var val = params[i]["tagData"];
digested[key] = val;
}
defer.resolve(digested);
//return digested;
};
}
return new XMLMC($http, $q);
}]);
I've created a plunk here with the code exactly as it is on my test server. The routes and etc aren't working for obvious reasons, but you can at least see the code and how it works together
http://plnkr.co/edit/AodFJfCijsp2VWxWpbR8?p=preview
And here is a further simplified plunk where everything has one scope and one controller and no routes. For some reason, this works in the plunk but the $http method fails in my server
http://plnkr.co/edit/nU4drGtpwQwFoBYBfuw8?p=preview
EDIT
Even this fails to update
var authenticated = api.request("session","selfServiceLogon",params).then(function(response) {
ManageSettings.set("session",response, $scope);
$scope.error = "foo!";
if(response.status === "ok") {
window.location.href = 'portal';
}
});
It appears that $scope.$apply is indeed needed. See AngularJS - why is $apply required to properly resolve a $q promise?
To quote #pkozlowski.opensource:
In AngularJS the results of promise resolution are propagated asynchronously, inside a $digest cycle. So, callbacks registered with then() will only be called upon entering a $digest cycle.