Cordova SQLite wait until insert finishes - angularjs

I have multiple INSERTs I'd like to be done before starting SELECT requests. My problem is that the INSERT is not yet finished when the SELECT fires.
Made a factory for the database handling:
databaseFactory.js
factory.insertIntoTable= function (database, data) {
var sqlCommand = 'INSERT INTO blablabla';
database.transaction(function(tx) {
database.executeSql(sqlCommand, [data.thingsToInsert], function (resultSet) {
console.log('success: insertIntoTable');
}, function (error) {
console.log('SELECT error: ' + error.message);
});
}, function(error) {
console.log('transaction error: ' + error.message);
database.close();
}, function() {
console.log('transaction ok: insertIntoTable');
});
};
app.js
ionic.Platform.ready(function () {
if (window.cordova && window.cordova.plugins.Keyboard) {
cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
cordova.plugins.Keyboard.disableScroll(true);
};
if(window.StatusBar) {
StatusBar.styleDefault();
}
db = window.sqlitePlugin.openDatabase({name: 'myDbName.db', location: 'default'});
if (window.Connection) {
if (navigator.connection.type !== Connection.NONE) {
databaseFactory.createTables(db);
MyService.getStuffFromAPI().then(function(result) {
for (var index = 0; index < result[0].campaigns.length; index++) {
databaseFactory.insertIntoTable(db, result[0].campaigns[index]);
}
var selectResult = databaseFactory.selectFromCampaigns();
console.log(selectResult); //This log comes earlier than the above inserts could finish.
}, function(result) {
console.log(result);
});
}
}
});
The INSERT is working fine anyways, I did check it.
I know that the datebase.transaction is asynchronous so also tried it with single db.executeSQL command but had the same problem even with adding $q resolve, reject to the factory. I really could use some help, thanks!

