I'm learning javascript indexedDB and so, I followed an example from Javascript tutorial - https://www.javascripttutorial.net/web-apis/javascript-indexeddb/.
I followed the example to understand how it works. Below are my codes.
<html>
<head>
<title>Indexed Database</title>
</head>
<body>
<script>
if (!window.indexedDB) {
console.log("Your browser doesn't support IndexedDB");
}
const request = indexedDB.open("indexedDatabase", 3);
request.onerror = (event) => {
console.error ("Database error: ${event.target.errorCode}");
};
request.onsuccess = (event) => {
console.log("success: " + request);
insertContact(db, {
email: 'john.doe#outlook.com',
firstName: 'John',
lastName: 'Doe'
});
insertContact(db, {
email: 'jane.doe#gmail.com',
firstName: 'Jane',
lastName: 'Doe'
});
};
// create the Contacts object store and indexes
request.onupgradeneeded = (event) => {
let db = event.target.result;
//create the Contacts object store
//with auto-increment id
let store = db.createObjectStore('Contacts', {
autoIncrement: true
});
//create an index on the email property
let index = store.createIndex('email', 'email', {
unique: true
});
};
function insertContact(db, contact) {
//create a new transaction
const txn = db.transaction('Contacts','readwrite');
}
//get the Contacts object store
const store = txn.objectStore('Contacts');
let query = store.put(contact);
//handle success case
query.onsuccess = function (event) {
console.log(event);
};
//handle the error case
query.onerror = function (event) {
console.log(event.target.errorCode);
}
//close the database once the transaction completes
txn.oncomplete = function () {
db.close();
};
</script>
</body>
</html>
However, I encountered the following 2 errors which I have spent a lot of time to understand why.
Uncaught ReferenceError: txn is not defined
at indexedStorage.html:53:47
Uncaught ReferenceError: db is not defined
at request.onsuccess (indexedStorage.html:18:47)
Any help would be much appreciated.
I am following the example from the Javascript tutorial and were expected to insert the two records into the indexedDB.
You have two problems:
You placed the } ending the insertContact function too early. It needs to wrap the logic for inserting the contact
You close the database connection as soon as one transaction is done. This will make the second transaction fail - maybe not in this sample (because both transactions will be kicked off simultaneously) but it'll be a surprise if you ever do anything else with the code.
function insertContact(db, contact) {
//create a new transaction
const txn = db.transaction('Contacts','readwrite');
// Moved the } from here...
//get the Contacts object store
const store = txn.objectStore('Contacts');
let query = store.put(contact);
//handle success case
query.onsuccess = function (event) {
console.log(event);
};
//handle the error case
query.onerror = function (event) {
console.log(event.target.errorCode);
}
//close the database once the transaction completes
txn.oncomplete = function () {
// You probably don't want to do this:
// db.close();
};
} // ...to here
Related
I am struggling to understand why my function is failing when trying to loop through a snapshot from Firebase Realtime Database.
The function should read through each 'Topic', from within each 'Topic' there is an 'Articles' field which has approximately 10 articles associated with it. The function reads each article URL and scrapes the URL for the largest image on the article website.
It should then add a new field 'imageURL' to each 'Article'.
When deployed I receive the following:
TypeError: snapshot.forEach is not a function
scraper.js
exports.imageScraper = functions.database.ref("searchTrends/google")
.onUpdate((snapshot, context) => {
functions.logger.error(snapshot);
snapshot.forEach(function(trendSnapshot) {
// TrendSnapshot - Key is topic Num
// Value is topic details with list of articles
const topicNum = trendSnapshot.key;
trendSnapshot.forEach(function(innerChild) {
if (innerChild.key == "articles") {
innerChild.forEach(function(articleData) {
const articleNum = articleData.key;
const myUrl = articleData.child("url").val();
// console.log(myUrl);
const options = {
url: myUrl,
};
// console.log(options);
ogs(options, (error, results, response) => {
if (typeof results.ogImage === "undefined") {
console.log("no Image");
} else {
if (results.ogImage.url === undefined) {
return "done";
}
console.log(articleNum);
const DBRef = admin.database().ref("searchTrends/google/" +
topicNum + "/articles/" + articleNum);
DBRef.update({imageURL: results.ogImage.url});
}
});
});
return "done";
}
}).catch((error) => {
console.log("Transaction failed: ", error);
return null;
});
});
});
The error is telling you that snapshot does not have a method called forEach. It is a not a DataSnapshot object as you are expecting. It is a Change object, specifically Change<DataSnapshot> From the documentation:
For onWrite or onUpdate events, the first parameter is a Change object that contains two snapshots that represent the data state before and after the triggering event.
Also refer to the API documentation for onUpdate.
I'm trying to use indexedDB.
Some parts of my code works.
In the following example, the first function adds server in my DB, however in Chrome debug console there is an undefined message not related to any line. The server is already added though.
The second function puts records in an array, there is also an undefined message not related to any line.
If I do a console.log(servers); just before return servers; I can see the array content, however if I call the function somewhere else in my code, the returned object is undefined.
var dbName = 'myDBname',
dbServersStoreName = 'servers',
dbVersion = 1,
openDBforCreation = indexedDB.open(dbName, dbVersion);
openDBforCreation.onupgradeneeded = function(e) {
var db = e.target.result;
var objStore = db.createObjectStore(dbServersStoreName, { keyPath: "alias"
});
var index = objStore.createIndex("serversAlias", ["alias"]);
};
function addServerInDB(serverAlias,serverAddress,user,pwd){
var myDB = indexedDB.open(dbName, dbVersion);
myDB.onerror = function() {
var notification = document.querySelector('.mdl-js-snackbar');
notification.MaterialSnackbar.showSnackbar(
{message: 'Error while trying to access internal database'});
}
myDB.onsuccess = function(e) {
var db = e.target.result,
request = db.transaction([dbServersStoreName],
"readwrite").objectStore("servers")
.put({alias:''+serverAlias+'',
address:''+serverAddress+'', login:''+user+'',
passwd:''+pwd+''});
request.onsuccess = function(){
var notification = document.querySelector('.mdl-js-snackbar');
notification.MaterialSnackbar.showSnackbar(
{message: 'Server added'});
}
}
};
function listServersInDB(){
var myDB= indexedDB.open(dbName, dbVersion);
myDB.onerror = function() {
var notification = document.querySelector('.mdl-js-snackbar');
notification.MaterialSnackbar.showSnackbar(
{message: 'Error while trying to access internal database'});
}
myDB.onsuccess = function(e) {
var servers = new Array(),
db = e.target.result,
request = db.transaction(["servers"], "readwrite")
.objectStore("servers")
.openCursor();
request.onsuccess = function(e){
var cursor = e.target.result;
if(cursor){
servers.push(cursor.value);
cursor.continue();
}
return servers;
}
}
};
I do not understand where this undefined comes from and if that is why the listServersInDB() function doesn't work.
You need to learn more about how to write asynchronous Javascript. There are too many errors in your code to even begin reasoning about the problem.
Briefly, don't do this:
function open() {
var openDatabaseRequest = ...;
}
openDatabaseRequest.foo = ...;
Instead, do this:
function open() {
var openDatabaseRequest = ...;
openDatabaseRequest.foo = ...;
}
Next, you don't need to try and open the same database multiple times. Why are you calling indexedDB.open twice? You can open a database to both install it and to start using it immediately. All using the same connection.
Next, I'd advise you don't name the database open request as 'myDB'. This is misleading. This is an IDBRequest object, and more specifically, an IDBOpenRequest object. A request isn't a database.
Next, you cannot return the servers array from the request.onsuccess at the end. For one this returns to nowhere and might be source of undefined. Two this returns every single time the cursor is advanced, so it makes no sense at all to return return servers multiple times. Three is that this returns too early, because it cannot return until all servers enumerated. To properly return you need to wait until all servers listed. This means using an asynchronous code pattern. For example, here is how you would do it with a callback:
function listServers(db, callbackFunction) {
var servers = [];
var tx = db.transaction(...);
var store = tx.objectStore(...);
var request = store.openCursor();
request.onsuccess = function() {
var cursor = request.result;
if(cursor) {
servers.push(cursor.value);
cursor.continue();
}
};
tx.oncomplete = function() {
callbackFunction(servers);
};
return 'Requested servers to be loaded ... eventually callback will happen';
}
function connectAndList() {
var request = indexedDB.open(...);
request.onsuccess = function() {
var db = request.result;
listServers(db, onServersListed);
};
}
function onServersListed(servers) {
console.log('Loaded servers array from db:', servers);
}
When you call a function that does not return a value, it returns undefined. All functions in JavaScript return undefined unless you explicitly return something else.
When you call a function from the devtools console, and that function returns undefined, then the console prints out '-> undefined'. This is an ordinary aspect of using the console.
If you want to get a function that returns the list of servers as an array, well, you cannot. The only way to do that in a pretend sort of way, is to use an 'async' function, together with promises.
async function getServers() {
var db = await new Promise(resolve => {
var request = indexedDB.open(...);
request.onsuccess = () => resolve(request.result);
});
var servers = await new Promise(resolve => {
var tx = db.transaction(...);
var request = tx.objectStore(...).getAll();
request.onsuccess = () => resolve(request.result);
});
return servers;
}
One more edit, if you want to call this from the console, use await getServers();. If you do not use the top-level await in console, then you will get the typical return value of async function which is a Promise object. To turn a promise into its return value you must await it.
Clear and helpfull explanations, Thank you.
I open database multiple times beacause the first time is for checking if DB needs an upgrade and doing something if needed. I'll add 'db.close()' in each functions.
Then, I tried your exemple and the result is the same:
console.log('Loaded servers array from db:', servers); works
but return servers; Don't work.
And in console there is already an undefined without related line :
Screenshot
In my node.js app, reading data from MSSQL using tedious, I'm calling the below every 1 second:
Fetch the data from the server (fetchStock function) and save it in temporary array
Send the data saved in the temporary array to the client using the Server-Sent Events (SSE) API.
It looks the 1 second is not enough to recall the fetchStock function before the previous call is completely executed, so I get execution errors from time to time.
I increased it to 5 seconds, but still get the same issue every once in a while.
How can I use Promise().then to be sure the fetchStock function is not re-called before the previouse call be completely executed?
var Request = require('tedious').Request;
var Connection = require('tedious').Connection;
var config = {
userName: 'sa',
password: 'pswd',
server: 'xx.xxx.xx.xxx',
options: {
database: 'DB',
rowCollectionOnRequestCompletion: 'true',
rowCollectionOnDone: 'true'
},
};
var sql = new Connection(config);
var addElem = (obj, elem)=> [].push.call(obj, elem);
var result = {}, tmpCol = {}, tmpRow = {};
module.exports = {
displayStock: function (es) {
var dloop = setInterval(function() {
if(result.error !== null)
if (es) es.send(JSON.stringify(result), {event: 'rmSoH', id: (new Date()).toLocaleTimeString()});
if(result.error === null)
if (es) es.send('connection is closed');
}, 1000);
},
fetchStock: function () {
request = new Request("SELECT ItemCode, WhsCode, OnHand FROM OITW where OnHand > 0 and (WhsCode ='RM' or WhsCode ='FG');", function(err, rowCount, rows) {
if (err) {
result = {'error': err};
console.log((new Date()).toLocaleTimeString()+' err : '+err);
}
if(rows)
rows.forEach(function(row){
row.forEach(function(column){
var colName = column.metadata.colName;
var value = column.value;
addElem(tmpCol, {colName: value})
});
addElem(tmpRow,{'item': tmpCol[0].colName, 'Whs': tmpCol[1].colName, 'Qty': tmpCol[2].colName});
tmpCol = {};
});
result = tmpRow;
tmpRow={}
});
sql.execSql(request);
}
}
I think what you need is a simple variable to check if there's already running request not Promise.
var latch = false;
// It will be called only if the previous call is completed
var doFetchStock = () => sql.execSql(new Request("SQL", (err, rowCount, rows) => {
// Your logic dealing with result
// Initializes the latch
latch = false;
});
module.exports = {
fetchStock: function () {
// Check if the previous request is completed or not
if (!latch) {
// Sets the latch
latch = true;
// Fetches stock
doFetchStock();
}
}
};
Actually I've used this kind of pattern a lot to allow some behavior only once.
https://github.com/cettia/cettia-javascript-client/blob/1.0.0-Beta1/cettia.js#L397-L413
https://github.com/cettia/cettia-javascript-client/blob/1.0.0-Beta1/cettia.js#L775-L797
Since javascript is mono-threaded a simple code like this should be enough on client-side
function () {
if(currentPromise != null){ // define in a closure outside
currentPromise = [..] // call to server which return a promise
currentPromise.then(function(){
currentPromise = null;
});
}
}
I need to know about the usage of Meteor.call. I did a simple example as shown below. The problem is that it never goes to insertDetails(). Can you please check the below code and suggest me what to do so that I don't get the Match Failed error.
Client.JS
Meteor.methods
({
//this method doesn't cal when using meteor.cal
insertDetails : function(adData, callback)
{
console.log(">>>>>>>>>>>>>>>>>>> ******* insertDetails ");
checkFields(adData);
var fields =
{
userID: adData.UserID,
fname: adData.fname,
lname: adData.lname,
dob: adData.dob
};
return Client.insert(fields, callback);
}
});
// SERVER-SIDE HELPERS ************************************
var nonEmpty = Match.Where(function(x) {return !!x;});
var checkFields = function(adData)
{
console.log(">>>>>>>>>>>>>>>>>>> checkFields ");
check(adData.userID, nonEmpty);
check(adData.fname, nonEmpty);
};
Insert.js
if (Meteor.isClient)
{
Template.hello.events({
'submit #addnewuserdetails': function (e,t)
{
if (typeof console !== 'undefined')
console.log(">>>>>>>>>>>>>>>>>>> Add button in details ");
e.preventDefault();
saveClientDetails();
}
});
}
var saveClientDetails = function()
{
console.log(">>>>>>>>>>>>>>>>>>> saveClientDetails ");
var fields = {
//ownerId: Meteor.userId(),
UserID : $('#userid').value
,fname : $('#fname').value
,lname :$('#lname').value
,dob : $('#dob').value
};
console.log(">>>>>>>>>>>>>>>>>>> fields.UserID "+fields.UserID);
//here cal to above insertDetails()
Meteor.call("insertDetails", fields, function(err, result)
{
if (!err)
{
console.log(">>>>>>>>>>>>>>>>>>> saveClientDetails Success");
}
else
{
console.log(">>>>>>>>>>>>>>>>>>> saveClientDetails ERROR "+err.reason);
}
});
};
The Match Failed error points to invalid data being rejected by your check function. My guess is that the problem is with user id: when you call the method you use UserID parameter, but then you check lowercase userID. Try fixing that and see whether it works. Also, try commenting out check call and see whether the rest of code is running. Also, how do you verify that the method was not called? Notice that log should be visible in the server console.
I'm trying to create an application for Firefox OS which basically needs to store some data using IndexedDB.
The store() function is called when the user clicks on a submit button which results in the creation of the name and description variables an user submitted form.
However, I keep getting a Reference Error saying my db object is not defined. Any ideas why this is happening?
Here's my current code:-
function store () {
// create the transaction with 1st parameter is the list of stores and the second specifies
// a flag for the readwrite option
var transaction = db.transaction([ 'Apps' ], 'readwrite');
//Create the Object to be saved i.e. our App details
var value = {};
value.name = name;
value.desc = description;
// add the details to the store
var store = transaction.objectStore('Apps');
var request = store.add(value);
request.onsuccess = function (e) {
alert("Your App data has been saved");
};
request.onerror = function (e) {
alert("Error in saving the App data. Reason : " + e.value);
}
}
$(document).ready(function(){
// variable which will hold the database connection
var db;
if (window.indexedDB) {
console.log("IndexedDB is supported");
}
else {
alert("Indexed DB is not supported!");
}
// open the database
// 1st parameter : Database name. We are using the name 'Appsdb'
// 2nd parameter is the version of the database.
var request = indexedDB.open('Appsdb', 1);
request.onsuccess = function (e) {
// e.target.result has the connection to the database
db = e.target.result;
console.log(db);
console.log("DB Opened!");
}
request.onerror = function (e) {
console.log(e);
};
// this will fire when the version of the database changes
// We can only create Object stores in a versionchange transaction.
request.onupgradeneeded = function (e) {
// e.target.result holds the connection to database
db = e.target.result;
if (db.objectStoreNames.contains("Apps")) {
db.deleteObjectStore("Apps");
}
// create a store named 'Apps'
// 1st parameter is the store name
// 2nd parameter is the key field that we can specify here. Here we have opted for autoIncrement but it could be your
// own provided value also.
var objectStore = db.createObjectStore('Apps', { keyPath: 'id', autoIncrement: true });
console.log("Object Store has been created");
};
});
The problem is with the scope of the db variable. Currently you have the following line var db; declared inside of the $(document).ready function. Move its declaration to a more global scope i.e. outside of this function and the variable will be visible in the store() function too.
Hope this helps.
var value = {};
value.name = name;
value.desc = description;
assign value to the name and description.
name=formname.name.value
description=formname.description.value