Use existing sqlite database in cordova - sqlite

Hello I'm creating a application with ionic framework and cordova and want to use a sqlite database to store the data.
So how can I use a existing sqlite database for this purpose?
Where do I have to save the database so that cordova can find it and uses it if I compile for a platform?
Edit:
I get the following error if I try to open the db in the recommend way:
[Error] TypeError: undefined is not an object (evaluating 'n.sqlitePlugin.openDatabase')
openDB (ng-cordova.min.js, line 9)
(anonyme Funktion) (app.js, line 19)
(anonyme Funktion) (ionic.bundle.js, line 44243)
onPlatformReady (ionic.bundle.js, line 2396)
onWindowLoad (ionic.bundle.js, line 2375)
Here is the head section of my index.html:
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
<title></title>
<link href="lib/ionic/css/ionic.css" rel="stylesheet">
<link href="css/style.css" rel="stylesheet">
<!-- ionic/angularjs js -->
<script src="lib/ionic/js/ionic.bundle.js"></script>
<!-- cordova sqlite extenseion -->
<script src="js/ng-cordova.min.js"></script>
<!-- cordova script (this will be a 404 during development) -->
<script src="cordova.js"></script>
<!-- your app's js -->
<script src="js/app.js"></script>
<script src="js/controllers.js"></script>
</head>
My app.js looks like this:
//Database variable
var db = null;
angular.module('starter', ['ionic', 'starter.controllers', 'ngCordova'])
.run(function($ionicPlatform, $cordovaSQLite) {
$ionicPlatform.ready(function() {
// Hide the accessory bar by default (remove this to show the accessory bar above the keyboard
// for form inputs)
if (window.cordova && window.cordova.plugins.Keyboard) {
cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
}
if (window.StatusBar) {
// org.apache.cordova.statusbar required
StatusBar.styleDefault();
}
db = $cordovaSQLite.openDB({ name: "db.spareParts" });
var query = "SELECT cars.name FROM cars";
$cordovaSQLite.execute(db, query).then(function(res) {
if(res.rows.length > 0) {
console.log("SELECTED -> " + res.rows.item(0).name);
} else {
console.log("No results found");
}
}, function (err) {
console.error(err);
});
});
})
The database db.spareParts is stored in the root www directory and is a sqlite3 database.