Every Insert returns a promise. Keep those promises in an array of promises and use $q.all to wait for all of them to complete.
Example: Factory method used to insert an object
function insert(object){
var deferred = $q.defer(); //IMPORTANT
var query = "INSERT INTO objectTable (attr1, attr2) VALUES (?,?)";
$cordovaSQLite.execute(db, query, [object.attr1, object.attr2]).then(function(res) { //db object is the result of the openDB method
console.log("INSERT ID -> " + res.insertId);
deferred.resolve(res); //"return" res in the success method
}, function (err) {
console.error(JSON.stringify(err));
deferred.reject(err); //"return" the error in the error method
});
return deferred.promise; //the function returns the promise to wait for
}
And then, in every insert:
promises.push(yourService.insert(obj).then(function(result){ //"result" --> deferred.resolve(res);
//success code
}, function(error){ //"error" --> deferred.reject(err);
//error code
}));
And finally:
$q.all(promises).then(function(){//do your selects}, function(err){//error!});
Hope it helps.
More info about $q and $q.all: https://docs.angularjs.org/api/ng/service/$q#all
And another example: https://www.jonathanfielding.com/combining-promises-angular/

The problem was the way I used the database.transaction(function (tx){}) since it's itself an asynchronous function and inside the function body I can make the synchronous CRUD operations and they will happen in order.
app.js (fixed)
ionic.Platform.ready(function () {
if (window.cordova && window.cordova.plugins.Keyboard) {
cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
cordova.plugins.Keyboard.disableScroll(true);
};
if(window.StatusBar) {
StatusBar.styleDefault();
}
db = window.sqlitePlugin.openDatabase({name: 'MagicalWonder.db', location: 'default'});
if (window.Connection) {
if (navigator.connection.type !== Connection.NONE) {
MyService.getMyPreciousData().then(function(result) {
db.transaction(function(tx) {
databaseFactory.createTable(tx);
for (var index = 0; index < result[0].campaigns.length; index++) {
databaseFactory.insertIntoTable(tx, result[0].myPreciousData[index]);
}
// HERE I CAN MAKE THE SELECT REQUESTS
}, function(error) {
console.log('transaction error: ' + error.message);
database.close();
}, function() {
console.log('transactions successfully done.');
});
}, function(result) {
console.log(result);
});
}
}
});
factory method (fixed)
factory.insertIntoTable = function (tx, data) {
var sqlCommand = 'INSERT INTO wanders (' +
'id, ' +
'magic_spell) values (?,?)';
tx.executeSql(sqlCommand, [data.id, data.magic_spell], function (tx, resultSet) {
console.log('Success: insertIntoBookOfWonder');
}, function (tx, error) {
console.log('SELECT error: ' + error.message);
});
};

Related

Creating new function in angularjs controller won't work

When creating new function in angularjs controller and assign it to button with ng-click and function is simple,
function toLogin() {
console.log("Entered function");
$state.go('login');
}
I tried with $scope and still does not work.
this is my controller:
'use strict';
angular.module('crudApp').controller('UserController',
['UserService', '$scope','$state', function( UserService, $scope, $state) {
var self = this;
self.user = {};
self.users=[];
self.user.enabled = false;
self.user.confirmationToken = '';
self.loggedUser = null;
self.submit = submit;
self.getAllUsers = getAllUsers;
self.createUser = createUser;
self.updateUser = updateUser;
self.removeUser = removeUser;
self.editUser = editUser;
self.reset = reset;
self.firstNamePattern=/^[A-Z][a-z]*\S$/;
self.lastNamePattern=/^[A-Z][a-z]*\S$/;
self.userNamePattern= /^\S*$/;
self.passwordPattern = /^\S*$/;
self.phonePattern = /^[0-9]+\S$/;
self.successMessage = '';
self.errorMessage = '';
self.done = false;
self.onlyIntegers = /^\d+$/;
self.onlyNumbers = /^\d+([,.]\d+)?$/;
function toLogin() {
console.log('Entered function');
// $state.go('login');
}
function submit() {
console.log('Submitting');
if (self.user.id === undefined || self.user.id === null) {
console.log('Saving New User', self.user);
createUser(self.user);
} else {
updateUser(self.user, self.user.id);
console.log('User updated with id ', self.user.id);
}
}
$scope.moje = function () {
console.log('Submitting');
if (self.user.id === undefined || self.user.id === null) {
console.log('Saving New User', self.user);
createUser(self.user);
} else {
updateUser(self.user, self.user.id);
console.log('User updated with id ', self.user.id);
}
}
function createUser(user) {
console.log('About to create user');
UserService.createUser(user)
.then(
function (response) {
console.log('User created successfully');
self.successMessage = 'User created successfully';
self.errorMessage='';
self.done = true;
self.user={};
$scope.registerForm.$setPristine();
$scope.registerForm.$setUntouched();
// $state.reload();
},
function (errResponse) {
console.error('Error while creating User');
self.errorMessage = 'Error while creating User: ' + errResponse.data.errorMessage;
self.successMessage='';
}
);
}
function updateUser(user, id){
console.log('About to update user');
UserService.updateUser(user, id)
.then(
function (response){
console.log('User updated successfully');
self.successMessage='User updated successfully';
self.errorMessage='';
self.done = true;
$scope.myForm.$setPristine();
},
function(errResponse){
console.error('Error while updating User');
self.errorMessage='Error while updating User '+errResponse.data;
self.successMessage='';
}
);
}
function removeUser(id){
console.log('About to remove User with id '+id);
UserService.removeUser(id)
.then(
function(){
console.log('User '+id + ' removed successfully');
},
function(errResponse){
console.error('Error while removing user '+id +', Error :'+errResponse.data);
}
);
}
function getAllUsers(){
return UserService.getAllUsers();
}
function editUser(id) {
self.successMessage='';
self.errorMessage='';
UserService.getUser(id).then(
function (user) {
self.user = user;
},
function (errResponse) {
console.error('Error while removing user ' + id + ', Error :' + errResponse.data);
}
);
}
function reset(){
self.successMessage='';
self.errorMessage='';
self.user={};
$scope.myForm.$setPristine(); //reset Form
}
}
]);
And and I using ui-routing and this is where I assign controller to my view:
}).state('success',{
url: '/success',
templateUrl: 'partials/successMessage',
controller: 'UserController',
controllerAs: 'sCtrl'
});
And the ftl part of code is here:
<button type="button" ng-click='sCtrl.toLogin()' class="btn btn-primary">Login</button>
It won't even print to console, but when assigning older function it works perfectly, although it is much more complicated. Ps. Sorry for bad clarification at first, I am new to community and still learning a proper way to ask question.
You're not registering the method to the controller, to do that.
Add this below in self.onlyNumbers
self.toLogin = toLogin;
You need to use $scope
$scope.toLogin = function(){
console.log("Entered function");
$state.go('login');
}

