I have a cross platform app developed using AngularJS, JS, Phonegap/Cordova, Monaca and Onsen UI.
I have implemented a SQLite Database in the app to store various data to ensure that the app can be used offline. I have done a few simple tests and this is all working as intended.
I now try to create all the tables I will need in the app (28 in total) in my apps first views onDeviceReady() function. For this I create an array of objects that I will pass one by one to the SQLite CREATE statement as below.
Setting up the table values
// Form values
var formValues = [{
tablename: "table_1",
id: "id_1",
desc: "desc_1",
dataType: "TEXT"
}, {
tablename: "table_2",
id: "id_2",
desc: "desc_2",
dataType: "TEXT"
}, {
...
...
...
...
}, {
tablename: "table_28",
id: "id_28",
desc: "desc_28",
dataType: "TEXT"
}];
Creating the tables
function onDeviceReady() {
for (var i = 0; i < formValues.length; i++) {
var createFormValues = 'CREATE TABLE IF NOT EXISTS ' + formValues[i].tablename + ' (' + formValues[i].id + ' INTEGER, ' + formValues[i].desc + ' ' + formValues[i].dataType + ')';
alert("SQL: " + createFormValues +
"\n\nForm: " + formValues +
"\nName: " + formValues[i].tablename +
"\nID: " + formValues[i].id +
"\nDesc: " + formValues[i].desc +
"\nType: " + formValues[i].dataType);
db.transaction(function (tx) { tx.executeSql(createFormValues); });
}
};
When I run the above code, the alert shows that all the information including the SQL statement is OK. However, when I query each table following the for() loop, it says no tables are created EXCEPT the last table.
I then try the following which DOES create all the tables but is obviously less efficient and manageable.
Calling a function
createFormTables("table_1", "id_1", "desc_1", "TEXT");
createFormTables("table_2", "id_2", "desc_2", "TEXT");
createFormTables("...", "...", "...", "...");
createFormTables("table_28", "id_28", "desc_28", "TEXT");
Executing the function
createFormTables: function (tableName, id, description, dataType) {
var sql = 'CREATE TABLE IF NOT EXISTS ' + tableName + ' (' + id + ' INTEGER, ' + description + ' ' + dataType + ')';
alert("Create Form Field SQL: " + sql);
db.transaction(function (tx) { tx.executeSql(sql); });
},
I know that the SQLite statements are executed asynchronously but I dont understand why that would create the tables in the second example and not the first? How do I create the tables using the first example? I can set a $timeout to execute each INSERT statement but this seems unnecessary and will cause delays.
I would use the JavaScript "Object" aproach to avoid conflict when calling function 'createFormTables' with something like:
var QuerySet = function(){
//OPTIONAL: I use JQuery Deferred to catch when an async event has finished
this.defRes = new $.Deferred();
this.defResProm = this.defRes.promise();
};
QuerySet.prototype.createFormTables= function(inputData){
//do your stuff and manage when this.defRes is resolved and results it sends back (if a result is needed)
this.sqlToExecute = "// YOUR SQL QUERY";
var self=this;
$.when(
(new SqlResult(this.sqlToExecute)).execSqlCustomDeferred(), //read below for SqlResult() explanations
self
).done(function(sqlRes,self){
self.defRes.resolve();
});
return this.defResProm;
};
Then in the code:
creatFromTables[i] = (new QuerySet()).createFormTables(inputData);
createFromTables[i].defRes.done(function(res){//do stuff with the result});
Don't need to use $.Deferred() but I think it could be useful if you want to know when all the tables have been created.
And here is sqlResult that is used to call the transaction on the DB itself:
var SqlResult = function(sqlToExecute,bracketValues){
//console.log("SqlResult("+sqlToExecute+','+bracketValues+') starts');
this.sqlToExecute = sqlToExecute;
this.bracketValues =bracketValues;
};
SqlResult.prototype.execSqlCustomDeferred = function(){
var execSqlCustomDeferredRes = $.Deferred();
var execSqlCustomDeferredResProm = execSqlCustomDeferredRes.promise();
//console.log("SqlResult.execSqlCustomDeferred sqlToExecute is: "+this.sqlToExecute);
var sqlToExecuteForTx = this.sqlToExecute;
var bracketValuesForTx = this.bracketValues;
DbManagement.db.transaction(function(tx){
console.log("SqlResult.execSqlCustomDeferred in TX sqlToExecute is: "+sqlToExecuteForTx);
if(bracketValuesForTx!=null){
console.log("SqlResult.execSqlCustomDeferred bracket value is: "+ArrayManagement.arrayToString(bracketValuesForTx));
}
tx.executeSql(sqlToExecuteForTx,bracketValuesForTx,success,error);
function success(tx,rs){
//console.log('before execSqlCustomDeferredRes resolve ' + execSqlCustomDeferredRes.state());
execSqlCustomDeferredRes.resolve(rs);
//console.log('before execSqlCustomDeferredRes resolve after ' + execSqlCustomDeferredRes.state());
}
function error(tx,error){
console.log('execSqlCustomDeferred error ' + error.message);
execSqlCustomDeferredRes.reject(error);
}
});
return execSqlCustomDeferredResProm;
};
Related
I'm trying to allow users to add routes to points on a leaflet map, using sql queries to a database of points. Because I want each point to have more than one route that passes through it, I added additional columns: "route2", "routenumber2" till "route5", "routenumber5" to the original "route" and "routenumber".
The following code does work, but I figured it's repetitive to write it out five times, each time only with a different set of columns.
if (e.target && e.target.id == "submitrest") {
const enteredRoute = document.getElementById("input_Route").value; // from user form input
const enteredNumber = document.getElementById("input_routenumber").value;
const enteredNickname = document.getElementById("input_nick").value;
const enteredID = document.getElementById("input_cartodb_id").value;
cartoData.eachLayer(function (layer) {
// SQL to put layer in -- Building the SQL query
let sql =
"UPDATE stops " +
"SET area = '" + enteredRoute + "', " +
"route_number = '" + enteredNumber + "', " +
"nickname = '" + enteredNickname + "' " +
"WHERE cartodb_id = " + enteredID + ";"
console.log(sql);
if (enteredRoute && enteredNickname && enteredNumber && enteredID) {
fetch(url1, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
body: "q=" + encodeURI(sql)
})
.then(function (response) {
return response.json();
})
.then(function (data) {
console.log("Data saved:", data);
})
What I am asking is if, like how document.getElementById allows the user to input different data each time and send the data using the same code, there is a less repetitive way to make an sql query function that allows me to put the data into "route3" and "routenumber3" if "route2" and "routenumber2" are already full, and so on.
What I would like is a row in CartoDB to have five sets/columns routes and routenumbers that a user can input data to, preferably with less duplicated code.
So far I've tried using Javascript class constructors and the Factory Method. Can I get these to allow the the columns specified in the sql query to be variable? Thank you
I have two functions I am using to pull JSON from my server side to then display it to HTML.
The first function that pulls the data from the route handler is successfully pulling the data and parsing it successfully with JSON.parse() and displaying the needed information to the console without issue. I am not having and ajax or route handling issue...
Here is how I am dealing with the JSON first in my function called "projectInfo()":
projInfo = JSON.stringify(data);
console.log("DEBUG DONE WITH CAPTURING project_info DATA: " );
// This console.log() prints the JSON string
// successfully pulled from route handler
// var projInfo is a local string var declared in the scope of
// this first function
console.log("var projInfo: " + projInfo);
// parse JSON data in projInfo and store in string var p
// string var p is a local var declared inside of the scope
// of this function
p = JSON.parse(projInfo);
console.log("Parsed Project JSON: " + p.Project);
// update "Global" pInfo with the value of the JSON data for
// "Project" as needed
pInfo = p;
console.log("What is inside of pInfo???: " + pInfo);
// This last console.log prints [object Object] to console
// How do I pul the value out of this Object?
The second function calls the first function in order to update a global variable with the parsed JSON data that I need to then display the global variable's data to the DOM element that I am trying to display.
Here is how I am trying to update my global var with a JSON Object in my function called "loginFun()":
// Call projectInfo() in order to update Global pInfo
// with the needed project info
projectInfo();
// This console.log() prints nothing...?
console.log("projectInfo var data should be aa2: " + pInfo);
document.getElementById("userBar").style.display = "";
// This is where I try to Display pInfo in the DOM but I only get Undefined...?
document.getElementById("signedinas").innerHTML = "<font face=\"verdana\" size =\"4\" color=\"white\">Logged in as: " + username + " Project: " + pInfo + " </font>";
When I JSON.parse() the data in the first function I run a console.log() statement and I get the needed data to print from a variable local to the function I am getting my JSON with using ajax and I verify that the function is in fact doing what I need so that part is good up until I get the [object Object] output.
I am having issues when I call this function from my second function to then try to use the global variable which should have the data stored.
when I try to use the global variable with the needed data I get an 'undefined'...
I have also tried returning the data that has been parsed in the first function to then storehttps://codepen.io/lopezdp/pen/owKGdJ the value returned into a local variable in the second function but I still get 'undefined'.
If you would like to see the complete code for both functions I have put them on a CodePen to make it easier at:
https://codepen.io/lopezdp/pen/owKGdJ
How can I get my Project Data to display in my DOM element?
EDIT: The JSON Data that I am using looks like this:
{"User":"aa2","Owner":"aa2_role","Status":"locked","Port":"5432","Description":"Transferred from CFS01 on Jun29","Project":"aa2","Server":"localhost"}
I rewrote your login function like this and it worked for me. I also eliminated the projectInfo() function!
var allMn = [];
var tags = [];
var pInfo = '';
function loginFun() {
var username = document.getElementById('username').value;
var password = document.getElementById('password').value;
if (username == "" || password == "") {
alert("Required fields cannot be left blank.");
} else {
$.ajaxSetup({
cache: false
});
$.ajax({
type: 'GET',
url: 'http://139.169.63.170:' + port + '/login/' + username + "zlz" + password,
cache: false,
success: function (data) {
// NEED SUB ROUTINE HERE FOR AJAX CALL DPL
// Make async call to ccdd tool database to get new data
// This collects allMn[] data!!!
getMnJson();
// END SUB ROUTINE HERE
// Checks to make sure user is logged in if not
// the condition redirects user to loginFun()
if (data.search("HTTP ERROR: ") != -1) {
alert("Login Failed.");
document.getElementById('username').value = "";
document.getElementById('password').value = "";
document.getElementById('searchResults').innerHTML = "Login Failed";
document.getElementById('searchRBar').style.display = "";
loginFun();
} else {
login = 1;
// Call projectInfo() in order to update pInfo with the needed project info
//projectInfo();
var projInfo = '';
var p = '';
// Get all Mn Data on startup tp display in DOM -DPL
$.ajax({
type: 'GET',
url: 'http://139.169.63.170:' + port + '/role',
dataType: 'json',
cache: true,
success: function (data) {
// projInfo = JSON.stringify(data);
console.log("DEBUG DONE WITH CAPTURING project_info DATA: " );
// console.log("var projInfo: " + projInfo);
// parse JSON data in projInfo
p = data['Project']; //JSON.parse(projInfo);
console.log("Parsed Project JSON: " + p);
// update "Global" pInfo with the value of the JSON data for "Project" as needed
pInfo = p;
console.log("What is inside of pInfo???: " + pInfo);
document.getElementById("signedinas").innerHTML = "<font face=\"verdana\" size =\"4\" color=\"white\">Logged in as: " + username + " Project: " + pInfo + " </font>";
}
}).fail(function () {
alert("Login Failed.");
document.getElementById('username').value = "";
document.getElementById('password').value = "";
console.log("Error. /role data access Error.");
});
console.log("projectInfo var data should be aa2: " + pInfo);
document.getElementById("userBar").style.display = "";
// Display pInfo in the DOM
// document.getElementById("signedinas").innerHTML = "<font face=\"verdana\" size =\"4\" color=\"white\">Logged in as: " + username + " Project: " + pInfo + " </font>";
$("div.create").children().remove();
//-------------------------------------------------------------------END OF GLOBAL VARIABLES
$.ajaxSetup({
cache: false
});
// get table data from proxy server on port 7071 DPL
// NEED SUB ROUTINE HERE FOR AJAX CALL
// Make call to server-side code to reload JSON data into table from port 7071
pushJsonData();
// END SUB ROUTINE HERE!!!
// getTblJson();
}
}
}).fail(function () {
alert("Login Failed.");
document.getElementById('username').value = "";
document.getElementById('password').value = "";
console.log("Error. Need user Credentials");
});
}
}
The docs for intercom-node, https://github.com/intercom/intercom-node, say that I can access my users data with the client.users.list or client.users.listBy or client.users.find.
The problem is I am only getting 50 users in the body of the response object (ie resp.body.users.length is 50) and I need all the users. Does anyone know how to query the API to get all the users from the intercom.io app?
var Intercom = require('intercom-client')
var client = new Intercom.Client('<APP ID>', '<API KEY>')
client.users.list( function(err, resp) {
console.log(resp.body.users.length)
})
With the current library version 2.11.2 the next page can be retrieved by specifying the page number and using .listBy or using the pagination object https://github.com/intercom/intercom-node#pagination after using .list (demo code below)
var Intercom = require('intercom-client');
var client = new Intercom.Client({ token: TOKEN });
client.users.listBy({page: 1}, printDetails);
client.users.listBy({page: 2}, printDetails);
client.users.list(printDetailsAndGetNextPage);
client.users.listBy({page: 1}, printDetailsAndGetNextPage);
function printDetails(d){
var output = d.body;
console.log("Page: " + output.pages.page + " out of " + output.pages.total_pages);
}
function printDetailsAndGetNextPage(d){
var output = d.body;
console.log("Page: " + output.pages.page + " out of " + output.pages.total_pages);
if(output.pages.page < output.pages.total_pages) {
console.log("Getting next page...");
client.nextPage(output.pages, printDetails);
}
else {
console.log("No more pages left");
}
}
When you create model in LoopBack framework, you can inherit from PersistedModel class. This way all the HTTP methods are generated. I am wondering how can I disable certain HTTP methods?
One option is to override functions from PersistedModel with empty logic, but want method to disappear from Swagger API explorer.
I did below in model.js files like below. This makes table readonly.
module.exports = function(model) {
var methodNames = ['create', 'upsert', 'deleteById','updateAll',
'updateAttributes','createChangeStream','replace','replaceById',
'upsertWithWhere','replaceOrCreate'
];
methodNames.forEach(function(methodName) {
disableMethods(model,methodName)
});
}
function disableMethods(model,methodName)
{
if(methodName!='updateAttributes')
model.disableRemoteMethod(methodName, true);
else
model.disableRemoteMethod(methodName, false);
}
The only thing which requires additional care is disabling of custom model methods (like User.login). You need to call disableRemoteMethod before explorer middleware: https://github.com/strongloop/loopback/issues/686
An update on Santhosh Hirekerur answer to make it hide everything on LB3, stop using the deprecated Model.disableRemoteMethod method and also a more intelligent way to hide updateAttributes and any other future methods that may work the same.
I check to see if the method is on the prototype and if it is, prefix the name with prototype. before we disableRemoteMethodByName:
module.exports = function (model) {
var methodNames = [
'create',
'upsert',
'deleteById',
'updateAll',
'updateAttributes',
'patchAttributes',
'createChangeStream',
'findOne',
'find',
'findById',
'count',
'exists',
'replace',
'replaceById',
'upsertWithWhere',
'replaceOrCreate'
];
methodNames.forEach(function (methodName) {
if (!!model.prototype[methodName]) {
model.disableRemoteMethodByName('prototype.' + methodName);
} else {
model.disableRemoteMethodByName(methodName);
}
});
}
I put the above code in server/middleware/disable-methods.js and call it from a model like so:
var disableMethods = require('../../server/middleware/disable-methods');
module.exports = function (Model) {
disableMethods(Model);
}
Found answer in documentation.
For example this disables PersistedModel.deleteById:
var isStatic = true;
MyModel.disableRemoteMethod('deleteById', isStatic);
So it looks like you it's not possible to disable all DELETE actions at the same time. For example method PersistedModel.deleteAll remains accessible in given example.
Developer must disable each relevant method from PersistedModel class explicitly.
Relevant docs are here
Sections:
Hiding methods and REST endpoints
Hiding endpoints for related models
In case of loopback 3
I had the same problem.
My first solution was to manually update the "public":true items in server/model-configuration.json but it was overridden anytime I used the Swagger tool to refresh the LoopBack API (with slc loopback:swagger myswaggerfilename command from the project root).
I finally wrote a Grunt task as a reliable workaround.
Run it just after a slc loopback:swagger generation or just before running the API live.
you just have to specify the names of the paths I want to expose in the javascript array list_of_REST_path_to_EXPOSE
and make sure you are happy with the backup folder for the original /server/model-config.json file.
I wanted to share it with you in case:
https://github.com/FranckVE/grunt-task-unexpose-rest-path-loopback-swagger
Basically:
module.exports = function (grunt) {
grunt.registerTask('unexpose_rest_path_for_swagger_models_v1', function (key, value) {
try {
// Change the list below depending on your API project :
// list of the REST paths to leave Exposed
var list_of_REST_path_to_EXPOSE =
[
"swagger_example-api_v1",
"write_here_the_paths_you_want_to_leave_exposed"
];
// Location of a bakup folder for modified model-config.json (change this according to your specific needs):
var backup_folder = "grunt-play-field/backups-model-config/";
var src_folder = "server/";
var dest_folder = "server/";
var src_file_extension = ".json";
var src_file_root_name = "model-config";
var src_filename = src_file_root_name + src_file_extension;
var dest_filename = src_file_root_name + src_file_extension;
var src = src_folder + src_filename;
var dest = dest_folder + dest_filename;
var free_backup_file = "";
if (!grunt.file.exists(src)) {
grunt.log.error("file " + src + " not found");
throw grunt.util.error("Source file 'model-config.json' does NOT exists in folder '" + src_folder + "'");
}
// timestamp for the backup file of model-config.json
var dateFormat = require('dateformat');
var now = new Date();
var ts = dateFormat(now, "yyyy-mm-dd_hh-MM-ss");
// backup model-config.json
var root_file_backup = src_file_root_name + "_bkp" + "_";
var root_backup = backup_folder + root_file_backup;
free_backup_file = root_backup + ts + src_file_extension;
if (!grunt.file.exists(root_file_backup + "*.*", backup_folder)) {
//var original_file = grunt.file.read(src);
grunt.file.write(free_backup_file, "// backup of " + src + " as of " + ts + "\n");
//grunt.file.write(free_backup_file, original_file);
grunt.log.write("Creating BACKUP"['green'] + " of '" + src + "' " + "to file : "['green'] + free_backup_file + " ").ok();
} else {
grunt.log.write("NO BACKUP created"['red'] + " of '" + src + "' " + "because file : " + free_backup_file + " ALREADY EXISTS ! "['red']).error();
throw grunt.util.error("Destination backup file already exists");
}
// load model-config.json
var project = grunt.file.readJSON(src);//get file as json object
// make modifications in model-config.json
for (var rest_path in project) {
if (rest_path.charAt(0) === "_") {
grunt.log.write("SKIPPING"['blue'] + " the JSON item '" + rest_path + "' belonging to the " + "SYSTEM"['blue'] + ". ").ok();
continue; // skip first level items that are system-related
}
if (list_of_REST_path_to_EXPOSE.indexOf(rest_path) > -1) { //
project[rest_path]["public"] = true;
grunt.log.write("KEEPING"['green'] + " the REST path '" + rest_path + "' " + "EXPOSED"['green'] + ". ").ok();
} else {
project[rest_path]["public"] = false;
grunt.log.writeln("HIDING"['yellow'] + " REST path '" + rest_path + "' : it will " + "NOT"['yellow'] + " be exposed.");
}
}
}
I wanted to hide the PATCH method, but when I was trying to hide it also I got to hide the PUT method , I was using this line :
Equipment.disableRemoteMethod('updateAttributes', false);
but later I found a way to only hide the PATCH method, this line works perfect for me.
Equipment.sharedClass.find('updateAttributes', false).http = [{verb: 'put', path: '/'}];
The above line overrides the original http that updateAttributes method had.
[{verb: 'put', path: '/'}, {verb: 'patch', path: '/'}]
I am using Parse.com and access it using Javascript. I want to search data in such a manner that it will not search for case sensitive like if i search abc then it will give abc, Abc, ABC,aBc etc all. but currently it gives only abc.
JavaScript:
var search_data="Temp";
var product = Parse.Object.extend("product");
var query = new Parse.Query(product);
query.contains("name",search_data);
query.find({
success: function(results) {
console.log("Total Product Found : "+results.length);
printSearchProducts(results,query); //custom method to print product detail
},
error: function(error) {
console.log("Error: " + error.code + " " + error.message);
}
});
Actual Result: Temp1,Temp2,Temp3,ProductTemp3,Temp4Product etc.
Required Result: Temp1,Temp2,Temp3,ProductTemp3,Temp4Product,temp5,producttemp6,TEMP7,tEMp8 etc.
For that you can use Parse.Query's matches() function, which even it's not indicated in its documentation page, but you can use it like this :
query.matches(key, value, 'i');
Hope that can help.
At this point in time you aren't able to perform case insensitive searches via a query.
A very easy workaround for this however, is to store a lower case version of the field you need to do this query on.
Creating The Item
var Product = Parse.Object.extend("product");
var newProduct = new Product();
var productName = "Temp4Product";
newProduct.set("name",productName);
newProduct.set("name_lowercase",productName.toLowerCase());
newProduct.save();
Performing the query
var search_data="Temp";
var product = Parse.Object.extend("product");
var query = new Parse.Query(product);
query.contains("name_lowercase",search_data.toLowerCase());
query.find({
success: function(results) {
console.log("Total Product Found : "+results.length);
printSearchProducts(results,query); //custom method to print product detail
},
error: function(error) {
console.log("Error: " + error.code + " " + error.message);
}
});
Note in the above example I've used the string.toLowerCase() function, however there may be a more appropriate function for you to use. Basically you want to find a way to "simplify" your data so that you can perform the appropriate query.
The solutions that suggest an extra stored field with the lowercase value have the disadvantage of requiring additional storage. The matches method works, but has a minor performance cost.
A better workaround that avoids both of these issues is to use an aggregate with the $toLower expression in a projection.
I have solved this issue.
I remove constraint query.contains("name",search_data); and apply manual search using java script inside printSearchProducts method like:
var search_data="Temp";
var product = Parse.Object.extend("product");
var query = new Parse.Query(product);
query.limit(500);
query.find({
success: function(results) {
printSearchProducts(results,query,search_data);
},
error: function(error) {
console.log("Error: " + error.code + " " + error.message);
}
});
printSearchProducts Method Where search manually via Java Script.
function printSearchProducts(results,query,search_data) {
var counter = results.length;
var productName = '',i=0;
search_data = search_data.toLowerCase();
if(counter){
while(i<counter){
var found = -1;
productName = results[i].get("name");
var lower_name = productName.replace(/[^a-zA-Z]/g,'_').toLowerCase();
found = lower_name.indexOf(search_data);
if(found>=0) {
//Access or print the required result here
}
i++;
}
}
}