The scope variable $scope.quizArray appears as undefined when accessed from the controller. The block of code below is definitely read as I have previously put tests in.
app.factory('getQuizService', function($http){
return {
getQuiz:function(videoId,scope){
var $promise=$http.post("http://localhost/PHP/getQuiz.php", {'videoId': videoId});
$promise.then(function(msg){
scope.quizArray = "TEST";
});
}
}
});
Controller code - The service is called by:
function getQuizList(){
getQuizService.getQuiz(videoIdService.getId(),$scope);
alert($scope.quizArray);
}
however the alert produces the result 'undefined' instead of 'TEST'
Can anyone see where I've gone wrong? Any help would be appreciated. Thanks
Is there a reason you're doing it like this?
Take a look at the approach(es) below:
Refer to: http://docs.angularjs.org/api/ng/service/$q for more information about how the promises work.
For example, the reason why the approach that lib3d has pointed out works:
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. It also notifies via the return value of the notifyCallback method. The promise can not be resolved or rejected from the notifyCallback method.
Code:
app.factory('getQuizService', function($http){
return {
getQuiz:function(videoId,scope){
return $http.post("http://localhost/PHP/getQuiz.php", {'videoId': videoId});
//If you want to handle the promise here, you can include the $q service in your controller and do this:
var deferred = $q.defer();
$http.post("...").then(function(data) {
//Special treatment here
deferred.resolve(data)
}, function(error) {
deferred.reject(error)
})
return deferred.promise
//As indicated by lib3d, you can directly do this as well:
return $http.post("...").then(function(data) {
//treatment
return data
}, function(error) {
//error handling
});
}
}).controller("Ctrl", function($scope, getQuizService) {
getQuizService.getQuiz(1).then(function(data) {
$scope.quizArray = data;
//Code when everything goes ok,
}, function(error) {
//Code when everything goes false;
})
})
Related
I have the following structure and need to simplify it,
Resuable service1 to interact with IndexedDB
function isDbExist (){
// do something , it return either reject or resolve
}
function createDB (){
// do something , it return either reject or resolve
}
function getData (){
// do something , it return either reject or resolve
}
In another service2 I'm injecting this service1 and using the functions as such
function1 (){
service1.isDbExist.then(function(data){
service1.createDB.then(function(data){
service1.getData.then(function(data){
referred.resolve(data);
},function(error){
deferred.reject(error)
})
},function(error){
deferred.reject(error);
})
},function(error){
deferred.reject(error);
})
}
The problem here is the readability of the code is not good, its not easy to debug as which reject function is for which promise. Is their some good way of doing this ? I have read about $q.all but don't this its applicable in this situation.
Exactly, the beauty of promises is that you can chain them as opposed to nesting them like callbacks. You can return another promise when dealing with the previous like so:
isDbExists().then(function(db) {
// do something
return createDb();
}).then(function(db) {
// do something with with the resolved db..
return getData();
}).then(function(data) {
// Do something with data
}).catch(function(error) {
// Uh oh, something went wrong. Deal with error
});
Finally, you deal with the error that may have occurred.
You can chain multiple promises like this:
service1.isDbExist()
.then(service1.createDB)
.then(service1.getData)
.then(function() {
// DB exists, created and data loaded
})
.catch(function(err) {
console.log('Handle error', err); // isDbExist, createDB or getData rejected
});
Check interactive demo to see how success and error data is passed to the next promise in chain and how you have full control over process on each step:
Demo: http://plnkr.co/edit/PM06e8NGJvHKmoJ9C2Lf?p=info
One of the really great things about promises is that they can be chained like this:
function1 (){
return service1.isDbExist.then(function(exists){
if(!exists) return service1.createDB()
})
.then(function() {
return service1.getData('some', 'args')
});
}
Remember that the .then will receive the result of the previous function, like the exists would be a boolean. function1 will also return a promise and this promise will return the resolved/rejected promise from service1.getData.
I'm having trouble understanding a basic concept of error handling with chaining promises.
In order to learn the rules, I have written a simple example, guessing what the result will be. But unfortunatly it doesn't behave as I though it will.
I have read multiple articles about the subject but perhaps can't I get details because of my poor english language.
Anyway, here is my code :
var promiseStart = $q.when("start");
var promise1 = promiseStart.then(function() {
return Serviceforpromise1.get();
});
var promise2 = promise1.then(function(data1)
{
return Serviceforpromise2.get(data1);
},function(error)
{
return $q.reject();
});
var promiseend = promise2.then(function(data2)
{
return data2;
},function(error)
{
return error;
});
return promiseend;
Well I know that it can be way better coded but it's just for the purpose.
Here is the code of Serviceforpromise1 function :
function Serviceforpromise1()
{
...
return $http.get(*whatever*).then(function (data){
return data;
},function(error)
{
return $q.reject();
});
}
Consider only the case of Serviceforpromise1's failure. A $q.reject is sent back to main chain so I'm waiting the error callback of "promise1 .then(" to be called and it worked as expected. I decided for the example to transfert the error to the "promise2 .then" so in this error callback I added the line return $q.reject();
But it never reached the second error callback (the "promise2 .then" one) and I don't understand why (like Serviceforpromise1, I returned a rejected promise !)
I will be happy to deeply understand what is happening here.
Thanks for your help.
Your understanding is correct, and the problem appears to lie somewhere in the way you are trying to observe this behavior (in something you haven't shown us).
If you return a rejected promise from either a success or error handler in then(), then the promise returned by then() will resolve to a rejected promise. Observe:
angular.module('app', [])
.controller('C', [
'$q',
function ($q) {
var promiseStart = $q.when("start");
var promise1 = promiseStart.then(function (value) {
console.log('Got a value:', value);
return $q.reject('Error!');
});
var promise2 = promise1.then(function (data1) {
return "Got some stuff";
}, function (error) {
console.log("Caught an error:", error);
return $q.reject('New error');
});
var promiseend = promise2.then(function (data2) {
return data2;
}, function (error) {
console.log('Caught an error:', error); // <-- this is logged to the console
return error;
});
return promiseend;
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.10/angular.min.js"></script>
<div ng-app='app' ng-controller='C'></div>
One thing to note here is that in that last handler, you are returning the error variable, and not throwing an exception or returning a rejected promise. So in this case, promiseend will successfully resolve with the value of that error variable.
Trying to get a function to output the result of a variable once all of it asynch processes have been completed. To do this I've learned that I have to use promises and so I've spent the day learning about them.
I've written my function with promises and have looked at many tutorials but I am still getting this error, not sure what I am doing wrong. It probably has to do with what I am doing with kpiDefault or how I wrote my kpiAverage function. Also I am using coffee script so it might even be a syntax issue.
here is my code for kpiAverage
kpiAverage = (period, kpiName, params) ->
result = $q.defer()
Sads.shops.getList(params).then (data) ->
shops = data.map((d) ->
new ScopeShopWithMetrics(d, $scope.organizations.current)
)
$q.all(shops.map((d) ->
d.getAverages period
)).then( ->
shopSum = 0
i = shops.length
shopSum += shops[i]["metrics"][kpiName]["value"] while i--
shopAverage = shopSum / shops.length)
.then((shopAverage) ->
result.resolve shopAverage
result.promise
)
Now here is the code that produces the error
kpiDefault = kpiAverage(period7, "visits", testParams).then((shopAverage) ->
shopAverage
)
If i do this I don't get an error but the output isn't a number, it looks like it is a promise object.
kpiDefault = kpiAverage period7, "visits", testParams
output
Object {then: function, catch: function, finally: function}
Edit:
So it looks like I'm using promises all wrong but this leaves me even more confused. I simply want the value to be returned after the asynchronous process is done, but now I am more lost than ever.
Looked through code and found out why it was giving me that error (old code that was uncommented by accident) but I am still receiving the promise Object as an output
// Here is my function in my Factory which retrieve DATA.
result.retrieveResult = function(){
var deferred = $q.defer();
return $http.get( constant.adressApi + 'result', { cache : true, params :{ 'limit' : 50 } } )
.success(function (data) { deferred.resolve(data); })
.error(function(error){ deferred.reject(); });
return deferred.promise;
};
// Here is my call to the function from my factory service.
presentationService.retrieveResult()
.then(function(result){
$scope.result = result.data;
});
This is how i use Promise easily hope this can help you.
As you can see you create a deferred(some people call it futur). you return it at the end of the function and it will represent your result. As soon as your asynchronous function return the result if everything was fine the .success callback is call and the deferred is resolve in that case you should get the result where i wrote .then( function(result) ... Otherwise the .error is call and then you retrieve the get the reason of the error.
Short Answer
I don't see anywhere in your code that you resolve or reject your deferred so i think that's why your output display :
Object {then: function, catch: function, finally: function}
I am having trouble testing the returned value of a function that waits for a promise to be resolved before executing.
Javascript Method (serviceClient._getProduct returns a jQuery ajax promise object)
serviceClient.getProductName = function(id, storeId) {
$.when(self._getProduct(id)).done(function(data) {
return data.name;
});
};
Test Code
before(function() {
serviceClient.sampleResponse = fixture.load('product_response.json')[0];
$.ajaxStub = sinon.stub($, 'ajax').returns(new $.Deferred().resolve(serviceClient.sampleResponse));
});
describe('.getProductName', function() {
it('should return the product name', function() {
var name = serviceClient.getProductName(serviceClient.sampleResponse.id);
$.when($.ajaxStub.returnValue).done(function() {
expect(name).to.equal(serviceClient.sampleResponse.name);
});
});
});
When I step through the call stack it seems to be executing correctly (steps inside of the promise callback in the actual js file, then steps into the test promise callback to assert), however the name variable in the test is still coming back as undefined. Any feedback would be appreciated.
You're trying to return data.name synchronously in serviceClient.getProductName, which can't be done since it depends on an asynchronous ajax request.
You're doing return data.name; inside your callback, which won't get you the expected result: if you'd return something synchronously, that return sentence should be at the scope outside that closure.
To simplify: if there's anything you can return there, it's a Deferred or Promise. It should be something like this:
serviceClient.getProductName = function(id, storeId) {
var deferredName = $.Deferred();
$.when(self._getProduct(id)).done(function(data) {
deferredName.resolve(data.name);
}).fail(function(jqXHR, textStatus, error) {
deferredName.reject(error);
});
return deferredName.promise();
// Or, if needed (I don't think so, it's resolved and rejected here)
// return deferredName;
};
Then, in your test:
before(function() {
serviceClient.sampleResponse = fixture.load('product_response.json')[0];
$.ajaxStub = sinon.stub($, 'ajax').returns(new $.Deferred().resolve(serviceClient.sampleResponse));
});
describe('.getProductName', function() {
it('should return the product name', function() {
serviceClient.getProductName(serviceClient.sampleResponse.id)
.done(function(name) {
expect(name).to.equal(serviceClient.sampleResponse.name);
});
});
});
Leaving code aside, the conceptual mistake is that you can return name synchronously from getProductName, when that function gets the product asyncrhonously (it won't be able to access its name until the deferred is resolved).
Note: you could implement getProductName using then, that returns a Promise (which is a subset of Deferred, but you can usually get away with it and code looks even clearer):
serviceClient.getProductName = function(id, storeId) {
return $.when(self._getProduct(id)).then(function(data) {
return data.name;
});
};
To remove that, also unnecessary $.when(...), you could return a Promise from _getProduct as well (if you've got a deferred, getting a Promise for it is as simple as calling deferred.promise()).
How do I delay execution of a function until after all of my $resources have resolved? My goal here is to be able to parse though the log array after all $resources have resolved and push a single success notification to the UI instead of pushing one notification per each success.
I've based my code below off of this question angular -- accessing data of multiple http calls - how to resolve the promises. I realize that $scope.promises is empty because item.$save() doesn't return anything but I hope you can see that I'm trying to push the unresolved promise to the promises array.
$scope.save = function () {
$scope.promises = [];
$scope.log = [];
angular.forEach($scope.menuItems, function(item) {
$scope.promises.push(item.$save(function(menu){
debugger; // execution gets here 2nd
console.debug("success");
$scope.log.push(msg: 'success');
}));
}, this);
$q.all($scope.promises).then(function() {
debugger; // execution gets here 1st
console.debug("all promises resolved");
});
};
Since $save does not return a promise, you will need an intermediate one:
angular.forEach($scope.menuItems, function(item) {
var d = $q.defer(); // <--- the intermediate promise
item.$save(
function(menu){
debugger;
console.debug("success");
$scope.log.push(msg: 'success');
d.resolve(menu); // <--- resolving it, optionally with the value
},
function(error){
d.reject(error); // <--- rejecting it on error
}
);
$scope.promises.push(d.promise);
}, this);
By the way, do not forget to throw away the array of promises, or you will keep garbage:
$q.all($scope.promises).then(...).always(function() {
$scope.promises = null;
});
And, if $scope.promises is NOT exposed to the view, it does not need to be in the scope; it can be just a var.