Angular $q.defer() returns Object{then: function} - javascript

I have the following Angular file, where I try to access a database with $http, and then use this data in a $scope variable (to display in the webpage). The trouble is I can't get $q.defer to run as I believe it should. The console.log() inside the $http function logs an object containing the data returned from the database. However when I call the function it logs Object {then: function}. The data is contained within this object, but it is not the only part of the object. (It seems like it's in Object.$$v. I'm not sure what that means.)
var app = angular.module("app", []);
app.factory('portfolioFactory', function ($http, $q) {
var obj = {};
obj.getResponse = function(){
var deferred = $q.defer();
$http.get('./../includes/portfolio/db_access.php').success(function(data){
deferred.resolve(data);
console.log(data);
});
//console.log(deferred.promise);
return deferred.promise;
}
return obj;
});
app.controller("PortfolioCtrl", function($scope, portfolioFactory) {
$scope.PortfolioItems = portfolioFactory.getResponse();
console.log($scope.PortfolioItems);
});

$http.get( ... );
Returns a promise.
app.controller("PortfolioCtrl", function($scope, portfolioFactory) {
portfolioFactory.getResponse().then(function(response) {
$scope.PortfolioItems = response.data;
})
});
Will work.
In older versions of Angular $scope.PortfolioItems = portfolioFactory.getResponse();
would have worked too, but in newer versions Angular does not automatically unwrap promises anymore.
A promise is basically an alternate and nicer way of handling async action (a design pattern).
Rather than using callbacks in a regular fashion you can put your callbacks in a queue with promise.then( callback ). Then whenever the deferred.resolve method is called, the callbacks are called with the results. If the promise already is resolved before the callbacks where queued they are immediately called with the cached data. Much nicer than doing query( callback ) code that quite often turns into callback hell.
FYI you could rather do this, as the result of any $http call is a promise already:
app.factory('portfolioFactory', function ($http) {
var obj = {};
obj.getResponse = function(){
return $http.get('./../includes/portfolio/db_access.php').then(function(data){
console.log(data);
return data; //Return data further up the chain.
});
}
return obj;
});

