Ionic Prepopulated Database with Antair Cordova SQLitePlugin [help request] - ios

____ INTRO
Hello everyone, first of all, three clarifications:
My english is not good, so I beg your pardon in advance for my mistakes,
I'm a newbie so forgive me for inaccuracies,
I have previously searched and tried the solutions I found on the internet but still I can not solve the problem of embedding a prepopulated database.
____ THE GOAL
I want to develop an app for iOS and Android with a prepopulated database.
Just for example, the database consists of 15.000 records each one made of three key-value pair (id, firstname and lastname).
___ WHAT I DID
Steps:
ionic start myapp blank
cd myapp
ionic platform add ios
ionic platform add android
Then I created an sqlite database for testing purpose, named mydb.sqlite, made of one table people containing two id, firstname, lastname records.
I decided to use the following plugin: https://github.com/Antair/Cordova-SQLitePlugin
That's because it can be installed with cordova tool.
ionic plugin add https://github.com/Antair/Cordova-SQLitePlugin
(Alert: I think that the instructions on the website show an incorrect reference - "cordova plugin add https://github.com/brodysoft/Cordova-SQLitePlugin" - which refers to another plugin).
Then, following the instructions on the plugin website, I copied the database to myapp/www/db/ so that it can now be found at myapp/www/db/mydb.sqlite
I modified the index.html including the SQLite plugin just after the default app.js script:
<!-- your app's js -->
<script src="js/app.js"></script>
<script src="SQLitePlugin.js"></script>
I also write some lines of code in index.html file to show a button:
<ion-content ng-controller="MyCtrl">
<button class="button" ng-click="all()">All</button>
</ion-content>
Finally I had modified ./js/app.js:
// Ionic Starter App
var db = null;
angular.module('starter', ['ionic' /* What goes here? */ ])
.run(function($ionicPlatform) {
$ionicPlatform.ready(function() {
// some predefined code has been omitted
window.sqlitePlugin.importPrepopulatedDatabase({file: "mydb.sqlite", "importIfExists": true});
db = window.sqlitePlugin.openDatabase({name: "mydb.sqlite"});
}); // $ionicPlatform.ready
}) // .run
.controller('MyCtrl', function($scope){
$scope.all = function(){
var query = "SELECT * FROM people";
// I don't know how to proceed
}; // $scope.all
}); // .controller
___ THE PROBLEM
I don't know how to proceed in the controller section to query all the records (just an example of query) and show the results in the console.log.
I think that the following code must be completed in some way:
angular.module('starter', ['ionic' /* What goes here? */ ])
And also the code inside controller section must be completed:
$scope.all = function(){
var query = "SELECT * FROM people";
// I don't know how to proceed
}; // $scope.all
___ FINAL THANKS
Thank you in advance for the help you will give to me.

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.

Related

Random image from folder

How can I make possible that the app will load all of the images from the specific folder and then put in array and choose one image randomly? When chose one then pass to the fronted to show the image. How to do that too?
I am C# developer but not long time ago I found ElectronJS and this framework does everything easier so therefore I am moving to this framework.
I did in C# programming this way:
// basic settings.
var ext = new List<string> { ".jpg", ".gif", ".png" };
// we use same directory where program is.
string targetDirectory = Directory.GetCurrentDirectory() + "\\assets\\" + "images\\" + "animals\\";
// Here we create our list of files
// New list
// Use GetFiles to getfilenames
// Filter unwanted stuff away (like our program)
if (Directory.Exists(targetDirectory))
{
Files = new List<string>
(Directory.GetFiles(targetDirectory, "*.*", SearchOption.TopDirectoryOnly)
.Where(s => ext.Any(es => s.EndsWith(es))));
// Show first picture so we dont need wait 3 secs.
ChangePicture();
}
else
{
panel5.BackgroundImage = new Bitmap(Resources.doggy);
}
I don't know how to do in ElectronJS.
Thank you in advance the answers.
Alright. I found the solution.
However I don't understand the people who are giving negative reputation for the opened question. If they are giving negative reputation then they could explain why.
Well anyway, I did fix this issue with this way:
I created images.js file and added this:
var fs = require('fs');
function getRandImage() {
var files = fs.readdirSync('./assets/images/animals/')
/* now files is an Array of the name of the files in the folder and you can pick a random name inside of that array */
let chosenFile = files[Math.floor(Math.random() * files.length)]
console.log('../assets/images/animals/' + chosenFile);
return '../assets/images/animals/' + chosenFile;
}
module.exports = { getRandImage }
I used console to see if the value is correct, otherwise others can delete that part.
Sending the data to the renderer process:
const { getRandImage } = require('./images');
child.webContents.send('random-image', getRandImage());
I did put in the preload.js file the following (I used the starter pack electronjs github to start with something):
var { ipcRenderer } = require('electron');
ipcRenderer.on('random-image', function (event, store) {
document.getElementById("randompic").src = store;
console.log(store);
});
Same here, I did use console.log just for test the value is correct and I used to change the randompic ID related image src html to the randomly chosen image.
Hopefully I did helping those people who are newbie as me.