i fixed this problem after a very long suffering :)
the prepopulated database MUST be in the "dataDirectory" of your application
at first you need to check if the db file exists in that directory, if not, you must copy it from application directory (i.e. /www folder)
check this piece of code
$ionicPlatform.ready(function() {
src = cordova.file.dataDirectory + 'data.sqlite';
window.resolveLocalFileSystemURL(src, function () {
db = sqlitePlugin.openDatabase('data.sqlite');
}, function () {
$scope.copyDb('data.sqlite');
});
$scope.copyDb = function (dbName) {
var sourceFileName = cordova.file.applicationDirectory + 'www/' + dbName;
var targetDirName = cordova.file.dataDirectory;
return Promise.all([
new Promise(function (resolve, reject) {
resolveLocalFileSystemURL(sourceFileName, resolve, reject);
}),
new Promise(function (resolve, reject) {
resolveLocalFileSystemURL(targetDirName, resolve, reject);
})
]).then(function (files) {
var sourceFile = files[0];
var targetDir = files[1];
return new Promise(function (resolve, reject) {
targetDir.getFile(dbName, {}, resolve, reject);
}).then(function () {
console.log("file already copied");
}).catch(function () {
console.log("file doesn't exist, copying it");
return new Promise(function (resolve, reject) {
sourceFile.copyTo(targetDir, dbName, resolve, reject);
}).then(function () {
console.log("database file copied");
db = sqlitePlugin.openDatabase('data.sqlite');
});
});
});
};
check this link
prepopulated db demo

So how can I use a existing sqlite database for this purpose?
You can use the existing sqlite database by using the built-in WebSQL. You do not need to install any plugin. Please see the link here for more details.
Note that there is limitation to the amount of storage using the standard approach.(I think it is about 5MB).
If you want to store more than the allowed standard storage than you need to use the sqlite plugin. Link here for details.
Where do I have to save the database so that cordova can find it and uses it if I compile for a platform?
You do not need to specify the location path for storing the database. The database open function call will automatically creates and stores it in a convenient location.
Edit 1:
What I understand from your edited question is that you require a pre-populated sqlite3 db to be used for your application.
Copying your db to the www directory will not work. You need to copy the pre-populated db to the default location where the app will use it for your application. The location is different for different platforms (iOS, Android, Windows). Also within a platform the location path for the db storage is different. E.g for Android Jelly bean the location path is different to the one in Android KitKat. Hence as far as I know you will not a find a complete solution on the Web for copying the db to the default location of the app.
This is a completely a different proposition.
You need to search stackoverflow for pre populated sqlite with cordova to get useful answers.
What I have answered is to create a new db in your application from scratch.
Also relating to your error.
You need to check that the deviceready event is fired before calling any cordova/phonegap related code.

if(window.cordova) {
// App syntax
db = $cordovaSQLite.openDB("myapp.db");
} else {
// Ionic serve syntax
db = window.openDatabase("myapp.db", "1.0", "My app", -1);
}

Related

Firebase Hosting Flutter Web App not clearing Cache of first deploy

We build a flutter web app and deployed it via firebase hosting. Unfortunately, we didn't configure any caching settings in our initial deploy.
Now we deployed a newer version of our website but people still get the old website shown form the first deploy. What we tried so far:
Adding version no. to our index.html:
<"script src="main.dart.js?version=1" type="application/javascript"></script>
Adding meta Data in our header in index.html:
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="0" />
In our firebase.json we added the following headers:
"headers": [
{
"source": "**",
"headers": [
{
"key": "Cache-Control",
"value": "max-age=10"
}
]
}
]
All of these attempts were without any luck. We think that the problem is that the newer version doesn't have those entries in the files. How can we force this to update to our newest version? We even consider opening a new firebase project if that might help.
Try this:
In your flutter app, in the index.html within the web folder, add a version number after the src="main.dart.js
So your new line will look like this:
<script src="main.dart.js?version=1" type="application/javascript"></script>
Then increment the version number to 2, etc before you do each build.
update june 2021: add this line in index.html as the last script in the body
I see Flutter now include this script by default in index.html if project is created recently which has serviceWorkerVersion element which updates the version when compiling:
<script>
var serviceWorkerVersion = null;
var scriptLoaded = false;
function loadMainDartJs() {
if (scriptLoaded) {
return;
}
scriptLoaded = true;
var scriptTag = document.createElement('script');
scriptTag.src = 'main.dart.js';
scriptTag.type = 'application/javascript';
document.body.append(scriptTag);
}
if ('serviceWorker' in navigator) {
// Service workers are supported. Use them.
window.addEventListener('load', function () {
// Wait for registration to finish before dropping the <script> tag.
// Otherwise, the browser will load the script multiple times,
// potentially different versions.
var serviceWorkerUrl = 'flutter_service_worker.js?v=' + serviceWorkerVersion;
navigator.serviceWorker.register(serviceWorkerUrl)
.then((reg) => {
function waitForActivation(serviceWorker) {
serviceWorker.addEventListener('statechange', () => {
if (serviceWorker.state == 'activated') {
console.log('Installed new service worker.');
loadMainDartJs();
}
});
}
if (!reg.active && (reg.installing || reg.waiting)) {
// No active web worker and we have installed or are installing
// one for the first time. Simply wait for it to activate.
waitForActivation(reg.installing ?? reg.waiting);
} else if (!reg.active.scriptURL.endsWith(serviceWorkerVersion)) {
// When the app updates the serviceWorkerVersion changes, so we
// need to ask the service worker to update.
console.log('New service worker available.');
reg.update();
waitForActivation(reg.installing);
} else {
// Existing service worker is still good.
console.log('Loading app from service worker.');
loadMainDartJs();
}
});
// If service worker doesn't succeed in a reasonable amount of time,
// fallback to plaint <script> tag.
setTimeout(() => {
if (!scriptLoaded) {
console.warn(
'Failed to load app from service worker. Falling back to plain <script> tag.',
);
loadMainDartJs();
}
}, 4000);
});
} else {
// Service workers not supported. Just drop the <script> tag.
loadMainDartJs();
}
If you do not want to use the server worker caching, you can provide --pwa-strategy=none. Otherwise, the app will update after the new version has been downloaded and the page revisited.
Example:
flutter build web --release --pwa-strategy=none
It will Generate a service worker with no body. This is useful for local testing or in cases where the service worker caching functionality is not desirable
By default, it is set to offline-first: Attempt to cache the application shell eagerly and then lazily cache all subsequent assets as they are loaded. When making a network request for an asset, the offline cache wil be preferred.
Reference:
https://github.com/flutter/flutter/issues/71333#issuecomment-736732314
flutter help build web
When you build flutter web then in directory build/web generated a version.json file.
just increase build_number and version in this file.

Nativescript SQLite null values after execSQL queries

i have the following problem. I designed my litesql database over 'DB Browser for SQLite' and I'm stuck as soon as a query gets executed. The functions I am exporting are getting imported and used in nativescript-vue.
Webpack also copies the database with *.sqlite ending to the device. The android version I use is 9.
The way I initialize my db is;
var Sqlite = require("nativescript-sqlite");
var db;
export function init() {
if (!Sqlite.exists("test.sqlite")) {
Sqlite.copyDatabase("test.sqlite");
}
new Sqlite("test.sqlite", function(err, dbConnection) {
if (err) {
console.log(err);
return;
}
db = dbConnection;
console.log("connection successful")
});
}
After running the function console shows 'connection successful'. The database is placed in the root of the app folder. That way it should pull the database?
Besides I got another question. How could I hide the database in the production?
So the way I execute the query is:
export function xxxx(**parameter**) {
db.execSQL(
"select random_id from random_table where some_id = ?",
**parameter**,
function(err, result) {
console.log("result 1: " + result + " err: " + err);
}
);
}
The output is:
JS: 'result 1: null err: null'
I'm not even sure if it opens the database in the right way?
If you just want to export DB file from Android / iOS, you may use nativescript-share-file plugin and pass the right path.
Android
const filePath = application.android.context
.getDatabasePath("your-db-name.sqlite")
.getAbsolutePath();
new ShareFile().open({
path: filePath,
});
For iOS the path will be different,
iOS
const filePath = knownFolders.documents().getFile("your-db-name.sqlite").path;
new ShareFile().open({
path: filePath,
});

Ionic and SQLite wait until database info is loaded

I have a project on Ionic where I need to render some information of the database in the home page, something like a TODO program.
I already have some information on the database and I'm trying to render the list of items but I have the next problem:
First the home page is loaded without any result
Then the data from the database is loaded and printed on the screen
The problem is I want the view to wait until the database info is loaded until showing anything, I'm wondering if I can use some kind of loading icon.
I've followed the answer here: Open database before main controller is called in Ionic and SQlite
I have the database initialization working but as I've said, the data is loaded after the view is rendered.
I've tried using $ionicLoading but I didn't get any good result
This is my view:
.controller('homeCtrl', function ($scope, $state, $cordovaSQLite, DB) {
$scope.$on('$ionicView.enter', function() {
tasks = []
var query = "SELECT * FROM task;";
$cordovaSQLite.execute(DB.db, query, []).then(function(results) {
if(results.rows.length > 0) {
for (i=0; i<results.rows.length; i++){
console.log("SELECTED -> " + results.rows.item(0).title);
$scope.tasks.push(results.rows.item(i))
}
} else {
console.log("No results found");
}
}, function (err) {
$scope.tasks = [];
console.error(err);
});
$scope.tasks = tasks;
});
})
This is a video example of the issue I'm having right now:
https://youtu.be/H2fUYQuV3xg
Finally I found a solution following the advice of using resolve in my routes.
.state('home', {
url: '/',
templateUrl: 'templates/home.html',
controller: 'homeCtrl',
resolve: {
tasks: function(DB) {
return DB.getTasks();
});
}
}
})
I have a factory called DB where I have some functions to retrieve data from the database. On this example I load the tasks before entering on the URL using DB.getTasks()
To load the variable tasks resolved on the route I have to add the variable name on the function like this:
app.controller('homeCtrl', function (tasks) {
$scope.tasks = tasks;
})