You should be able to invoke the "then" function to get to your data.
app.controller("PortfolioCtrl", function($scope, portfolioFactory) {
$scope.PortfolioItems = portfolioFactory.getResponse();
$scope.PortfolioItems.then(function(results){
console.log(results);
};
});
It might spell it out to label things like this....
app.controller("PortfolioCtrl", function($scope, portfolioFactory) {
$scope.PortfolioItems = null;
var fetchPortfolioItems = portfolioFactory.getResponse();
fetchPortfolioItems.then(function(results){
$scope.PortfolioItems = results;
console.log($scope.PortfolioItems);
};
});

Related

How to share a variable between error and success callback of angular http request

i want to share a variable between both methods of $http.get request of angular js
my code looks like this
app.run(function($rootScope,$http) {
$rootScope.checkSession = function(){
window.a = false;
$http.get('/data/url')
.success(function(response) {
window.a = true;
})
.error(function(response){
window.a = false;
});
return window.a;
};
});
and my controller code looks like this
app.controller('profileController', function($scope, $rootScope) {
alert($rootScope.checkSession());
});
it is always output false while if i alert response in both functions it works fine.
You are confusing returns and promises. The window.a will be set async at the time your promise ($http.get) resolves, and by that time your return with the initial value has already been returned and used by alert().
The way your are doing is not the correct way of dealing with asynchronous call, as per current implementation it will always return false from that checkSession method. You need to return promise from that method which dealing with asynchronously. That promise will return data from its resolve & reject.
Additional thing is, I would love to have this code inside a factory, rather than having in inside $rootScope.
Factory
app.factory('sessionFactory', function($http) {
var sessionFactory = {}
sessionFactory.checkSession = function(){
return $http.get('/data/url')
.then(function(response) {
return true;
},(function(response){
return false;
});
};
return sessionFactory;
});
So for using it in controller, you 1st need to inject sessionFactory on your controller factory function & then use checkSession method on it. Inside .then of that method you will have returned data.
Controller
app.controller('profileController', function($scope, sessionFactory) {
sessionFactory.checkSession().then(function(data){
alert(data);
});
});

Asynchronous Angular Promise and Variable Initialization

I have a factory that looks like such:
app.factory('thingFactory', function($http) {
var factory = {};
var things = [];
factory.refreshThings = function() {
return $http.post('/GetThings');
}
factory.initializeThings = factory.refreshThings()
.then(function(response) {
things = response.data;
}, function(response){
// some error handling code here...
});
factory.getThings = function() {
return things;
}
return factory;
}
and a controller
app.controller('myController', function($scope, thingFactory) {
$scope.things = thingFactory.getThings();
}
Because of the asynchronous nature of promises, and other collections being initialized (in addition to things), should I be concerned with getThings() returning an empty array, and thus, returning before the $http.post() call has resolved?
Is this a better alternative?
app.controller('myController', function($scope, thingFactory) {
$scope.things = []
thingFactory.initializeThings
.then(function(response) {
$scope.things = response.data;
}, function (response) {
// some error handling code here...
});
}
Is there a safe alternative, where I can get the controller to not think about the promise and just safely get the collection from the factory?
You're code is definitely going to be problematic. The factory will not be instantiated until it is used by a controller, so things will not be initialized until it is called by a controller, at which time initializeThings will get called right before you call getThings, which will likely return an empty array. Also, it's never a good idea to follow a "let's hope it's there" approach.
I see two approaches you can take: getThings accepts a callback as an argument or it returns a promise, which could look something like this:
Callbacks - I prefer callbacks over promises, but that's a personal thing. Also, I use NodeJS-inspired syntax:
var things; // no need to initialize array
// callback -> function (error, things) {}
factory.getThings = function (callback) {
if (things) {
return callback(null, things);
}
factory.refreshThings()
.then( function (response) {
things = response.data;
return callback(null, things);
}, function (reason) {
return callback(reason);
});
}
Promises - not the best syntax, but you get the idea
factory.getThings = function () {
var d = $q.defer();
// I'm sure there's a better way to do this, but setting
// this in a timeout will allow the promise to return so that
// you can try to return it.
$timeout( function () {
if (things) {
return d.resolve(things);
}
factory.refreshThings()
.then( function (response) {
things = response.data;
return d.resolve(things);
}, function (reason) {
return d.reject(reason);
});
});
return d.promise;
}
As a side note, if you're using a RESTful API, GET should be used to get information rather than POST.

Angular service returning undefined is not a function

I've got a function in an Angular controller like so:
app.controller("UsersController", ["$scope", "UserList", function($scope, UserList) {
$scope.createUserSubmit = function(newUser) {
UserList.createUser().success(function(data){
console.log(data);
});
}
}]);
When the createUserSubmit() function is called, it calls another function to a function createUser() in a service UserList.
The service looks like this:
app.service("UserList", function() {
this.createUser = function(){
console.log("doing it");
return "hi";
}
});
Now, when this all runs, I get the "doing it" message logged, but then I get the following error and the console.log(data) does not log.
TypeError: undefined is not a function
Normally this type of error would be self explanatory, so either I'm being a moron or there's something confusing going on. As far as I can see, it is a function.
You do not get success method from a string return value (which you are returning from your method createUser). You get it as a part of angular $http's httpPromise. If you are actually doing an ajax call with http just return $http call.
app.service("UserList", function($http) {
this.createUser = function(){
return $http('...');
}
});
success and error methods are special function added to httpPromise inaddition to $q promise functions, it is an extension to $q promise.
And if you are looking to return data return a $q promise, which has a then method which is equivalent to success method. $q promise does not have success and error methods.
app.service("UserList", function($http) {
this.createUser = function(){
return $http('...').then(function(response){
//Do your transformation and return the data
return response.data;
});
}
});
and
UserList.createUser().then(function(data){
console.log(data);
});

Angularjs custom service method that returns a boolean value

One of my controllers has problems talking to one of my services in Angularjs :
The user enters a code;
The controller takes the entered code and passes it to my custom service to know if the code is valid or not, via a method called IsCodeValid;
That service method makes a call to a web api method that returns true if the code is valid, otherwise false;
The service method returns the result of the web api call (true if valid; otherwise false);
Here is (part of) my controller :
angular.module('clockin').controller('KeypadController', ['$scope', '$location', 'userService',
function ($scope, $location, userService) {
...
$scope.keypadEnter = function () {
var result = userService.isCodeValid($scope.code);
console.log(result);
};
...
}]);
Here is my Service :
angular.module('clockin').service('userService', ['$http',
function ($http) {
this.isCodeValid = function (code) {
$http.get("/api/clockin/iscodevalid?code=" + code)
.then(function (result) {
return result;
});
}
}
]);
My web API method works perfectly. If I call it manually and specify a valid code as a parameter into the URL, it returns true as expected.
The controller, however, always gets false as a result, even if a valid code is entered.
What am I missing ?
$http.get cal is an asynchrnous call. So you wont get the expected result like this.
$http.get call returns a promise which when resolved call a callback function that you have specified in your then method.
Solution:
In your service method return the promise object like this
angular.module('clockin')
.service('userService', ['$http', function ($http) {
this.isCodeValid = function (code) {
return $http.get("/api/clockin/iscodevalid?code=" + code);
};
}]);
In your controller wait for promise to get resolved for getting the result
$scope.populatekeypadEnter = function () {
var result = userService.isCodeValid($scope.code)
.then(function (result) {
$scope.keypadEnter = result;
});
};
You can also use .success function instead of then, to register the callback method.
You can refer this link: https://docs.angularjs.org/api/ng/service/$http

AngularJS $q.all() results are null

I'm trying to implement a $q.all to run some functions and then return all the outputs into a function attached to the .then at the end.
At the moment the promises look like they're calling in the correct order and the $all .then is occurring at the end, but the results variable comes back with an array of nulls (one for each promise in the $q.all)
JS Fiddle can be found at http://jsfiddle.net/QqKuk/120/ and I'm using angular 1.0.1
The below is a simplified example of the code I have.
Here is my html, simply just there to display some debug text and the output.
<div ng-controller="MyCtrl">
<p>{{fromThen}}</p>
<p>{{fromThen2}}</p>
<p>{{runOrder}}</p>
</div>
and here is my controller, in reality logOne, logTwo, and logThree aren't going to be identical functions.
var myApp = angular.module('myApp', []);
function MyCtrl($scope, $q, $timeout) {
var logOne = function (value) {
$scope.fromThen = $scope.fromThen + value;
var deffered = $q.defer();
deffered.promise.then( function() {
$scope.runOrder = $scope.runOrder + '.logOne()';
$scope.fromThen = $scope.fromThen + value.toUpperCase();
deffered.resolve(value);
return deffered.promise;
});
deffered.resolve();
};
var logTwo = function (value) {
$scope.fromThen = $scope.fromThen + value;
var deffered = $q.defer();
deffered.promise.then( function() {
$scope.runOrder = $scope.runOrder + '.logTwo()';
$scope.fromThen = $scope.fromThen + value.toUpperCase();
deffered.resolve(value);
return deffered.promise;
});
deffered.resolve();
};
var logThree = function (value) {
$scope.fromThen = $scope.fromThen + value;
var deffered = $q.defer();
deffered.promise.then( function() {
$scope.runOrder = $scope.runOrder + '.logThree()';
$scope.fromThen = $scope.fromThen + value.toUpperCase();
deffered.resolve(value);
return deffered.promise;
});
deffered.resolve();
};
$scope.fromThen = '';
$scope.fromThen2 = 'No Value';
$scope.runOrder = '';
$q.all([logOne('One'), logTwo('Two'), logThree('Three')])
.then(function(results) {
$scope.runOrder = $scope.runOrder + '.then';
$scope.fromThen2 = results;
});
}
The output I'm getting is
OneTwoThreeONETWOTHREE
[null,null,null]
.logOne().logTwo().logThree().then
Which to me looks like things are calling in the correct order, so I'm confused why I'm getting nulls in the return value. Am I using the defer.resolve(value) incorrectly?
I've looked at some of the other examples on here but I haven't been able to work out why I'm not getting a result.
Thanks for any help you can give. Since this is also my first post, any tips on what information i should also include (or didn't need to include) would also be appreciated.
Thanks.
Neil
Your issue is that you're not returning your promises from the log functions themselves for $q.all to follow. You're resolving the promises and returning them somewhere, but not to anywhere that's listening. Functions inside calls to .then are called by $q and the return values are sent to the resolution callbacks for the promises .then itself returns. Your promising functions should take the form:
var function = doSomthingDeferred(data) {
var deferred = $q.defer();
doSomethingDeferredWith(data).then(function(deferredResult) {
var processedResult = processDeferredResult(deferredResult);
deferred.resolve(processedResult);
});
return deferred.promise;
}
Alternatively
var function = doSomthingDeferred(data) {
return doSomethingDeferredWith(data).then(function(deferredResult) {
var processedResult = processDeferredResult(deferredResult);
return processedResult;
});
}
In your case, when you doSomethingDeferredWith(data) you:
function doSomethingDeferredWith(data) {
var deferredMore = $q.defer();
$scope.fromThen += data;
deferredMore.resolve($scope.fromThen);
This particular action doesn't really need to be deferred, it finishes immediately, but if you were querying an $http based service, then you'd get your deferredMore promise back:
return deferredMore.promise;
}
Then, after you're done doing that, you're going to get some result as a parameter to a function referenced in a call to .then on a promise like the one returned from doSomethingDeferredWith:
doSomethingDeferredWith(data).then(function(deferredResult) {
Now, because of the way $q works, the call to doSomethingDeferredWith(data) returns a promise, .then is called on that promise and the function passed in is queued up, but is not executed until the current script loop ends. This means that .then is called, the function is queued, and then doSomethingDeferred continues executing, returns, and its calling function then continues executing until the call stack has cleared. Only after that does $q have the opportunity to come back and run all of the callbacks for resolved promises.
In your code, doSomethingDeferred, the various log*** functions, do not actually return a promise. They return undefined. If you instead return the promise we created and will resolve when $q runs the callback instead of at the end of doSomethingDeferred, you'll get your data in the callback for $q.all.
To fix your code, change the deffered.resolve(); calls at the end of each of your log files to return deffered.promise; Then, the return values for the log functions won't be undefined, they'll be promises that $q can follow and run a callback on all three of their .resolve calls at once after all three calls are made, setting your $scope.runFrom2 value to an array of ['One','Two','Three'] as each individual promise resolves with the value from the closure frame of the deferring function.
tl;dr Version
Change the deffered.resolve(); calls at the end of each of your log files to return deffered.promise;

Categories