passing parameters to power bi filter programmatically from asp.net MVC

I have developed a powerBI report and deployed the same in Workspace.
From PowerBI interface I viewed the report and am able to add filter like this:
filter=tablename~2Fcolumnname eq 4
The condition is working fine from the interface. But, when I add filter condition in the code of my asp.ner MVC application, filter does not work.
The condition is working fine from the interface.
But, when I add filter condition in the code of my asp.ner MVC application, filter does not work.
My sample code:
var client = new PowerBIClient(new Uri(ApiUrl), tokenCredentials);
var reports = await client.Reports.GetReportsInGroupAsync(WorkspaceId);
report = reports.Value.FirstOrDefault(r => r.Id == "aa07v254-8t46-2387-4k53-2725y90n3456");
report.EmbedUrl = report.EmbedUrl + "?filter=tablename/columnname%20eq%204";
Please help me diagnose, why the filter is not working.
You don't need to change the embed url, instead try to use the following example:
// Build the filter you want to use. For more information, See Constructing
// Filters in https://github.com/Microsoft/PowerBI-JavaScript/wiki/Filters.
const filter = {
$schema: "http://powerbi.com/product/schema#basic",
target: {
table: "Geo",
column: "Region"
},
operator: "In",
values: ["West"]
};
// Get a reference to the embedded report HTML element
var embedContainer = $('#embedContainer')[0];
// Get a reference to the embedded report.
report = powerbi.get(embedContainer);
// Set the filter for the report.
// Pay attention that setFilters receives an array.
report.setFilters([filter])
.then(function () {
Log.logText("Report filter was set.");
})
.catch(function (errors) {
Log.log(errors);
});

Script does not trigger on FormSubmit: Remove Duplicates in Google Sheet of Google Form responses based on column

I am trying to remove older duplicate form responses based on a column using the following code.
The credit for the code goes to: http://www.jacorre.com/tutorial/remove-duplicate-rows-google-spreadsheets/
The code in my script is:
function removeDuplicates() {
var ss = SpreadsheetApp.getActiveSpreadsheet(),
responses = ss.getSheetByName('Name of Source Sheet'),
range = responses.getDataRange(),
numRows = range.getNumRows()-1,
data = range.getValues(),
columnHeadings = [data[0]],
newData = [];
for (var i=numRows; i>0; i--) {
var row = data[i],
duplicate = false;
for (var j in newData) {
if (row[4] == newData[j][4]) {
duplicate = true;
// [4] is the column number from the 1st column. the above would be 1 + 4 = 5th column
}
}
if (!duplicate) {
newData.push(row);
}
}
var final = ss.getSheetByName('Name of Destination Sheet');
if (!final) {
var final = ss.insertSheet('Name of Destination Sheet');
}
final.clearContents();
final.getRange(1,1,1,columnHeadings[0].length).setFontWeight('bold').setValues(columnHeadings);
final.getRange(2, 1, newData.length, newData[0].length).setValues(newData);
}
This has been set to trigger on Form Submit. It works well on new form submissions.
However, when an existing response is edited using 'Form Edit URL' from: https://webapps.stackexchange.com/questions/89551/show-url-used-to-edit-responses-from-a-google-form-in-a-google-spreadsheet-by-us/89566 the values are not updated into the new sheet.
But if the function is run manually the updated row is updated to the new sheet.
How can I sort this problem? Any help will be appreciated. Thank you.
From my own answer posted at Web Applications SE.
I just did a test and found that the on form submit event it's not
being triggered when a response is edited.
I'm not sure if the on form submit trigger is working as intended, if
the above is due to a bug or to a glitch. To be sure, post an issue to
the Google Apps Script Issue
Tracker.
As a workaround, instead of using the on form submit event, use
another way to run your script, like a time-drive trigger.
References
Custom menus in Google Apps - Google Apps Script Guides
Simple or installable triggers - Google Apps Script Guides
Google Apps Script Support

