____ INTRO
Hello everyone, first of all, three clarifications:
So this guy's code has helped a lot to encapsulate my DAL. I highly recommend that you use he's code pretty much verbatim.
https://gist.github.com/jgoux/10738978
You'll see he has the following method:
self.query = function(query, bindings) {
bindings = typeof bindings !== 'undefined' ? bindings : [];
var deferred = $q.defer();
self.db.transaction(function(transaction) {
transaction.executeSql(query, bindings, function(transaction, result) {
deferred.resolve(result);
}, function(transaction, error) {
deferred.reject(error);
});
});
return deferred.promise;
};
Let's break this down a bit. The query function takes a query string (the query param) and a list of possible bindings for ? in a query like "SELECT * FROM A_TABLE WHERE ID = ?". Because he's code is a service, the self value points to the service itself for all future invocations. The function will execute a transaction against the db, but it returns a promise that is only fulfilled once the db comes back.
His service provides a second helper function: fetchAll.
self.fetchAll = function(result) {
var output = [];
for (var i = 0; i < result.rows.length; i++) {
output.push(result.rows.item(i));
}
return output;
};
fetchAll will read the rows in their entirety into an array. The result param for fetchAll is the result variable passed in the query function's promise fulfillment.
If you copy and paste his code into your service file, you now have a bonafide DB service. You can wrap that service up in a DAL. Here's an example from my project.
.service('LocationService', function ($q, DB, Util) {
'use strict';
var self = this;
self.locations = [];
self.loadLocked = false;
self.pending = [];
self.findLocations = function () {
var d = $q.defer();
if (self.locations.length > 0) {
d.resolve(self.locations);
}
else if (self.locations.length === 0 && !self.loadLocked) {
self.loadLocked = true;
DB.query("SELECT * FROM locations WHERE kind = 'active'")
.then(function (resultSet) {
var locations = DB.fetchAll(resultSet);
self.locations.
push.apply(self.locations, locations);
self.loadLocked = false;
d.resolve(self.locations);
self.pending.forEach(function (d) {
d.resolve(self.locations);
});
}, Util.handleError);
} else {
self.pending.push(d);
}
return d.promise;
};
})
This example is a bit noisy since it has some "threading" code to make sure if the same promise is fired twice it only runs against the DB once. The general poin is to show that the DB.query returns a promise. The "then" following the query method uses the DB service to fetchAll the data and add it into my local memory space. All of this is coordinated by the self.findLocations returning the variable d.promise.
Yours would behalf similarly. The controller could have your DAL service, like my LocationService, injected into it by AngularJS. If you're using the AngularJS UI, you can have it resolve the data and pass it into the list.
Finally, the only issue I have with the guy's code is that the db should come from this code.
var dbMaker = ($window.sqlitePlugin || $window);
The reason for this is that the plugin does not work within Apache Ripple. Since the plugin does a fine job mirroring the Web SQL interface of the browser, this simple little change will enable Ripple to run your Ionic Apps while still allowing you to work your SQLite in a real device.
I hope this helps.