AngularJS How to execute function after some function finish?

I have some page which contain register with facebook button which I set hidden with ng-hide="fbLoggedIn" and form input which I set hidden with ng-show="fbLoggedIn"
My goal is register with facebook button will hide if fbLoggedIn set to true and form input will show if fbLoggedIn set to true.
register facebook button ng-click="registerFb()" execute this function
$scope.registerFB = function () {
authService.fbLogin();
$scope.fbLoggedIn = authService.fb_logged_in();
console.log($scope.fbLoggedIn); //this show false even `fb_access_token` not null
}
Here is my authService.fbLogin and authService.fb_logged_in function
authService.fbLogin = function () {
var FB = window.FB;
FB.login(function(response) {
console.log(response);
if (response.authResponse) {
sessionService.set('fb_id', response.authResponse.userID);
sessionService.set('fb_access_token', response.authResponse.accessToken);
sessionService.set('fb_expiration_date', new Date(new Date().getTime() + response.authResponse.expiresIn * 1000).toISOString());
//console.log('Welcome! Fetching your information.... ');
FB.api('/me', function(response) {
console.log('Good to see you, ' + response.name + '.');
console.log(response);
});
} else {
console.log('User cancelled login or did not fully authorize.');
//console.log(response);
}
});
};
authService.fb_logged_in = function () {
if(sessionService.get('fb_access_token') != null){
return true;
}else {
return false;
}
};
In other function I try to check if fb_access_token is not null, just to make sure something wrong with my logic, and the result is true.
With above debuggin I can say that $scope.fbLoggedIn = authService.fb_logged_in(); execute before authService.fbLogin(); finish.
So how I can execute $scope.fbLoggedIn = authService.fb_logged_in(); after authService.fbLogin(); finish? maybe how to achieve my goal?
Alright. This can be achieved using promise. I don't know the parameters you have included in your autService service, so I will be making a factory of the same name with the new parameters that you might need to add.
Hence, according to me, this is how your factory should be.
angular.module('YourModuleName').factory('authService',['$http','$q',function($http,$q){
var obj = {};
obj.fbLogin = function () {
var defer = $q.defer();
var FB = window.FB;
FB.login(function(response) {
console.log(response);
if (response.authResponse) {
sessionService.set('fb_id', response.authResponse.userID);
sessionService.set('fb_access_token', response.authResponse.accessToken);
sessionService.set('fb_expiration_date', new Date(new Date().getTime() + response.authResponse.expiresIn * 1000).toISOString());
FB.api('/me', function(response) {
console.log(response);
defer.resolve('Good to see you, ' + response.name + '.');
});
}
else {
defer.reject('User cancelled login or did not fully authorize.');
}
});
return defer.promise;
}
obj.fb_logged_in = function () {
if(sessionService.get('fb_access_token') != null){
return true;
}else {
return false;
}
};
return obj;
}])
And thus, the function call from the controller should be as follows.
$scope.registerFB = function () {
authService.fbLogin().then(function(response){
$scope.fbLoggedIn = authService.fb_logged_in();
console.log($scope.fbLoggedIn);
},function(error){
console.error("Error : ",error);
});
}
Note: CODE NOT TESTED.
Hence it would solve the problem with the best practices of angularJS
use the $rootscope to assign values they provide event emission/broadcast and subscription facility.

