My angularjs $http.post .then method never gets executed neither the .catch method is executed although the server is working fine and returning what I want it to return.
This is my node js controller, doing validation for email exists
module.exports.add = function(req, res, next) {
var user = new User(req.body);
User.find({email:req.body.email},function(err,doc){
if(err) {
{
err.status = 406;
return next(err);
}
}
if(doc.length){
return res.status(409).json({message: 'Email already exist'});
}
else {
user.save(function(err, doc) {
if (err) {
err.status = 406;
return next(err);
}
return res.status(201).json({message: 'Your account has been created successfully. Please login to continue.'});
}) //end of user.save
} //end of else
});
};
This is my angular controller
quizApp.controller("createAcc",['$scope','$http','$location','$timeout','signup',function ($scope,$http,$location,$timeout,signup) {
$scope.createAcc = function () {
signup.createAcc($scope.fName, $scope.email, $scope.password)
.then(function () {
console.log("it happened");
})
.catch(function (err) {
if(response.status === 409){
$scope.emailExist = true;
}
console.log("12345");
});
}
}
]);
This is my angular service
quizApp.factory('signup', ['$http',function ($http) {
return {
createAcc : function (fName,email,password) {
var data = {fName:fName,email:email,password:password};
return $http.post('/api/v1/user/add',data)
}
}
}]);
So now the problem is, my server is doing the right validation and sending me the status of 409 i.e, email exists but in my angularjs controller the .then function or the .catch function never runs. In the console it just throws an error of 409 but the .then function or the .catch function never gets executed. I have also tried with .success( function successCallback()}, function errorCallback(){} ) , that doesn't work either. I don't understand where I am doing wrong, everything works fine with Postman (the application) though but I am unable to debug the issue here. I think I am doing something wrong in my angular code , can anyone please tell me why my .then and .catch function never gets executed?
inject the $q to the factory also
change
['$http','$q',function ($http) {
to
['$http','$q',function ($http,$q) {
Related
I am using Node.js and have a database call being performed with a promise. I setup a '.then/.catch' to handle the result of the promise. That part all seems to work without any issue. After I do some processing of the data returned from the database I am attempting to 'redirect' to another route within the '.then' statement. This re-direction seems to be causing a problem with the '.catch' statement. I have no error issues if I remove the 'res.redirect'...is there a workaround? Any suggestions appreciated.
code:
const db = require('./routes/queries');
//**Query PostGres database...
db.getMembers()
.then(function(value) {
console.log('Async success!', value);
//do some processing on the data returned from the promise call here...
//if I insert a redirect here...such as "res.redirect('/_members');"...
//I get a "Caught an error! ReferenceError: res is not defined" message...?
//evidently the redirect fires the '.catch' statement below...why?
})
.catch(function(err) {
console.log('Caught an error!', err);
});
I recommend providing a reusable promise in case you need to find your members list elsewhere in your Express app
const db = require('./routes/queries');
const findMembers = () => {
return db.getMembers()
.then(members => {
return members
})
.catch(err => {
console.log('Caught an error!', err);
});
}
//inside your express app
app.get("/some_route", (req, res) => {
return findMembers()
.then(members => {
//do something with your members list
res.redirect("/redirect_route")
})
})
I use Auth0 for signin and I have the following code:
$scope.login = function () {
$scope.loginFailed = false;
$scope.loading = true;
loaderService.show();
let credentials = {
email: $scope.email,
password: $scope.password
};
principal.signin(credentials)
.then(() => {
$state.go('main.index');
})
.catch(e => {
showError(e.error_description);
loaderService.hide();
});
};
The principle service contains signin function:
signin(credentials) {
return new Promise((resolve, reject) => {
this.auth.signin({
connection: 'Username-Password-Authentication',
email: credentials.email,
sso: false,
password: credentials.password,
authParams: {
scope: 'openid name email'
}
}, this.onLoginSuccess.bind(this, resolve, reject), this.onLoginFailed.bind(this, reject));
});
}
So, as you can see I create promise and pass resolve/reject to Auth0 callbacks.
Callbacks are pretty simple:
onLoginSuccess(resolve, reject, profile, token) {
let userData = this.collectData(profile);
... store token
return this.syncCurrent(userData) //request to server
.then(() => {
return resolve();
})
.catch(() => {
this.signOut();
return reject();
});
}
onLoginFailed(reject, error) {
return reject(error.details);
}
So, let's return to the first snippet. There are the following code:
principal.signin(credentials)
.then(() => {
$state.go('main.index');
})
.catch(e => {
showError(e.error_description);
loaderService.hide();
});
When I use correct email/password redirect works fine and I see main page. But when I use wrong email/password I see that catch block is executed, I see that values are changed in the debugger, but I don't see error block and loading image is not disappered. This is not problem in html, because I am refactoring code now, and all the code above was in one file and I didn't use promises and all worked fine. I tried to execute showError method before principal.signin(credentials) function, just for test, and I saw error and loading image was hidden. So, I think the problem is with promises and catch block exactly, but I don't know where.
PS. The showError is the following:
function showError(errorText) {
$scope.loading = false;
$scope.loginFailed = true;
$scope.loginErrorMessage = errorText;
}
The cause of this problem is using non-Angular promises. Angular promises, i.e. the $q service, take care of invoking the digest cycle after they are resolved. The digest cycle is the implementation of change detection in Angular 1, i.e. what notifies watchers and enables actions to take place.
Using $q solves this problem.
The then part of your code probably worked because it called $state.go() which in turn invokes the digest cycle. The catch part did not, so the changes never got a chance to fire the watchers.
I have made a web application in Node.js that lets users manage their mongoDB database. I have several click events that do CRUD actions that change the status of the database. On each ng-click I call the corresponding function to do the CRUD action. The problem is that, in each of these functions I call the a method called refresh which updates the page so it matches the current database info. Is there a way, I can just hook up the refresh function to be called whenever a ng-click event has ended?
Examples
$scope.dropDB = function (DB) {
$http.post('/dropDB', JSON.stringify({ 'DB': DB })).success(function (response) {
refresh();
});
};
$scope.addDB = function (DB) {
try{
var objson = JSON.stringify({ 'DB': DB }) ;
$http.post('/addDB', objson).success(function (response) {
console.log("DB Added: ");
refresh();
});
}catch(ex){
alert("Not a valid input type");
}
};
Make wrapper factory for your http call
['$http', function($http) {
return function(method, route, body) {
var args = method === 'POST' ? [route, body] : [route];
return new Promise(function(resolve, reject) {
return $http[method].apply(this, args).then(function(res) {
refresh();
resolve(res)
}).catch(function(err) {
reject(err);
})
});
}
}];
Then you can use it in your controller
myFactory('POST', '/database', {foo: 'bar'}).then(function(res) {
// at this point refresh() was already called
})
This may just come from a misunderstanding of how to best do authentication in a MEAN stack app, or my lack of knowledge in how promises and $http's .then() method works, but whenever I try to authenticate to my backend Node server with incorrect credentials, it is calling the success callback of $http's .then() method instead of the error callback. Here's my setup:
I'm using the jsonwebtoken and express-jwt packages, AngularJS interceptors to add the token the to request and check for status 401 responseErrors, a TokenService that sets/removes, etc JWTs, and a UserService to handle login, logout, etc.
From debugging, here's what's happening:
Request to login is sent out
Server catches request, looks for specified user and can't find them in database. Returns 401 error with JSON object including error message, etc.
HttpInterceptor engages the responseError method, correctly sees it's a status 401, removes any possible existing token, redirects to /login screen, and returns $q.reject(response).
UserService.login() correctly uses the error callback and does a return response.
Problem - the success callback in my login.js .login() method runs, instead of the second, error callback one. I have a feeling this has to do with what is discussed in this article about promise chaining, but my expertise has its limit right here and I can't quite understand what I should be doing next to tell the next callback in the chain that the previous one had an error...
Here's my setup:
Express:
authRoutes.js
authRoutes.post("/login", function (req, res) {
User.findOne({username: req.body.username}, function (err, user) {
if (err) res.status(500).send(err);
if (!user) {
res.status(401).send({success: false, message: "User with the provided username was not found"})
} else if (user) {
bcrypt.compare(req.body.password, user.password, function (err, match) {
if (err) throw (err);
if (!match) res.status(401).json({success: false, message: "Incorrect password"});
else {
var token = jwt.sign(user, config.secret, {expiresIn: "24h"});
res.json({token: token, success: true, message: "Here's your token!"})
}
});
}
});
});
From debugging, when I log in with incorrect credentials, it's correctly hitting the res.status(401).send(...) line, so this part seems to be okay.
Angular:
app.js (includes HttpInterceptor)
var app = angular.module("TodoApp", ["ngRoute"]);
app.factory("AuthInterceptor", ["$q", "$location", "TokenService", function ($q, $location, TokenService) {
return {
request: function (config) {
var token = TokenService.getToken();
if (token) {
config.headers = config.headers || {};
config.headers.Authorization = "Bearer " + token
}
return config;
},
responseError: function (response) {
if (response.status === 401) {
TokenService.removeToken();
$location.path("/login");
}
return $q.reject(response);
}
}
}]);
app.config(function ($routeProvider, $httpProvider) {
$httpProvider.interceptors.push('AuthInterceptor');
$routeProvider
.when("/", {
templateUrl: "landing/landing-page.html"
});
});
userService.js
var app = angular.module("TodoApp");
app.service("UserService", ["$http", "TokenService", function ($http, TokenService) {
this.signup = function (user) {
return $http.post("http://localhost:8080/auth/signup", user).then(function (response) {
return response;
}, function (response) {
return response;
});
};
this.login = function (user) {
return $http.post("http://localhost:8080/auth/login", user).then(function (response) {
if (response.data.success) TokenService.setToken(response.data.token);
return response;
}, function (response) {
return response;
})
};
this.isAdmin = function (user) {
return user.admin;
};
}]);
login.js (Where the problem seems to manifest itself)
var app = angular.module("TodoApp");
app.config(function ($routeProvider) {
$routeProvider
.when("/login", {
templateUrl: "auth/login.html",
controller: "LoginController"
})
});
app.controller("LoginController", ["$scope", "$http", "$location", "UserService", "TokenService", function ($scope, $http, $location, UserService, TokenService) {
$scope.login = function (user) {
UserService.login(user).then(function (response) {
$location.path("/todo");
}, function (response) {
console.log("There was a problem: " + response);
});
}
}]);
That last part, UserService.login(user).then(function (response) { $location.path("/todo"); is the line that is running and trying to redirect the user to the list of Todo items, when I want it to run that console.log("There was a problem: " + response); line instead...
Like I said above, I have a feeling it has to do with chaining promises and how errors are handled partway through the chain instead of bubbling down through the chain. Not sure if I need to add a .catch() block like the site I mentioned above says to do. And even if that is the answer, I'm not entirely sure how to write that.
If there is a better way I should be organizing this, I'm definitely open to suggestions as well. I have to teach this to a class of students and want to make sure I'm teaching good practices.
Thanks in advance for the help!
Take a closer look at this part of your code:
this.login = function (user) {
return $http.post("http://localhost:8080/auth/login", user).then(function (response) {
if (response.data.success) TokenService.setToken(response.data.token);
return response;
}, function (response) {
return response;
})
}
Here you've provided error callback with a return value which is passed to the next callback in promise chain. The source of your confusion is that you still need to return rejected promise of throw from the callback if you want the error to propagate further. Otherwise, it effectively means that you've recovered from error situation and the next step in the flow will be success. This is what you have now.
In your case you either remove error callback altogether
return $http.post("http://localhost:8080/auth/login", user).then(function (response) {
if (response.data.success) TokenService.setToken(response.data.token);
return response;
});
... or make sure you return failed promise
return $http.post("http://localhost:8080/auth/login", user).then(function (response) {
if (response.data.success) TokenService.setToken(response.data.token);
return response;
}, function (response) {
return $q.reject(response);
});
... or throw:
return $http.post("http://localhost:8080/auth/login", user).then(function (response) {
if (response.data.success) TokenService.setToken(response.data.token);
return response;
}, function (response) {
throw new Error(response);
});
Have you tried to use $q.reject in error cases for then() calls?
E.g.
// remember to add $q to deps
this.login = function (user) {
return $http.post("http://localhost:8080/auth/login", user).then(function (response) {
if (response.data.success) TokenService.setToken(response.data.token);
return response;
}, function (response) {
$q.reject(response);
})
};
Related docs: https://docs.angularjs.org/api/ng/service/$q
I'm just getting started with Angular and Express and facing tough times with it. I come from a java background and want to learn Angular and Express and therefore trying out to build one small application.
What I'm trying to do: I have given a password reset link to user so as to change his password. The link is something like:
localhost:9000/reset/:token
Now, I have created a simple view which shows an input box to change his password if token is valid otherwise prints an error if token is invalid based on ng-show property of angular.
Problem: Before I can render my above created view, I want ui-router to check if the :token is valid or not. I will be using the information of validity of token in my controller to control ng-show property mentioned above.
After reading this I tried to leverage the $stateProvider.state functionality with a resolve so as to get the response of validation of token as pre-requisite. This will help me when rendering the actual view where I'm using ng-show technique to show error message or input box to change the password based on the ui-router resolve promiseObject.
What is the issue now ?
Well, after breaking my head for too long, I decided to post my question over here. Can anyone please help me here ?
My questions:
1. I'm able to get the data/err from the api call but somehow ui-router is not injecting it in my controller. Can anyone tell me am I doing anything wrong here ?
2. Right now if the token is not valid, I'm returning a 404 in response from my backend api. But the factory method in frontend takes it as err (Is this expected in Node.js ?) and the err is thrown which results in deferred.reject(). Now, if I go with ui-router definition, if the promise is not resolved then the view won't be rendered, right ? Is there any way by which I can pass this err also to my controller ? Reason why I'm asking to pass err is, my view's ng-show logic is based on the response code (4xx/2xx) depending on which I'll show error message or input box.
Code snippets:
Factory Method which calls the rest api:
isPasswordResetTokenValid: function(token, callback) {
var cb = callback || angular.noop;
var deferred = $q.defer();
return User.getUserByPasswordResetToken(token,
function(data) {
deferred.resolve(data);
return cb(data);
},
function(err) {
deferred.reject(err);
return cb(err);
}.bind(this)).$promise;
}
'use strict';
angular.module('scrubApp')
.config(function ($stateProvider) {
$stateProvider
.state('passwordreset', {
url: '/reset/:token',
templateUrl: 'app/account/passwordreset/passwordreset.html',
resolve: {
promiseObj2: function($stateParams, Auth){
var token = $stateParams.token;
console.log(token);
var response = Auth.isPasswordResetTokenValid({token: token})
.then( function(response) {
console.log(response); // shows response
if(response.status == 404) {
//$scope.token-expiry = true;
return response;
}
if(response.status == 200) {
// $scope.user = response.data;
}
})
.catch( function(err) {
console.log(err); // shows error
return err;
});
}
},
controller: 'ResetPasswordCtrl'
});
});
ResetPasswordCtrl controller:
'use strict';
angular.module('myApp')
.controller('ResetPasswordCtrl', function ($scope, User, Auth, Mailer, $stateParams, promiseObj2) {
$scope.errors = {};
$scope.user = {};
console.log(promiseObj2); // This is coming undefined
$scope.isTokenExpired = promiseObj2; // Not able to inject promiseObj2
$scope.isFormSubmitted = false;
});
Thanks in advance
Your resolve promiseObj2 should return a promise from promise service, so that your controller will wait till promise gets resolved.
return Auth.isPasswordResetTokenValid({token: token})
Update
If you want to handle some logic on failure of your token request then you could handle it in your promise itself, that can do couple of thing like
You could redirected to other page using $state.go('login') or $state.go('error') page.
Code
promiseObj2: function($stateParams, Auth, $state){
var token = $stateParams.token;
console.log(token);
return Auth.isPasswordResetTokenValid({token: token}) //added return here
.then( function(response) {
console.log(response); // shows response
if(response.status == 404) {
$state.go('error')
}
if(response.status == 200) {
return response;
}
})
.catch( function(err) {
console.log(err); // shows error
return err;
});
}
If you want to show html page anyhow if error occurs then You could also return data from the .then of promiseObj2 object that will have information about error message. So that error information is return to the controller
Code
promiseObj2: function($stateParams, Auth, $state){
var token = $stateParams.token;
console.log(token);
return Auth.isPasswordResetTokenValid({token: token}) //added return here
.then( function(response) {
console.log(response); // shows response
if(response.status == 404) {
return {status: 404, data: "doen't found resource"}
}
if(response.status == 200) {
return response;
}
})
.catch( function(err) {
console.log(err); // shows error
return err;
});
}
Then inside controller we will get resolve the promise of promiseObj2 object and then you will get the value of error in the .then function of it.
angular.module('myApp')
.controller('ResetPasswordCtrl', function ($scope, User, Auth, Mailer, $stateParams, promiseObj2) {
$scope.errors = {};
$scope.user = {};
promiseObj2.then(function(resp){
console.log(resp)
$scope.isTokenExpired = resp.isTokenExpired;
}, function(err){
console.log(err)
})
});
Update
If we want to handle a condition where server return 4XX status that means our ajax will call catch function that won't return promise though. We could solve this case by creating custom promise using $q and we will resolve it from the promiseObj2
Code
promiseObj2: function($stateParams, Auth, $state, $q){
var token = $stateParams.token,
deffered = $q.defer();
console.log(token);
Auth.isPasswordResetTokenValid({token: token}) //added return here
.then( function(response) {
console.log(response); // shows response
if(response.status == 404) {
//return {status: 404, data: "doen't found resource"}
deffered.resolve({status: 404, data: "doen't found resource"});
}
if(response.status == 200) {
//return response;
deffered.resolve(response);
}
})
.catch( function(err) {
console.log(err); // shows error
deffered.resolve(err);
});
return deffered.promise;
}