Trying to get an async DB request to work in Angul

2019-05-28 09:49发布

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.

Service

  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;

        }

Controller

$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.

2条回答
我欲成王,谁敢阻挡
2楼-- · 2019-05-28 10:24

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;
      }
查看更多
迷人小祖宗
3楼-- · 2019-05-28 10:26

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.

查看更多
登录 后发表回答