Service.js
this.userLogin = function (username, password) {
var dataBody = $.param({'username': username,'password': password});
return $http({
method: 'POST',
url: servicePathURL,
data: dataBody,
headers: {
"Authorization": "Basic",
"Content-Type": "application/x-www-form-urlencoded"
}
})
.then(function (response) {
$rootScope.globals = {
currentUser: {
username: username,
}
};
return response;
}).catch(function (error) {
throw error;
});
};
Controller.js
AuthenticationServiceLogin.userLogin($scope.username, $scope.password)
.then(function (response) {
if (response.status ==200) {
toaster.pop('success', "", "Login Successful");
$location.path('/home');
}
}).catch(function (error) {
toaster.pop('error', "", error.statusText);
});
In Controller.js, toaster.pop('error', "", error.statusText); is not being called when there is an exception while user logs in.
Also I have used $http method, is there any advantage to returning
a $q.defer() promise rather than an $http promise or considered as best practice ? If yes, how can I modify above $http code into promise ?
Your code appears to be fine. So long as you re-throw any errors encountered by your $http call, they should propagate all the way up to the controller. I'm inclined to say that any problems with your error handling are not in the code that you've posted.
There's no advantage to having a catch handler in service.js if you're not going to do any work with the error thrown. Service.js will want to look like this:
Service.js
this.userLogin = function (username, password) {
return $http({
method: 'POST',
url: servicePathURL,
data: $.param({'username': username,'password': password}),
headers: {
"Authorization": "Basic",
"Content-Type": "application/x-www-form-urlencoded"
}
})
.then(function (response) {
$rootScope.globals = {
currentUser: {
username: username,
}
};
return response;
// The catch block here is useless. The promise returned by $http will transmit any
// errors on its own.
//}).catch(function (error) {
// throw error;
});
};
In response to your second question: there is no advantage to using $q.defer() instead of returning the promise returned by $http itself, and a number of major disadvantages - namely that any exceptions thrown by your login method will disappear. See The Deferred Anti-pattern here for details: https://github.com/petkaantonov/bluebird/wiki/Promise-anti-patterns
$http is definitely the preferred option.
Related
I have an async fetch call which calls my backend to create a customer with an email address. If successful, the JSON returned is sent to a doNextThing() function.
If the backend returns a non-200 status code it also returns JSON like {"message": "Something went wrong"}. I want to catch the error and send that message to the console.
I've read dozens of slightly similar questions and edged close to an answer. So far I have the below, but if the backend's response was a 403 status code, for example, then the console outputs "FORBIDDEN". I think this is because the promise hasn't yet resolved, so doesn't yet have the full JSON response. Or something. But I can't work out what I'm missing.
async function createCustomer(email) {
return fetch("/api/create-customer", {
method: "post",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({email: email})
})
.then(function(response) {
if (response.ok) {
return response.json();
} else {
return Promise.reject({
status: response.status,
statusText: response.statusText
});
}
})
.then(function(returned_data) {
doNextThing(returned_data);
})
.catch(function(e) {
console.error(e.statusText);
});
}
Personally I recommend the async/await syntax if the version you're using supports it. It really simplifies the code and allows you to easily use the try/catch syntax.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await#Browser_compatibility
It seems it doesn't work in IE though if that's a dealbreaker.
async function createCustomer(email) {
try {
const response = await fetch("/api/create-customer", {
method: "post",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email: email })
})
if (response.ok) {
const returnedData = await response.json();
// if doNextThing is an async function, you can await it as well or just return it
doNextThing(returnedData);
} else {
throw {
json: await response.json()
status: response.status,
statusText: response.statusText
};
}
} catch (requestErr) {
// do what you like with the error
// this will be called if you "throw" above or
// if fetch() rejects
if (requestErr.json){
console.error(JSON.stringify(requestErr.json));
}
console.error("Request err:" + requestErr);
}
}
Let me know if that helps.
I have following function, which returns promise for authentication
authenticate: function(options) {
return new Promise((resolve, reject) => {
$.ajax({
url: this.tokenEndpoint,
type: 'POST',
headers: {'Content-Type': 'application/json', 'Accept-Encoding': 'gzip, deflate'},
data: JSON.stringify({
username: options.identification,
password: options.password
})
}).then(function(response) {
console.log(response) // I get here object with good properties
resolve({
lastLoginDate: response.lastLoginDate,
login: response.login,
name: response.name,
role: response.role,
token: response.id_token
});
}, function(xhr, status, error) {
if(error !== undefined) {
console.log(error);
}
var response = xhr.responseText;
reject(response);
});
});
When i call it and pass good username and password it returns "undefined", but when i pass bad properties, my "reject" works perfectly . Does someone know why my "then" doesn't return expected output?
this.get('session').authenticate('authenticator:custom', {'identification': identification, 'password': password})
.then((data)=>{
console.log(data); //Undefined <- here is my problem.
})
.catch((reason) => {
console.log(reason); // It works, when pass bad password!
this.set('errorMessage', reason.error);
});
Firstly, $.ajax returns a jQuery Promise which these days is 99.999% as good as a native Promise - so no need to wrap $.ajax in a Promise
Secondly, .then takes two callback arguments, onfullfilled and onrejected, both of these callbacks only receive a single argument - your onrejected callback will never have status and error arguments - so that code is flawed
In the onfullfilled callback, the argument is an array, being the [result, status, jqXHR] that you would get in the $.ajax success call
Similarly, in the onrejected callback, the argument is an array, being [jqXHR, status, errorThrown]
Given all that, the authenticate function can be written
authenticate: function(options) {
return $.ajax({
url: this.tokenEndpoint,
type: 'POST',
headers: {'Content-Type': 'application/json', 'Accept-Encoding': 'gzip, deflate'},
data: JSON.stringify({
username: options.identification,
password: options.password
})
})
.then(function(arr) {
var response = arr[0];
console.log(response);
// return instead of resolve
return({
lastLoginDate: response.lastLoginDate,
login: response.login,
name: response.name,
role: response.role,
token: response.id_token
});
}, function(arr) {
var xhr = arr[0], status = arr[1], error = arr[2];
if(error !== undefined) {
console.log(error);
}
var response = xhr.responseText;
// return a rejection - could "throw response" instead
return Promise.reject(response);
}
);
}
and FYI - ES2015+ you could write
.then(function([response, status, xhr]) {
.....
}, function([xhr, status, error]) {
....
I am new to Angular.js and trying to use factory for sending data via http post request. I am getting this error Cannot read property 'success' of undefined here is my code...
<!DOCTYPE html>
<html ng-app="myApp">
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<body>
<div ng-controller="myCtrl">
</div>
<script>
var app = angular.module('myApp', []);
app.factory('DataFactory', ['$http', function($http) {
return {
setData: function(stud) {
return
$http({
method: 'POST',
url: 'http://www.w3schools.com/jquery/demo_test_post.asp',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: stud
});
}
}
}]);
app.controller('myCtrl', function($scope, DataFactory) {
var stud = {
name: "Alex",
city: "Berlin"
};
DataFactory.setData(stud).then(function(response) {
console.log(response);
})
.catch(function(err) {
console.error(err)
})
.finally(function() {
console.log("Finished the chain");
});
});
</script>
</body>
</html>
The error I am getting is at line DataFactory.setData(stud).success... any help much appreciated...
So the docs state:
var promise = someAsyncMethodCall();
promise.then(function(greeting) {
alert('Success: ' + greeting);
}, function(reason) {
alert('Failed: ' + reason);
});
See the bottom, there are 2 functions to pass to the then function. So your code is:
DataFactory.setData(stud).then(function(response) {
console.log(response);
}, function (err) {
console.error(err)
});
The $q library is what $http uses under the hood.
See the promise api.
There are these methods:
then(successCallback, errorCallback, notifyCallback) – regardless of when the promise was or will be resolved or rejected, then calls one of the success or error callbacks asynchronously as soon as the result is available. The callbacks are called with a single argument: the result or rejection reason. Additionally, the notify callback may be called zero or more times to provide a progress indication, before the promise is resolved or rejected.
This method returns a new promise which is resolved or rejected via the return value of the successCallback, errorCallback (unless that value is a promise, in which case it is resolved with the value which is resolved in that promise using promise chaining). It also notifies via the return value of the notifyCallback method. The promise cannot be resolved or rejected from the notifyCallback method.
catch(errorCallback) – shorthand for promise.then(null, errorCallback)
finally(callback, notifyCallback) – allows you to observe either the fulfillment or rejection of a promise, but to do so without modifying the final value. This is useful to release resources or do some clean-up that needs to be done whether the promise was rejected or resolved. See the full specification for more information.
So to further your code, you could do:
DataFactory.setData(stud).then(function(response) {
console.log(response);
})
.catch(function (err) {
console.error(err)
})
.finally(function () {
console.log("Finished the chain");
});
--- EDIT ---
For completeness, there was another error that #Duncan said, that is the line break:
app.factory('DataFactory', ['$http', function($http) {
return {
setData: function(stud) {
return
$http({
method: 'POST',
url: 'http://www.w3schools.com/jquery/demo_test_post.asp',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: stud
});
}
}
}]);
The break between return and $http({ is causing an error. So it needs to look like this:
app.factory('DataFactory', ['$http', function($http) {
return {
setData: function(stud) {
return $http({
method: 'POST',
url: 'http://www.w3schools.com/jquery/demo_test_post.asp',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: stud
});
}
}
}]);
$http returns a Promise. you need to use .then() to register a callback on success.
The more general form is .then(successFn, failFn)
Your problem is in the function setData():
return
$http({
method: 'POST',
url: 'http://www.w3schools.com/jquery/demo_test_post.asp',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: stud
});
That is two statements: return just returns from the function, then the call to $http() is additional code that is never reached.
Be careful where you put line breaks in Javascript, move the $http({ up to the previous line.
Oh, and as others have said, using .success on the result from $http calls is deprecated, switch to using the normal promise methods instead.
Is it just markdown or do you have a newline between return and $http? If you return there, $http becomes unreachable. Functions can be below the return, since they are registered before code execution.
So you should write it like
return $http({...})
instead of
return
$http({...})
I am trying to implement angular resource for login like this
var data = new Login({
username: user.userName,
password: user.password
})
data.$save()
This is suppose to return some data to me if login is successful or return error if it is not.
What I want is the callback like the angular http post method like this .
data = JSON.stringify({
username: user.userName,
password: user.password
})
$http.post('API/sigin',data,
{
headers: {
'Content-Type': 'application/json'
}
}
)
.success(
function(response){
// success callback
console.log("doing sign in");
},
function(error){
// failure callback
return error
}
)
I switched to resource when http post failed me. It will just just hang perpetually and will later return error.
I am using angular 1.4.3.
any help, info will be appreciated
..factory.js
//Create a factory for the Login API
angular.module(...)
.factory('LoginEntity', ['$resource', function ($resource) {
return $resource(
'API/sigin',
{
save: {method: 'POST'},
update: {method: 'PUT'}
}
);
}])
...controller.js
angular.module(...)
.controller('xxxController', ['LoginEntity', function(LoginEntity){
//in Controller add LoginEntity dependency
LoginEntity.save({
username: user.userName,
password: user.password
},
function (response) {
//success callback
},
function () {
//error callback
//usually do some logging stuff here
});
}]);
I want to write a function in AngularJS that returns a value (actually it is a string). That value is returned by a http request, but async is driving me crazy.
My first attempt was:
this.readParameter = function(key) {
$http({
method: "GET",
url: "XXXXXXX",
headers: { 'Content-Type': 'application/json' }
}).then(function successCallback(response) {
return response.data;
}, function errorCallback(response) {
throw new Error("Error");
})
};
But of course it does not work because of Angular async features (response.data is undefined)
What is the way to do it? I just want to return the value (string), so I can use this function like
var a = readParameter("key1")
What you can do is define some variable with initial value outside function and on response set value inside success function instead of returning it.
Delegator pattern works great here to assign $http task to some service and use callback method for response.
Controller (Call Service for specific request) -> Service (Manage request params and other things and return factory response to Controller) -> Factory (Send request and return it to Service)
Basic example of Callback
var myVariable = '';
function myFunction (key, callback) {
$http({
method: "GET",
url: "XXXXXXX",
headers: { 'Content-Type': 'application/json' }
}).then(function successCallback(response) {
callback(response);
}, function errorCallback(response) {
throw new Error("Error");
})
};
function myCallbackFunction(response) {
myVariable = response.data; // assign value to variable
// Do some work after getting response
}
myFunction('MY_KEY', myCallbackFunction);
This is basic example to set value but instead use callback pattern from above example.
var myvariable = '';
function myFunction (key) {
$http({
method: "GET",
url: "XXXXXXX",
headers: { 'Content-Type': 'application/json' }
}).then(function successCallback(response) {
myvariable = response.data; // set data to myvariable
// Do something else on success response
}, function errorCallback(response) {
throw new Error("Error");
})
};
myFunction('MY_KEY');
Don't try to mix async and sync programming. Instead use a callback to use like
readParameter("key1", callback)
for example:
this.readParameter = function(key, callback) {
$http({
method: "GET",
url: "XXXXXXX",
headers: { 'Content-Type': 'application/json' }
}).then(function successCallback(response) {
callback(response)
}, function errorCallback(response) {
throw new Error("Error");
})
};
I resolve this by using promise:
Example :
in Service (invoicesAPIservice => invoicesapiservice.js) you use:
angular.module('app')
.service('invoicesAPIservice', function ($http) {
this.connectToAPI= function () {
return new Promise(function(resolve,reject){
var options = {
method:'GET',
url :'',
headers:{
'X-User-Agent': '....',
'Authorization': '....',
}
};
$http(options).then(function successCallback(response) {
resolve(response);
//console.log(response);
},function errorCallback(response) {
reject(response);
})
});
});
});
and in your Controller (mainCtrl=> mainCtrl.js):
angular.module('app').controller('mainCtrl', function($scope,invoicesAPIservice) {
$scope.connectToAPI=function () {
invoicesAPIservice.connectToAPI().then(function (content) {
console.log(content.statusText);
}.catch(function (err) {
//console.log(err);
alert("Server is Out");
});
}
});
And in your page : index.html:
<button ng-click="connectToAPI()"></button>
:)