Angular "Cannot read property 'then' of undefined" with promise - javascript

I am stuck on why my promises aren't working. From the other articles I have looked at, I think I am doing it correctly. Here is the code I have currently:
Factory code
factory.isLoggedIn = function() {
$http.get('/api/v1/fitbit/auth')
.success((data) => {
return data.status;
})
.error((error) => {
console.log('Error: ' + error);
});
}
Controller code
$scope.fitbitAuth = function() {
FitbitFactory.isLoggedIn()
.then(
function(data) {
$scope.fitbitStatus = data;
},
function(errorData) {
console.log(errorData);
});
return $scope.fitbitStatus;
};
From my understanding of promises, the return $scope.fitbitStatus should be populating the $scope.fitbitAuth, but it isn't. I am also returning a boolean in the Factory, which should be populating $scope.fitbitStatus.

You have to return something (the promise), or it is undefined.
Factory code:
factory.isLoggedIn = function() {
return $http.get('/api/v1/fitbit/auth');
}
Controller code:
$scope.fitbitAuth = function() {
return FitbitFactory.isLoggedIn()
.then(
function(data) {
$scope.fitbitStatus = data;
},
function(errorData) {
console.log(errorData);
});
};
The complete success/error block in your factory is not necessary and should be removed. I'm also unsure why you return $scope.fitbitStatus; as it is undefined at the time of return.
Edit: Edited the answer to actually return the promise.