Using Imagemagick with a file from a mongo collection

I'm building a simple database for small pixel-art files. The images are saved directly to the database:
Template.pixUpload.events({
'change .myPixInput': function(event, template) {
event.preventDefault();
var file = event.target.files[0]; //assuming 1 file only
if (!file) return;
var reader = new FileReader();
reader.onload = function(event){
MyPix.insert({
binary: reader.result,
createdAt: new Date
});
}
reader.readAsDataURL(file);
}
})
The idea is to be able to modify the images on their way back to the browser, scale them on the fly (if things don't get too slow). So I'm trying to read the image from the db, and scale it with Imagemagick, before displaying it. It doesn't work – and I can't find anything helpful I would be able to understand:
Template.pixList.helpers({
'thumbnail': function() {
var bin = this.binary;
var thumb = new FileReader();
Imagemagick.convert(['bin', '-filter', 'point', '64x64', 'thumb']);
return thumb;
}
})
im using GM Package Right now, take a look at full repo here
First Install All FSCollecton Packages.
GridFS (Because you say you want to store the file inside MongodB).
Graphicsmagick meteor add cvs:graphicsmagick
Second Declare collections on /lib/collection.js
kaiStackExample = new FS.Collection("kaiStackExample ", {
stores: [new FS.Store.GridFS("kaiStackExample ",{
beforeWrite:function(fileObj){
return {
extension: 'png',
type: 'image/png'
};
},
transformWrite:function(fileObj, readStream, writeStream){
// Here you can choose, for example 10x10 (thumbnail), etc.
gm(readStream).resize(600).stream('PNG').pipe(writeStream);
}
})]
});
and our basic Subscribe, on the same /lib/collection.js
if(Meteor.isClient) {
Meteor.subscribe('kaiStackExample');
}
Ok, at this point we have the GridFS and GM, to verify both
server console.
=> GraphicsMagick found
Client console.
kaiStackExample.find().fetch();
should return [];
Third SECURITY
kaiStackExample.allow({
insert:function(userId,doc){
//here we can do stuff like, only permit user with accounts insert on the collection
if(!Meteor.userId()){
return false;
}else{
return true
},
update:function(userId,doc){
if(userId === doc.authorId{
return true;
}else{
return false; // if user don't own the document can update it.
}
}
});
Four Template and Events
HTML markup
<template name="exampleKai">
Select File
<input type="file" id="fileExampleKai">
<button type="submit" id="buttonExampleKai">Click to upload</button>
</template>
Js Code
Template.exampleKai.events({
'click #buttonExampleKai':function(event,template){
event.preventDefault();
var file = $('#fileExampleKai').get(0).files[0];
fsFile = new FS.File(file);
fsFile.metadata = {
coolMetadata:"Yes You can add some metadata too"
}
if(file === undefined){
alert("Please upload a file to continue")
}else{
kaiStackExample.insert(fsFile,function(err,succes){
if(err){
console.log(err.reason);
}else{
console.log("ok we insert yeaaa")
}
});
}
}
});
Like i say this works for me, and i think its your best option for editing size,type,etc Take a look hope it help you
Even though Meteor is very good at keeping the client and server environments in sync, that doesn't mean it can do everything on the client that it can do on the server and vice versa. Converting images using ImageMagick would be one example of what you can only do on the server.
If I were to build something like this, I'd look into using CollectionFS for syncing files. They also have a section in the README that describes how to manipulate images before saving them, which seems to be just what you're after.

dynamically add files to the public folder and getting the refreshed results in meteor

I'm playing around with the idea of using Meteor as a backend for creating a static html generator (with an admin UI).
I want that, when triggered, a new file will be created on the public folder and instantly reviewed by the author on an iframe embed in the html.
The file gets created but two side effects happen:
The server is restarted.
The file is cached - so the user can't see that the change took place.
Any ideas?
if (Meteor.is_client) {
Template.hello.events = {
'click input' : function () {
Meteor.call('makeFile', 'filename.html', function(error, result){
alert(result);
});
//window.location = '/filename.txt';
// template data, if any, is available in 'this'
if (typeof console !== 'undefined')
console.log("You pressed the button");
}
};
}
if (Meteor.is_server) {
var fs = __meteor_bootstrap__.require('fs');
Meteor.startup(function () {
// code to run on server at startup
});
Meteor.methods({
'makeFile': function(fileName) {
/*
fs.unlink("public/"+fileName, function (err) {
if (err) throw err;
console.log('successfully deleted ');
});
*/
fs.writeFile("public/"+fileName, "<html><body><h1>test</h1></body></html>", function(err) {
if(err) {
console.log(err);
} else {
console.log("The file was saved! "+ fileName);
}
});
return fileName;
}
});
}
I think the meteor server restarts whenever you change any file in a meteor subdirectory. So to prevent this don't write your files within the meteor app directory, make a created_files directory one level up or something. This is a good idea anyway, to separate the generated files from the code that's generating them.
Easiest way to show the content in the admin interface is probably to write it to the database (as opposed to trying to watch for directory changes). No sense re-inventing the wheel, since Meteor is designed to watch for db updates.
Create a collection (at the top, so it's on client & server):
GeneratedFiles = new Meteor.Collection("generated_files");
Then assign the file contents to a variable before writing them and in your fs.writeFile callback:
if(err) {
console.log(err);
} else {
GeneratedFiles.insert({name: fileName, contents: fileContents});
console.log("The file was saved! "+ fileName);
}
And then auto-publish the file contents to template in the client code (not sure why you want to use an iframe, seems like a div would work fine. But either way would probably work).
Hope that helps. And good luck, I think meteor is a great framework for this kind of thing. Most of the admin interfaces I use are still entirely synchronous and just annoying to work with.

Resources