Ionic/Angular concurrent SQLite connections and writes failing

I'm fairly new to both Ionic and Angular. I'm trying to write to a SQLite database and it's intermittently successful. When I do a for loop and insert records rapidly, some succeed and some fail (without apparent error). The query execution uses promises, so multiple queries may be trying to execute concurrently. It seems that this causes a synchronization issue in SQLite - or the SQLite plugin. I've tried opening a new DB connection with every execute(), reopening the existing connection on every execute(), and I've also tried opening a connection globally in app.js once and reusing that connection. They all seem to behave the same.
A custom 'dbQuery' function is used to build queries and is chainable. The idea is that any place in my app with access to the DB service can execute a query and expect the results to flow into an "out" variable like:
var my_query_results = [];
DB.begin().select("*","table1").execute(my_query_results).then(function() {
console.log("Query complete");
});
That much works, but the problem comes from writes:
var records = [
{id:1, name:"Bob"},
{id:2, name:"John"},
{id:3, name:"Jim"},
];
for (var i = 0; i < records.length; i++) {
var obj = records[i];
var result = [];
DB.begin().debug(true).insert("table1", "(id,name)", "("+obj.id+","+ obj.name+")").execute(result).then(function () {
console.log("Inserted record", JSON.stringify(obj));
});
}
Sometimes it fails without any logged or apparent error, sometimes it succeeds. If I perform the inserts slowly over time, it seems to work without issue.
app.js
var db;
angular.module('starter', ['ionic', 'starter.controllers', 'starter.services'])
.run(function ($ionicPlatform, $cordovaSQLite, appConfig, $q) {
$ionicPlatform.ready(function () {
db = $cordovaSQLite.openDB({
name: appConfig.sqlite_db,
location: appConfig.sqlite_db_location
});
dbQuery = function () {
this.bDebug = false;
this.query = "";
this.result = [];
this.params = [];
this.debug = function (value) {
this.bDebug = (value === true ? true : false);
return this;
};
this.rawQuery = function (query) {
this.query = query;
return this;
};
this.insert = function (table, fields, values) {
this.query = "INSERT INTO '" + table + "' (" + fields + ") VALUES (" + values + ")";
return this;
};
this.select = function (fields, table) {
this.query = "SELECT " + fields + " FROM " + table;
return this;
};
this.delete = function (query) {
this.query = "DELETE FROM " + query;
return this;
};
this.where = function (column, expression, value) {
expression = expression || "=";
this.query += " WHERE `" + column + "` " + expression + " ? ";
this.params[this.params.length] = value;
return this;
};
this.and = function (column, expression, value) {
expression = expression || "=";
this.query += " AND '" + column + "' " + expression + " ? ";
this.params[this.params.length] = value;
return this;
};
this.execute = function (out_var) {
var self = this;
this.result = out_var;
if (this.bDebug) {
console.log("Compiled query is", this.query);
}
var deferred = $q.defer();
db.open(function () {
console.log("Opened");
}, function () {
console.log("Failed");
});
//actually execute the query
$cordovaSQLite.execute(db, this.query, this.params).then(
function (res) {
for (var i = 0; i < res.rows.length; i++) {
self.result.push(res.rows.item(i));
console.log("Added row to set", JSON.stringify(res.rows.item(i)));
}
if (res.rows.length == 0 && self.bDebug === true) {
console.log("No results found ");
}
deferred.resolve();
}, function (err) {
console.error(JSON.stringify(err), this.query);
deferred.reject();
});
return deferred.promise;
}
services.js
.factory('DB', function ($ionicPlatform) {
return {
begin: function () {
return new dbQuery();
}
}
})
.factory('DbBootstrap', function ($cordovaSQLite, appConfig, $q, $state, DB) {
return {
wipe: function () {
DB.begin().rawQuery("DELETE FROM table1").execute().then(function () {
console.log("Purged records");
});
},
init: function () {
var result = []; //out variable
DB.begin().rawQuery("CREATE TABLE IF NOT EXISTS table1 (id integer primary key, name text)").execute(result).then(function () {
console.log("Schema create returned", JSON.stringify(result));
});
var records = [
{
id: 1, name:'Jim'
...
},
{
id: 2, name:'Bob'
...
},
{
id: 3, name:'John'
...
}
];
for (var i = 0; i < records.length; i++) {
var obj = records[i];
var result = [];
DB.begin().debug(true).insert("table1", "(id,name)", "(obj.id, obj.name).execute(result).then(function () {
console.log("Inserted record", JSON.stringify(obj));
});
}
}
})
I'm sure I'm missing something fundamental about angular, promises, and sqlite locking. If anyone has advice I'd really appreciate it.
I resolved this following the excellent advice here - Angular/Ionic and async SQLite - ensuring data factory initialised before return
The key issue being that I needed to wrap all my DB operations in promises and use them for orderly initialization and callbacks.
.factory('DB', function ($q, $cordovaSQLite, appConfig) {
//private variables
var db_;
// private methods - all return promises
var openDB_ = function (dbName, location) {
var q = $q.defer();
try {
db_ = $cordovaSQLite.openDB({
name: dbName,
location: location
});
q.resolve(db_);
} catch (e) {
q.reject("Exception thrown while opening DB " + JSON.stringify(e));
}
return q.promise;
};
var performQuery_ = function (query, params, out) {
var q = $q.defer();
params = params || [];
out = out || [];
//open the DB
openDB_(appConfig.sqlite_db, appConfig.sqlite_db_location)
.then(function (db) {
//then execute the query
$cordovaSQLite.execute(db, query, params).then(function (res) {
//then add the records to the out param
console.log("Query executed", JSON.stringify(query));
for (var i = 0; i < res.rows.length; i++) {
out.push(res.rows.item(i));
console.log("Added row to set", JSON.stringify(res.rows.item(i)));
}
if (res.rows.length == 0 && self.bDebug === true) {
console.log("No results found ");
}
}, function (err) {
console.log("Query failed", JSON.stringify(query));
q.reject();
});
db_.open(function () {
q.resolve("DB Opened")
}, function () {
q.reject("Failed to open DB");
});
}, function (err) {
console.log(JSON.stringify(err), this.query);
q.reject(err);
});
return q.promise;
};
// public methods
var execute = function (query, params, out) {
var q = $q.defer();
performQuery_(query, params, out).then(function () {
q.resolve([query, params]);
}, function (err) {
q.reject([query, params, err]);
});
return q.promise;
};
return {
execute: execute
};
})

AngularJS/Ionic promises - sometimes page loads forever

I am using promises in my controller, and most of the times it works well. But sometimes it just loads forever and the WordPress.getAllCategories() function does not even get called.
This is my controller:
var mod = angular.module('app.controllers.home', []);
mod.controller('HomeCtrl', function ($scope, $q, $sce, $ionicPlatform, WordPress, Loading) {
console.log('HomeCtrl init');
$scope.$on('$ionicView.enter', function () {
Loading.show();
WordPress.getAllCategories()
.then(function (cats) {
console.info(angular.toJson(cats));
console.info('cats ^');
$q.all(cats.data.map(function (cat) {
var d = $q.defer();
console.error(cat.name);
WordPress.getLatestPostOfCategory(cat.id)
.then(function (post) {
console.debug(post.data.title.rendered);
WordPress.getMediaById(post.data.featured_media)
.then(function (media) {
console.log(media.data.source_url);
cat.firstPost = {};
cat.firstPost.id = post.data.id;
cat.firstPost.title = post.data.title.rendered;
cat.firstPost.content = post.data.content.rendered;
if (cat.firstPost.title.length > 50) {
cat.firstPost.title = cat.firstPost.title + '...';
}
if (cat.firstPost.content.length > 70) {
cat.firstPost.content = cat.firstPost.content.substr(0, 60) + '...';
}
cat.firstPost.thumbnail = media.data.source_url;
d.resolve(cat);
}, function (err) {
console.error(angular.toJson(err));
});
});
return d.promise;
})).then(function (cats) {
console.log('Loaded all articles and for main page.');
$scope.homeCategories = cats;
Loading.hide();
});
});
});
});
Is there anything wrong in my controller?
P.S. I also debug all the WordPress service functions and they work just fine, and provide the needed data.
EDIT:
Sometimes when it loads forever, I see the console.error(cat.name); debug message only logs 3 messages. But still proceeds to the next function...
This is how I solved it, by Bergi's advice.
Source for help: Promise anti pattern by Gorgi Kosev (bluebird)
var categories = [];
function sort() {
return WordPress.getAllCategories()
.then(function (cats) {
console.log('thens');
return $q.all(cats.data.map(function (cat) {
console.info('cat: ' + cat.name);
var category = {};
category.name = cat.name;
return WordPress.getLatestPostOfCategory(cat.id)
.then(function (post) {
var post = post.data;
category.post = {};
category.post.id = post.id;
category.post.title = post.title.rendered;
category.post.content = post.content.rendered;
console.log('ID: ' + category.post.id + ', title: ' + category.post.title);
return WordPress.getMediaById(post.featured_media);
}).then(function (media) {
category.post.thumbnail = media.data.source_url;
categories.push(category);
console.log('Pushed category "' + category.name + '"');
});
}));
}, function (err) {
console.error('ERR1');
console.error(angular.toJson(err));
});
}
sort()
.then(function () {
console.info('LOADED ALL CATEGORIES');
$scope.categories = categories;
}, function (err) {
console.error('err:' + angular.toJson(err));
});

is invoking a service into another service in angular

I need to save data and then I need to dispaly the changes immediately afterwards.
That's why I Have a
updateSaisine which allows me to update data
getOneSaisine which allows me get the data and display them:
Which is the more correct way and for which reasons ?
Should I write:
$scope.saveSaisine = function() {
saisineSrv.updateSaisine($scope.currentSaisine.idSaisine, $scope.currentSaisine).
then(
function() {
$scope.errorMessages = [];
if ($scope.currentSaisine.idMotif) {
toaster.pop('success', 'Réponse', 'Success');
angular.element('#modalSaisine').modal('hide');
saisineSrv.getOneSaisine($scope.currentSaisine.idSaisine, $scope.currentSaisine).then(function(response) {
$scope.currentSaisine.dateModif = response.dateModif;
});
},
function error(response) {
$scope.errorMessages = response.data;
toaster.pop('error', 'Réponse', 'We have a problem');
}
);
};
OR
$scope.saveSaisine = function() {
saisineSrv.updateSaisine($scope.currentSaisine.idSaisine, $scope.currentSaisine).
then(
function() {
$scope.errorMessages = [];
if ($scope.currentSaisine.idMotif) {
toaster.pop('success', 'Réponse', 'Success');
angular.element('#modalSaisine').modal('hide');
},
function error(response) {
$scope.errorMessages = response.data;
toaster.pop('error', 'Réponse', 'We have a problem');
}
);
saisineSrv.getOneSaisine($scope.currentSaisine.idSaisine, $scope.currentSaisine).then(function(response) {
$scope.currentSaisine.dateModif = response.dateModif;
});
};
the first option is a correct way how you should refresh your data because these services are asynchronous thus in the second example you may don't get fresh data (the getOneSaisine can finish before updateSaisine).

Resources