Currently you haven't return anything from isLoggedIn factory method and you are calling .then method over it.
To make it working return $http promiseobject from service method. In your case you could simply return$http.getmethod call which return promise object itself and then you can easily chain them up by callingFitbitFactory.isLoggedIn().then`
factory.isLoggedIn = function() {
return $http.get('/api/v1/fitbit/auth');
}

Related

handle a function that returns a promise or a null value

I have defined a function as following :
function getCurrentComponent(){
if($rootRouter._currentInstruction){
return $rootRouter.recognize($rootRouter._currentInstruction.urlPath).then(function (data) {
return data.component.componentType;
});
}else{
return null;
}
}
To call this function I did as following :
factory.getCurrentComponent().then(function (data) {
...
});
The problem is when the getCurrentComponent function returns a null value, the following error is generated :
Cannot read property 'then' of null
How can I solve this ?
Edit:
I forgot to say that I'm limited to use ES5, so I can't work with the object Promise
Use Promise.reject() function.
function getCurrentComponent() {
if ($rootRouter._currentInstruction) {
return $rootRouter.recognize($rootRouter._currentInstruction.urlPath).then(function(data) {
return data.component.componentType;
});
} else {
return Promise.reject('_currentInstruction is fale');
}
}
factory.getCurrentComponent().then(function(data) {
...
}).catch(function(e) {
console.log(e); // Output: _currentInstruction is fale
});
Resource
Promise.reject()
If you're unable to use Promise you can return an object with a function then.
function getCurrentComponent() {
if ($rootRouter._currentInstruction) {
return $rootRouter.recognize($rootRouter._currentInstruction.urlPath).then(function(data) {
return data.component.componentType;
});
} else {
var helperThen = { then: function(fn) { fn(null) } };
return helperThen;
}
}
factory.getCurrentComponent().then(function(data) {
// Check if data is null.
...
});
I cant use the object Promise in ES5.
Use the AngularJS $q Service:
function getCurrentComponent(){
if($rootRouter._currentInstruction){
return $rootRouter.recognize($rootRouter._currentInstruction.urlPath).then(function (data) {
return data.component.componentType;
});
}else{
̶r̶e̶t̶u̶r̶n̶ ̶n̶u̶l̶l̶;̶
return $q.reject(null);
}
}
AngularJS modifies the normal JavaScript flow by providing its own event processing loop. This splits the JavaScript into classical and AngularJS execution context. Only operations which are applied in the AngularJS execution context will benefit from AngularJS data-binding, exception handling, property watching, etc.
The $q Service is a Promises/A+-compliant implementation of promises/deferred objects thst is integrated with the AngularJS framework and its digest cycle.
Convert the function to use a Promise.
function getCurrentComponent(){
return new Promise((resolve, reject)=>{
if($rootRouter._currentInstruction){
resolve( $rootRouter.recognize($rootRouter._currentInstruction.urlPath).then(function (data) {
return data.component.componentType;
}));
} else{
reject(null);
})
}
Now you can check resolve and reject with then() function,
factory.getCurrentComponent().then(function (resolved) {
//handle success here
}, function(rejected) {
// handle rejection here
});
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

Function being removed from object during return

I'm trying to add an abort function to my promise in Angular. When debugging the code, I can see the function gets added as expected. However, when the object is returned to the calling service, the function is no longer there. I'm hoping it's something trivial.
.factory('MatchmakerSearch', ['$resource', 'OBB_ENV_CONF', '$q', function ($resource,
OBB_ENV_CONF, $q) {
// Create the $resource object to handle the API requests
function _query(params) {
var _deferredAbort = $q.defer();
var _request = $resource(OBB_ENV_CONF.API_HOST + 'int/matchMaker', {}, {
'query': {
method: 'GET',
params: params,
isArray: false,
timeout: _deferredAbort.promise
}
});
var _promise = _request.query().$promise.then( // Convert from $resource to $http
function (response) {
return response;
}, function (response) {
return $q.reject('Ajax call aborted');
}
);
_promise.abort = function () {
_deferredAbort.resolve();
};
_promise.finally(function () {
_promise.abort = angular.noop;
_deferredAbort = _request = _promise = null;
});
return _promise; // <~~~~ abort function exists here
}
return {
query: _query
}
}
]);
The service making the call looks like this:
_searchRequest = MatchmakerSearch.query(buildQueryParams()).then(function (result) {
// <~~~~ _searchRequest does not contain an abort() function.
});
I really thought this would be a simple thing to code. Any ideas on why my function is disappearing on return?
Every time you chain a promise with then, catch, or finally you receive a new Promise back:
_searchRequest = MatchmakerSearch
.query(buildQueryParams()) // Your customised promise.
.then(function (result) {}) // a new promise returned here.
So _searchRequest ends up being a fresh new Promise instance.
The documentation for deferred.then() talks about this:
then(successCallback, errorCallback, notifyCallback) – ...
This method returns a new promise which is resolved or rejected via the return value of the successCallback, errorCallback ...
(Emphasis in the original)

How can I return a success and an error promise with and without data?

I would like to return a promise and an object called output either before or after the $http call. Can someone advise me how I can do this with the AngularJS framework and very important with Typescript so I can be sure it is working correctly?
topicNewSubmit = (): ng.IPromise<any> => {
var self = this;
var myData1 = { abc: 123 }
if (self.abc = 22) {
// How can I return an OKAY promise from here?
}
if (self.abc = 33) {
// How can I return an OKAY promise with myData1 from here?
}
if (self.abc = 88) {
// How can I return a FAIL promise from here?
}
return self.$http({ url: self.url, method: "GET" })
.then(
(response: ng.IHttpPromiseCallbackArg<any>): any => {
var myData2 = { abc: 245 }
// How can I return a promise and myData2.
// return(myData2) gives an error with Typescript
// How can I return a promise and no data
// return gives an error with Typescript
},
(error: ng.IHttpPromiseCallbackArg<any>): ng.IPromise<any> => {
var myData3 = { abc: 345 }
// Is this the correct way to return an unhandled reject with myData3
return self.$q.reject(myData);
});
}
Edit: Fixed the code and added a TypeScript Playground example. The methods are correctly typed, you can verify this by the typing errors that are thrown, try and fix them ;). I copied over the very basic required interfaces from the angular definition file.
Edit #2: Here's the fixed version of the TypeScript Playground example above.
If I understand your question correctly, you're trying to define a return type for the service method stating it returns a promise whose result upon resolving will be a certain object?
In that case you're almost there, I've split up your two example methods in separate blocks, as they require a different approach.
On a general note, I removed the scope copying (self = this) as you are using the fat arrow methods, which will automatically scope the method to the outer lexical scope. In short, there's no need to do the scope copy and in fact, in your example self doesn't always point to the service (as you're copying the scope inside of the method instead of outside of it).
Also, please note the definition of an Angular promise (truncated):
interface IDeferred<T> {
resolve(value?: T): void;
reject(reason?: any): void;
}
As such, typing an Angular Promise will only add a typing for the resolve case of the promise, not for the rejected case. Consequently, when calling your service, it will verify that the result in the success handler is of the type you have defined, but the type of the parameters in the error handler is of type any.
topicTest
For this method to work you need to inject $q into your service and then use it to create your own deferred
topicTest = (): IPromise<Foo> => { // return a promise which will result in a parameter of MyType upon resolving
var deferred = this.$q.defer<Foo>(); // Type the deferred to get better 'intellisense' support
if (this.abc = 99) {
deferred.resolve(new Foo());
}
if (this.abc = 88) {
deferred.reject("You can pass in whatever you like to the reject case");
}
return deferred.promise;
};
topicNewSubmit
The $http already returns promises, so you only need to hook into these by attaching a then callback and returning from that method to allow chaining other then callabacks to it.
In that case the return type of your service method would be angular.IPromise<() => any> where you can replace any with a type you'd like. The return type of the then method would have to correspond to whatever type you chose for the generic placeholder in the return type of the service method.
topicNewSubmit = () : IPromise<Foo> => {
return this.$http({ url: this.url, method: "GET" }).then((response): Foo => {
return new Foo();
}, (error) => {
return "whatever you'd like, it does not have to correspond to Foo";
});
}
You could then use your service like
MyService.topicNewSubmit().then((data) => {
// data needs to be of type T, where T is the type you defined in the generic placeholder of IPromise<T>
}, (error: any) => {
// In the error case, the parameters are of type any
});
To be honest, I feel like I'm shooting in the dark with your code sample, but here's my solution.
Like Anzeo, I removed the references to self or this. $q and $http should be assigned somewhere.
declare var $q: ng.IQService;
declare var $http: ng.IHttpService;
var topicNewSubmit = (): ng.IPromise<any> => {
var deferred = $q.defer<any>();
var myData1 = { abc: 123 };
if (this.abc = 22) {
deferred.resolve();
} else if (this.abc = 33) {
deferred.resolve(myData1);
} else if (this.abc = 88) {
deferred.reject();
} else {
$http({
url: this.url,
method: "GET"
})
.then(() => {
deferred.resolve({ abc: 245 });
}, () => {
deferred.reject({ abc: 345 });
});
}
return deferred.promise;
};
Answer
You cannot return something that isn't there yet unless you block all operations till you get it. Since the browser JavaScript is (mostly) single threaded you don't want to block the thread while the file downloads. Hence you return a promise that will eventually resolve. The consumer needs to use the then function to eventually get the value.
Humor
A promise is for life. Its 🐢s all the way down 🌹
You can use promise like
var deferred = $q.deferred();
$http(api url and method)
.success function() {
deferred.resolve();
}
.failure function() {
deferred.reject();
}
return deferred.promise;

angular controller is executing before factory complete

I have moved some common code to factory. but the controller is executing before factory get loaded. In this case i am getting the blank response(zero results)
can anyone suggest the best solution.
here is my angular factory,
app.factory('TabsFactory', function($resource){
var activetabs = {};
activetabs.getDepositAccountDetails = function() {
return $resource('xxxx/:number', {}, {
getDepositAccountDetailsService: {
method: 'GET',
isArray: false
}
});
}
activetabs.getAccountInfo = function(){
return accountinit.accountInfo;
}
activetabs.setAccountInfo = function(accountnumber, result) {
var accountinit = {
accountInfo: []
}
if (result.code == "v") {
activetabs.getDepositAccountDetails().getDepositAccountDetailsService({
number: accountnumber
}).$promise.then(function(response) {
accountinit.accountInfo = response;
//here i am getting the JSON response
}, function(error) {
});
}
return accountinit;
}
return activetabs;
});
controller,
TabsFactory.setAccountInfo(accountnumber, $scope.accountInfo);
$scope.accountInfo = TabsFactory.getAccountInfo();
alert(JSON.stringify($scope.accountInfo));
You should use chain promise to update scope variable, because your accountInfo variable is updated inside $resource promise.
Code
TabsFactory.setAccountInfo(accountnumber, $scope.accountInfo).then(function(data){
$scope.accountInfo = TabsFactory.getAccountInfo();
alert(JSON.stringify($scope.accountInfo));
});
Update
Service method should return promise inorder to continue promise chain
activetabs.setAccountInfo = function(accountnumber, result) {
var accountinit = {
accountInfo: []
}
if (result.code == "v") {
//added return below
return activetabs.getDepositAccountDetails().getDepositAccountDetailsService({
number: accountnumber
}).$promise.then(function(response) {
accountinit.accountInfo = response;
return accountinit.accountInfo;
//here i am getting the JSON response
}, function(error) {
});
}
return accountinit;
}
Yes, this will happen because of JavaScript executing asynchronous operations but your controller in such a way that it expects things to be synchronous operations.
When you call TabsFactory.getAccountInfo() its possible that your $resource('xxxx/:number') is still not completed and response ready for you to process!!
So, what to do? You have make use of promise. I usually have a repository (A factory with method that return promise) to handle server communications. Here is an example:
app.factory('accountRepository', ["$http","$q",function($http,$q){
return {
getDepositAccountDetails : function(id) {
var deferred = $q.defer();
$http.ger('xxx').success(deferred.resolve).error(deferred.reject);
return deferred.promise;
}
};
}] );
My repository will have more operations like add account, update account info etc..
my controller/service then calls these methods as follows:
accountRepository.getDepositAccountDetails(123).then(function(response) {
// Process the response..
}, function(error) {
// Some error occured! handle it
});
doing so, my code gets executed only after I get response from server and data is ready for consumption or display. Hope this helps..
Update: You might want to have a look at this to get the idea ;)

Correct use of Promises in Angular

I am trying to get my head wrapped around the concept of Promises in AngularJS.
Is the following code on the right track? And could someone explain the PlateCheckService.checkPlate method and why if I don't return the promise? return $http.post().then why the inner return of the object with the message and alertClass does not work? I think it is a chained/inner promise?
/// <reference path="angular.js" />
(function () {
"use strict"
var app = angular.module('cs');
app.service('PlateCheckService', ['$http', function ($http) {
return {
checkPlate: function (plateNumber) {
return $http.post('PlateCheck/Index', {
plateNumber: plateNumber
}).then(function (response) {
return {
message: response.data.VehicleAtl === null ? 'Clean' : 'Hot',
alertClass: response.data.VehicleAtl === null ? 'alert-success' : 'alert-danger'
}
});
}
}
}]);
app.controller('PlateCheckCtrl', ['$scope', 'PlateCheckService', function ($scope, PlateCheckService) {
var plateCheck = {
plateNumber: '',
message: '',
alertClass: '',
checkPlate: function (plateNumber) {
var _this = this;
PlateCheckService.checkPlate(plateNumber).then(function (response) {
_this.message = response.message;
_this.alertClass = response.alertClass;
});
}
};
$scope.plateCheck = plateCheck;
}]);
}());
Yes, it is because of chained promises. Remember that a call to then will return a promise before the function it encloses is called.
So, your call here: PlateCheckService.checkPlate(plateNumber) will expect to return a chained promise. However, your service's then enclosed function is not returning a promise. Therefore, it will fail to have a then function to chain once it is resolved.
You can visualize this with the following psuedo code:
$http.get('url')
.then(function(response) {
return aPromise;
})
.then(function(aPromiseResponse) {
return bPromise;
})
.then(function(bPromiseResponse) {
return cPromise;
})
.then(function(cPromiseResponse) {
// when scope is available
$scope.bindToMe = cPromiseResponse.value;
});
If you want to add functionality to a promise chain in a service, then the wrapped promise function needs to return a promise as well.
The easiest way I have found to do this is with $q.when. $q.when will wrap the object in a promise if the object is not a promise. If an object is already available (like in your case), then $q.when will instantly resolve. Documentation for $q.when is here.
So, you should be able to get your code to work by using this in the service:
return $q.when({
message: response.data.VehicleAtl === null ? 'Clean' : 'Hot',
alertClass: response.data.VehicleAtl === null ? 'alert-success' : 'alert-danger'
});

Categories

Resources