Security Error when trying to load content from resource in a Firefox Addon (SDK)

I am creating a firefox addon using the SDK. My goal is simple, to intercept a specific iframe and load my own HTML page (packaged as a resource with my addon) instead of the content that was requested originally.
So far I have the following code:
var httpRequestObserver =
{
observe: function(subject, topic, data)
{
var httpChannel, requestURL;
if (topic == "http-on-modify-request") {
httpChannel = subject.QueryInterface(Ci.nsIHttpChannel);
requestURL = httpChannel.URI.spec;
var newRequestURL, i;
if (/someurl/.test(requestURL)) {
var ioService = Cc["#mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
httpChannel.redirectTo(ioService.newURI(self.data.url('pages/test.html'), undefined, undefined));
}
return;
}
}
};
var observerService = Cc["#mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
observerService.addObserver(httpRequestObserver, "http-on-modify-request", false);
This code works in that it detects the proper iframe loading and does the redirect correctly. However, I get the following error:
Security Error: Content at http://url.com may not load or link to
jar:file:///.../pages/test.html.
How can I get around this limitation?
actually man i was really over thinking this.
its already solved when I changed to using loadContext. Now when you get loadContext you get the contentWindow of whatever browser element (tab browser, or frame or iframe) and then just abort the http request like you are doing and then loadContext.associatedWindow.document.location = self.data('pages/tests.html');
done
ill paste the code here removing all the private stuff. you might need the chrome.manifest ill test it out and paste the code back here
Cu.import('resource://gre/modules/Services.jsm');
var httpRequestObserver = {
observe: function (subject, topic, data) {
var httpChannel, requestURL;
if (topic == "http-on-modify-request") {
httpChannel = subject.QueryInterface(Ci.nsIHttpChannel);
requestURL = httpChannel.URI.spec;
var newRequestURL, i;
if (/someurl/.test(requestURL)) {
var goodies = loadContextGoodies(httpChannel);
if (goodies) {
httpChannel.cancel(Cr.NS_BINDING_ABORTED);
goodies.contentWindow.location = self.data.url('pages/test.html');
} else {
//dont do anything as there is no contentWindow associated with the httpChannel, liekly a google ad is loading or some ajax call or something, so this is not an error
}
}
return;
}
}
};
Services.obs.addObserver(httpRequestObserver, "http-on-modify-request", false);
//this function gets the contentWindow and other good stuff from loadContext of httpChannel
function loadContextGoodies(httpChannel) {
//httpChannel must be the subject of http-on-modify-request QI'ed to nsiHTTPChannel as is done on line 8 "httpChannel = subject.QueryInterface(Ci.nsIHttpChannel);"
//start loadContext stuff
var loadContext;
try {
var interfaceRequestor = httpChannel.notificationCallbacks.QueryInterface(Ci.nsIInterfaceRequestor);
//var DOMWindow = interfaceRequestor.getInterface(Components.interfaces.nsIDOMWindow); //not to be done anymore because: https://developer.mozilla.org/en-US/docs/Updating_extensions_for_Firefox_3.5#Getting_a_load_context_from_a_request //instead do the loadContext stuff below
try {
loadContext = interfaceRequestor.getInterface(Ci.nsILoadContext);
} catch (ex) {
try {
loadContext = subject.loadGroup.notificationCallbacks.getInterface(Ci.nsILoadContext);
} catch (ex2) {}
}
} catch (ex0) {}
if (!loadContext) {
//no load context so dont do anything although you can run this, which is your old code
//this probably means that its loading an ajax call or like a google ad thing
return null;
} else {
var contentWindow = loadContext.associatedWindow;
if (!contentWindow) {
//this channel does not have a window, its probably loading a resource
//this probably means that its loading an ajax call or like a google ad thing
return null;
} else {
var aDOMWindow = contentWindow.top.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShellTreeItem)
.rootTreeItem
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow);
var gBrowser = aDOMWindow.gBrowser;
var aTab = gBrowser._getTabForContentWindow(contentWindow.top); //this is the clickable tab xul element, the one found in the tab strip of the firefox window, aTab.linkedBrowser is same as browser var above //can stylize tab like aTab.style.backgroundColor = 'blue'; //can stylize the tab like aTab.style.fontColor = 'red';
var browser = aTab.linkedBrowser; //this is the browser within the tab //this is where the example in the previous section ends
return {
aDOMWindow: aDOMWindow,
gBrowser: gBrowser,
aTab: aTab,
browser: browser,
contentWindow: contentWindow
};
}
}
//end loadContext stuff
}
NOTE: Now try this first, I didn't test it yet, if you get a security error when it tries to redirect then create a chrome.manifest file and put it in the root directory. If it throws a security error than you definitely need a chrome.manifest file and that will without question fix it up. I'll test this myself later tonight when I get some time.
The chrome.manifest should look like this:
content kaboom-data ./resources/kaboom/data/ contentaccessible=yes
Then in the code way above change the redirect line from goodies.contentWindow.location = self.data.url('pages/test.html'); to goodies.contentWindow.location = 'chrome://kaboom-data/pages/test.html');.
see this addon here: https://addons.mozilla.org/en-US/firefox/addon/ghforkable/?src=search
in the chrome.manifest file we set the contentaccessible parameter to yes
you dont need sdk for this addon. its so simple, just ocpy paste that into a bootstrap skeleton as seen here:
Bootstrap With Some Features, Like chrome.manifest which you will need
Bootstrap Ultra Basic
if you want to really do a redirect of a page to your site, maybe you want to make a custom about page? if you would like ill throw togather a demo for you on making a custom about page. you can see a bit hard to understand demo here
posting my trials here so it can help all:
trail 1 failed - created chrome.manifest file with contents content kaboom-data resources/kaboom/data/ contentaccessible=yes
var myuri = Services.io.newURI('chrome://kaboom-data/content/pages/test.html', undefined, undefined);
httpChannel.redirectTo(myuri);
Error Thrown
Security Error: Content at http://digg.com/tools/diggthis/confirm? may
not load or link to
jar:file:///C:/Documents%20and%20Settings/SONY%20VAIO/Application%20Data/Mozilla/Firefox/Profiles/vr10qb8s.default/extensions/jid1-g4RtC8vdvPagpQ#jetpack.xpi!/resources/kaboom/data/pages/test.html.
trial 2 failed - created resource in bootstrap.js
alias.spec =
file:///C:/Documents%20and%20Settings/SONY%20VAIO/Application%20Data/Mozilla/Firefox/Profiles/vr10qb8s.default/extensions/jid1-g4RtC8vdvPagpQ#jetpack.xpi
alias updated to spec:
jar:file:///C:/Documents%20and%20Settings/SONY%20VAIO/Application%20Data/Mozilla/Firefox/Profiles/vr10qb8s.default/extensions/jid1-g4RtC8vdvPagpQ#jetpack.xpi!/
let resource = Services.io.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler);
let alias = Services.io.newFileURI(data.installPath);
Cu.reportError('alias.spec = ' + alias.spec);
if (!data.installPath.isDirectory()) {
alias = Services.io.newURI("jar:" + alias.spec + "!/", null, null);
Cu.reportError('alias updated to spec: ' + alias.spec);
}
resource.setSubstitution("kaboom_data", alias);
...
var myuri = Services.io.newURI('resource://kaboom_data/resources/kaboom/data/pages/test.html', undefined, undefined);
httpChannel.redirectTo(myuri);
Error Thrown
Security Error: Content at http://digg.com/tools/diggthis/confirm? may
not load or link to
jar:file:///C:/Documents%20and%20Settings/SONY%20VAIO/Application%20Data/Mozilla/Firefox/Profiles/vr10qb8s.default/extensions/jid1-g4RtC8vdvPagpQ#jetpack.xpi!/resources/kaboom/data/pages/test.html.
CONCLUSION
in both trials above it was the weirdest thing, it wouldnt show the resource or chrome path in the security error thrown but it would give the full jar path. Leading me to believe that this has something to do with redirectTo function.
The solution that did work was your solution of
var gBrowser = utils.getMostRecentBrowserWindow().gBrowser;
var domWin = httpChannel.notificationCallbacks.getInterface(Ci.nsIDOMWindow);
var browser = gBrowser.getBrowserForDocument(domWin.document);
//redirect
browser.loadURI(self.data.url('pages/test.html'));
however I changed this to use loadContext instead of this method because it is the recommended way. also gBrowser to getMostRecentBrowserWindow will fail if the url load is slow and in that time the user swithces to another tab or window
I also changed to use Services.jsm as you had imported Cu anyways. Using Services.jsm is super fast not even blink fast. Its just a pointer.
Im still working on trying to the redirectTo method working its really bothering me. The changes I made are to my local copy.
Have you considered turning your local HTML file into a data URL and loading that?

