Angularjs .then is not a function - javascript

I need to call a Factory method from a Controller method in Angular. But when I call the Factory method AuthFactory.login(username,password).then(), it shows Error like this
TypeError: AuthFactory.login(...).then is not a function
Plunker code here .
I think I am missing here a concept of AngularJS promises.

For login method, you are sending simple javascript object not promise object.
function login(username, password) {
var userInfo = {
"authToken": "6a65dd0c-b35a-429b-a9c0-f5c327ec5d6f",
"id": "1445138519"
};
return userInfo; // Returning javascript object
}
In plunkr, you have some commented code as below:
// $http.post(API_URL + '/auth/login', {
// email: username,
// password: password
// })
So instead returning userInfo, return $http.post object and then use then method.

You need to wrap the response in a promise, which then resolves.
.factory('AuthFactory', function AuthFactory($http, API_URL, $q) {
'use strict';
return {
login: login
};
function login(username, password) {
var deferred = $q.defer();
deferred.resolve(
{
"authToken":"6a65dd0c-b35a-429b-a9c0-f5c327ec5d6f",
"id": "1445138519"
});
return deferred.promise;
You return the promise, but resolve the actual result. If you want it to fail, you can do a deferred.reject() which returns the error condition in your then.
Plunker

Your are returning an object, not a promise.
If you want to return a promise that resolves to that object is pretty simple:
return $q.when(userInfo);
http://plnkr.co/edit/YeE8JmqcDSKqQY969Kpo?p=preview

Related

Angular 1.6.4 $http post callback not called [duplicate]

I'm finding it hard to understand the "deferred antipattern". I think I understand it in principal but I haven't seen a super simple example of what a service, with a differed promise and one with antipattern, so I figured I'd try and make my own but seeing as how I'm not super in the know about it I'd get some clarification first.
I have the below in a factory (SomeFactory):
//url = 'data.json';
return {
getData: function(){
var deferred = $q.defer();
$http.get(destinationFactory.url)
.then(function (response) {
if (typeof response.data === 'object') {
deferred.resolve(response.data);
} else {
return deferred.reject(response.data);
}
})
.catch(function (error) {
deferred.reject(error);
});
return deferred.promise;
}
The reason I am checking its an object is just to add a simple layer of validation onto the $http.get()
And below, in my directive:
this.var = SomeFactory.getData()
.then(function(response) {
//some variable = response;
})
.catch(function(response) {
//Do error handling here
});
Now to my uderstanding, this is an antipattern. Because the original deferred promise catches the error and simply swallows it. It doesn't return the error so when this "getData" method is called I have do another catch to grab the error.
If this is NOT an antipattern, then can someone explain why both require a "callback" of sorts? When I first started writing this factory/directive I anticipated having to do a deffered promise somewhere, but I didn't anticipate having to .catch() on both sides (aka I was sort of thinking I could get the factory to return the response or the error if I did a SomeFactory.getData()
Is this a “Deferred Antipattern”?
Yes, it is. 'Deferred anti-pattern' happens when a new redundant deferred object is created to be resolved from inside a promise chain. In your case you are using $q to return a promise for something that implicitly returns a promise. You already have a Promise object($http service itself returns a promise), so you just need to return it!
Here's the super simple example of what a service, with a deferred promise and one with antipattern look like,
This is anti-pattern
app.factory("SomeFactory",['$http','$q']){
return {
getData: function(){
var deferred = $q.defer();
$http.get(destinationFactory.url)
.then(function (response) {
deferred.resolve(response.data);
})
.catch(function (error) {
deferred.reject(error);
});
return deferred.promise;
}
}
}])
This is what you should do
app.factory("SomeFactory",['$http']){
return {
getData: function(){
//$http itself returns a promise
return $http.get(destinationFactory.url);
}
}
while both of them are consumed in the same way.
this.var = SomeFactory.getData()
.then(function(response) {
//some variable = response;
},function(response) {
//Do error handling here
});
There's nothing wrong with either examples(atleast syntactically)..but first one is redundant..and not needed!
Hope it helps :)
I would say that it is the classic deferred anti-pattern because you are creating needless deferred objects. However, you are adding some value to the chain (with your validation). Typically, IMO, the anti-pattern is particularly bad when deferred objects are created for very little or no benefit.
So, the code could be much simpler.
$q promises have a little documented feature of automatically wrapping anything returned inside a promise in a promise (using $q.when). In most cases this means that you shouldn't have to manually create a deferred:
var deferred = $q.defer();
However, that is how the documentation demonstrates how to use promises with $q.
So, you can change your code to this:
return {
getData: function(){
return $http.get(destinationFactory.url)
.then(function (response) {
if (typeof response.data === 'object') {
return response.data;
} else {
throw new Error('Error message here');
}
});
// no need to catch and just re-throw
});
}
Using the $q constructor is a deferred anti-pattern
ANTI-PATTERN
vm.download = function() {
var url = "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf";
return $q(function(resolve, reject) {
var req = {
method: 'POST',
url: url,
responseType: 'arraybuffer'
};
$http(req).then(function(response) {
resolve(response.data);
}, function(error) {
reject(error);
});
});
}
CORRECT
vm.download = function() {
var url = "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf";
var req = {
method: 'POST',
url: url,
responseType: 'arraybuffer'
};
return $http(req).then(function(response) {
return response.data;
});
}
The $http service already returns a promise. Using the $q constructor is unnecessary and error prone.

How do you handle errors from chained promises?

I am building an Angular app, and have a factory that executes an XHR, and returns the promise to the controller that called it. When I get an error, it gets caught in the factory fail method but because I'm returning it as a promise, it still calls the success method in the controller. Is there a way that I can get the factory error to pass on to the controller fail method and prevent the controller success method from being called? Reason being is that I'd like to properly display success and error notifications on the client side. Am I just going about this entirely wrong?
Example factory method:
function add(payload) {
return $http.post('/companies', payload)
.then(success)
.catch(fail);
function success(response) {
return response.data;
}
function fail(error) {
$log.log('Company Factory XHR failed: ', error.data);
}
}
Corresponding controller method:
function add(isValid) {
if (isValid) {
var payload = {
company: vm.new_company
};
companyFactory.add(payload)
.then(success)
.catch(fail);
}
function success() {
getCompanies();
vm.new_company = {};
toastrFactory.success("Added company!");
}
function fail(err) {
$log.log('Companies Controller XHR Failed: ' + err.data);
}
}
You don't need to handle the promise in the factory if you are handling it in the controller.
In the factory you want to just
return $http.post('/companies', payload);
Then handle it like you are already in your controller and you're good to go.
Using .then and .catch are functions on a promise, the way you are doing it now in your factory, you aren't returning a promise, you are returning other things after the promise is resolved. You just want to return the promise alone, which is the $http.post() function.
Edit:
To add a little more depth, this is how I setup my factories for this kind of stuff...
function factory() {
var self = {};
self.addCompany = function(payload) {
return $http.post('/companies', payload);
}
return self;
}
Then handle it in controllers like
factory.addCompany(payload)
.then(function(data) {
console.log('success', data);
})
.catch(function(data) {
console.log('something happened', data);
})

Queuing asynchronous promises

I'm using promises to query a rest api (using httpplease with the Promise plug-in):
api.call = function (myurl) {
return http.get({
"url" : myurl
});
}
This returns a promise that I can use with something like:
api.call (myurl)
.then (function (resp) {
// do whatever with the data
});
Now I'm trying to authenticate the api connection and for this I need to queue 2 async calls to the rest api:
1.- Ask for an authentication token
2.- Use the authentication token to make the actual call:
var getToken = function () {
var tokenUrl = "....";
return http.get({
"url": tokenUrl;
})
}
api.call = function (myurl) {
return getToken().then(function (token) {
return http.get({
"url" : myurl,
"headers" : {
"auth-token": token
}
})
})
}
The client code would remain the same:
api.call (myurl)
.then (function (resp) {
// do whatever with the data
});
Unfortunately, the code above is returning the final promise before the first one finishes (ie, token is undef in the final "then").
I'm probably missing something, but I thought that this should work after reading this:
"If you return a value, the next "then" is called with that value.
However, if you return something promise-like, the next "then" waits
on it, and is only called when that promise settles (succeeds/fails)"
Any idea how to do this?
M;
EDIT: Fixed typo in code

Asynchronus calls in angularjs

I am new to Javascript and Angularjs. I wanted to know , how to call a function asynchronously without waiting for it to return from it.
Please let me know and if there is some example then it would be very helpful.
Regards,
nG
Use Angular's deferred:
function myAsyncFunction() {
var deferred = $q.defer();
//..
setTimeout(function() {
deferred.resolve({ message: "resolved!" });
// or deferred.reject({ message: "something went terribly wrong!!" });
}, 1000);
//..
return deferred.promise;
}
myAsyncFunction()
.then(function(data){
// success
console.log("success", data.message);
}, function(data) {
// fail
console.log("error", data.message);
}).finally(function() {
// always
});
You can use a deferred to return a promise, you can then resolve the promise once your function is complete. Try something like this:
http://jsfiddle.net/adcoxwga/
var myApp = angular.module('myApp',[])
.service('myService', function($q, $timeout){
this.someFunction = function(){
var deferred = $q.defer(); //making a new deferred
$timeout(function(){
deferred.resolve('something'); //resolving the deferred with a response
}, 3000);
return deferred.promise; //returning a promise
}
})
.controller('MyCtrl', function($scope, myService){
myService.someFunction().then(function(response){ //calling the asynchronous function
console.log(response); //getting response
}, function(error){
console.log(error); //getting error
});
});
You have a couple of different options ahead of you, but one thing to note is that you have to use a callback or promise for asynchronous activity. Since you are using angular, I'd suggest using promise's.
Using a promise
If you are writing an http call to an api, you can use either $http or $resource. If you start to research angular providers, you will see that $http returns a promise, while $resource returns a promise but you must call it specifically to access it. For instance:
someService
function someServiceFunction () {
return $http.get('some/url');
}
someController
$scope.results = [];
someService.someServiceFunction().then(function(data) {
$scope.results = data;
});
Something to note is that the first returned function is the success scenario and if you declare a second callback, then it is the failure scenario.
If you were to use callbacks in the same scenario:
Using a callback
someService
function someServiceFunction (callback) {
return $http.get('some/url')
.success(callback);
}
someController
$scope.results = [];
someService.someServiceFunction(function(data, status, headers, config) {
$scope.results = data;
});
Here's a link to the $http provider.
Here's a link to the $resource provider.
Cheers!

Chaining Angular promise rejections

I have a chained promise and in the case of a rejection for either of the promises, I need to perform an async operation (get the translated error message). As I've already got a chained promise on success, I assume it's not possible to also chain on rejection - I am attempting to simply nest the async calls, but I'm not getting the resolved promise back from deferred.reject(deferredRejection.promise); below. Pointers appreciated!
login: function(email, password) {
var deferred = $q.defer();
AuthService.login(email, password).then(function(response) {
var user = {
'accountToken': response.accountToken,
'email': response.username,
'onboarded': response.onboarded,
'verified': response.verified
};
return SyncStorageService.write(SyncStorageService.storageKeys.user,
user);
}, function(error) {
// login failed
var deferredRejection = $q.defer();
$translate('ALERTS.LOGIN_FAILED').then(function(translatedValue) {
deferredRejection.resolve(translatedValue);
});
deferred.reject(deferredRejection.promise);
}).then(function(data) {
deferred.resolve(data);
}, function(error) {
// saving data failed
var deferredRejection = $q.defer();
$translate('ALERTS.UNKNOWN').then(function(translatedValue) {
deferredRejection.resolve(translatedValue);
});
deferred.reject(deferredRejection.promise);
});
return deferred.promise;
}
Updated Solution:
Based on the answer below, I was able to re-write the code as follows:
login: function(email, password) {
return AuthService.login(email, password).then(function(response) {
return {
'accountToken': response.accountToken,
'email': response.username,
'onboarded': response.onboarded,
'verified': response.verified
};
}).then(function(data) {
return SyncStorageService.write(SyncStorageService.storageKeys.user,
data);
});
}
Notes:
Both AuthService.login and SyncStorageService.write now reject promises with an Error object (e.g. new Error('ALERT.ERROR_MESSAGE');), which bubbles up through login to the controller (previously was doing the translation at the service level);
The controller that calls the login method has .then() and .catch() blocks - on a .catch(), the passed Error.message is translated and displayed.
It looks like you're not really chaining promises, and using the forgotten promise/deferred anti pattern. Making a few assumptions about how you actually want it to behave, and factoring out the calls to $translate, then something like the following I suspect is what you're after:
login: function(email, password) {
return AuthService.login(email, password).then(function(response) {
return {
'accountToken': response.accountToken,
'email': response.username,
'onboarded': response.onboarded,
'verified': response.verified
};
}, function() {
return $q.reject('ALERTS.LOGIN_FAILED');
}).then(function(user) {
return SyncStorageService.write(SyncStorageService.storageKeys.user, user).catch(function() {
return $q.reject('ALERTS.UNKNOWN');
});
}).catch(function(message) {
return $translate(message).then(function(translatedValue) {
return $q.reject(translatedValue);
});
});
}
The main things to keep in mind are:
If you definitely want to reject the derived promise, return $q.reject(error) from the success or error callback.
All the error callbacks above do this. The ones after attempted login or saving use the translation key as the error that will be eventually passed to the final catch callback. The success callback from $translate also does this to transform its resolved promise to a rejected one, so the final catch callback returns a rejected promise, so the calling code gets a rejected promise, and (presumably) shows the translated error to the user.
If you definitely want to resolve the derived promise, return anything that isn't a promise from he success or error callbacks. The derived promise will be resolved with that value. (This includes undefined if you don't have an explicit return value).
This is what is done above when returning the user return {'accountToken'.... in the first callback.
If you want to defer the resolution/rejection of a promise, return another promise from the success or error callback. Then the derived promise will be eventually resolved or rejected in whatever manner this other promise is resolved/rejected.
This is what's done above when returning SyncStorageService.write..., and when returning $translate(....

Categories

Resources