Reputation: 3538
I'm building a PhoneGap app using AngularJS + an SQLite database. I am having a classic "How does asynchronous work" Angular problem with a database query, getting error "Cannot call method then of undefined". I am hoping someone can help me to see the error of my ways.
Here's my query function. Every alert() in here returns meaningful data indicating that the transaction itself is successful:
.factory('SQLService', ['$q', '$rootScope', 'phonegapReady',
function ($q, $rootScope, phonegapReady) {
function search(query) {
alert("Search running with " + query);
var promise = db.transaction(function(transaction) {
var str = "SELECT category, id, chapter, header, snippet(guidelines, '<b>', '</b>', '...', '-1', '-24' ) AS snip FROM guidelines WHERE content MATCH '" + query + "*';";
transaction.executeSql(str,[], function(transaction, result) {
var resultObj = {},
responses = [];
if (result != null && result.rows != null) {
for (var i = 0; i < result.rows.length; i++) {
resultObj = result.rows.item(i);
alert(resultObj.category); //gives a meaningful value from the DB
responses.push(resultObj);
}
} else {
//default content
}
},defaultNullHandler,defaultErrorHandler);
alert("End of transaction");
});
// Attempting to return the promise to the controller
alert("Return promise"); //this alert happens
return promise;
}
return {
openDB : openDB,
search: search
};
}]);
And in my controller, which gives the "Cannot call method then of undefined" error:
$scope.search = function(query) {
SQLService.search(query).then(function(d) {
console.log("Search THEN"); //never runs
$scope.responses = d; //is never defined
});
}
Thanks to the accepted answer, here is the full working code.
function search(query) {
var deferred = $q.defer();
db.transaction(function(transaction) {
var str = "SELECT category, id, chapter, header, snippet(guidelines, '<b>', '</b>', '...', '-1', '-24' ) AS snip FROM guidelines WHERE content MATCH '" + query + "*';";
transaction.executeSql(str,[], function(transaction, result) {
var resultObj = {},
responses = [];
if (result != null && result.rows != null) {
for (var i = 0; i < result.rows.length; i++) {
resultObj = result.rows.item(i);
responses.push(resultObj);
}
} else {
resultObj.snip = "No results for " + query;
responses.push(resultObj)
}
deferred.resolve(responses); //at the end of processing the responses
},defaultNullHandler,defaultErrorHandler);
});
// Return the promise to the controller
return deferred.promise;
}
$scope.search = function(query) {
SQLService.search(query).then(function(d) {
$scope.responses = d;
});
}
I can then access the responses in the template using $scope.responses.
Upvotes: 2
Views: 3226
Reputation: 794
You can resolve multiple promises. Pass the array of queries as args
function methodThatChainsPromises(args,tx){
var deferred = $q.defer();
var chain = args.map(function(arg){
var innerDeferred = $q.defer();
tx.executeSql(arg,[],
function(){
console.log("Success Query");
innerDeferred.resolve(true);
},function(){
console.log("Error Query");
innerDeferred.reject();
}
);
return innerDeferred.promise;
});
$q.all(chain)
.then(
function(results) {
deferred.resolve(true)
console.log("deffered resollve"+JSON.stringify(results));
},
function(errors) {
deferred.reject(errors);
console.log("deffered rejected");
});
return deferred.promise;
}
Upvotes: 0
Reputation: 12477
The question here is: what does db.transaction
return.
From the way you're using it, I'm guessing it's some 3rd-party code that doesn't return a promise.
Assuming that you're using it correctly (your alert
shows the right results), you need to actualy use $q
to get the promise working.
Something like this:
function search(query) {
// Set up the $q deferred object.
var deferred = $q.defer();
db.transaction(function(transaction) {
transaction.executeSql(str, [], function(transaction, result) {
// do whatever you need to do to the result
var results = parseDataFrom(result);
// resolve the promise with the results
deferred.resolve(results);
}, nullHandler, errorHandler);
});
// Return the deferred's promise.
return deferred.promise;
}
Now, in your controller, the SQLService.search
method will return a promise that should get resolved with the results of your DB call.
Upvotes: 5