External link in WinJS, iframe or not, doesnt matter

I work on a Windows 8 app, and from a page that I use link hystory for running back and forward through the app, I also have 3 or 4 links to external websites(eg: facebook or my site). I tried to run them in iframe, or also to make them open in the default browser like simple links. Both method resulted in an error in base.js that says it can't handle my error (!?) I searched a lot before asking here. I watched msdn sample that works just fine, but if i copy what I need in my app results in the same error. I I use it from another page where I dont have forward history, it works, but i really need it on the front page. Any ideeas? Thank you very much.
LE:
This is my items.js code: ( for the items.html page )
(function () {
"use strict";
var appViewState = Windows.UI.ViewManagement.ApplicationViewState;
var ui = WinJS.UI;
ui.Pages.define("/pages/items/items.html", {
// This function is called whenever a user navigates to this page. It
// populates the page elements with the app's data.
ready: function (element, options) {
var listView = element.querySelector(".itemslist").winControl;
listView.itemDataSource = Data.groups.dataSource;
listView.itemTemplate = element.querySelector(".itemtemplate");
listView.oniteminvoked = this._itemInvoked.bind(this);
this._initializeLayout(listView, Windows.UI.ViewManagement.ApplicationView.value);
listView.element.focus();
WinJS.Utilities.query("a").listen("click", this.linkClickEventHandler, false);
},
// This function updates the page layout in response to viewState changes.
updateLayout: function (element, viewState, lastViewState) {
/// <param name="element" domElement="true" />
var listView = element.querySelector(".itemslist").winControl;
if (lastViewState !== viewState) {
if (lastViewState === appViewState.snapped || viewState === appViewState.snapped) {
var handler = function (e) {
listView.removeEventListener("contentanimating", handler, false);
e.preventDefault();
}
listView.addEventListener("contentanimating", handler, false);
var firstVisible = listView.indexOfFirstVisible;
this._initializeLayout(listView, viewState);
if (firstVisible >= 0 && listView.itemDataSource.list.length > 0) {
listView.indexOfFirstVisible = firstVisible;
}
}
}
},
linkClickEventHandler: function (eventInfo) {
eventInfo.preventDefault();
var link = eventInfo.target;
WinJS.Navigation.navigate(link.href);
},
// This function updates the ListView with new layouts
_initializeLayout: function (listView, viewState) {
/// <param name="listView" value="WinJS.UI.ListView.prototype" />
if (viewState === appViewState.snapped) {
listView.layout = new ui.ListLayout();
} else {
listView.layout = new ui.GridLayout();
}
},
_itemInvoked: function (args) {
var groupKey = Data.groups.getAt(args.detail.itemIndex).key;
WinJS.Navigation.navigate("/pages/split/split.html", { groupKey: groupKey });
}
});
})();
And from items.html I have different types of links: some of them links to other application pages, from where I can return with history buttons back/forward and some of them are links to external page. Simple link.These links crashes my app with the error that I mentioned below. If I erase the next line:
WinJS.Utilities.query("a").listen("click", this.linkClickEventHandler, false);
from my js script, external links works, but I dont have anymore history buttons in my others's app pages.
You are trying to use the navigation framework to navigate to an external URI. It's usually meant to be used within the application's local context and pages that can contain 'fragments' to load up into your main nav control.
I wouldn't hook anchor tags with your function call, instead in your linkClickEventHandler I would do the following to only hook your internal links
WinJS.Utilities.query(".nav").listen("click", linkClickEventHandler, false);
in turn your internal links would be
click me
This approach only hooks the navigation framework into your internal links. Another approach is to inspect the 'this.href' in your handler and if it contains http:// or https:// then call window.open instead

Resources