I am trying to update an entry in my simple to do app with indexedDB, however I am getting Failed to execute 'put' on 'IDBObjectStore': The transaction has finished.
I can't seem to figure out why it won't finish the transaction, I tried the debugger and it stops at this line: var updateNameRequest = tasksStore.put( requestForItem.result.name, Number(requestForItem.result.id)) Please see the snippet I included below. For additional context creating, reading, and deleting work just fine it's just updating data that I'm having trouble with
I also tried to implement the openCursor technique which I got from Mozilla which I commented out since it also doesn't work (I get the same behavior) Check out my repo I know it's still very messy :(
const request = window.indexedDB.open("toDoList", 2);
var db;
request.onsuccess = function (event) {
console.log("check out some data about our opened db: ", request.result);
db = event.target.result; // result of opening the indexedDB instance "toDoList"
getTasks(); //just a function to retrieve data
};
$(document).on("click", ".editBtn", function () {
var transaction = db.transaction("tasks", "readwrite");
var tasksStore = transaction.objectStore("tasks");
console.log(tasksStore);
let taskId = $(this).attr("idNo");
var requestForItem = tasksStore.get(Number(taskId));
requestForItem.onsuccess = function () {
// console.log(requestForItem.result)
var oldData = requestForItem.result;
// prepopulate the input
$(".editInput").val(requestForItem.result.name);
$(".saveBtn").click(function () {
requestForItem.result.name = $(".editInput").val().trim()
console.log( requestForItem.result)
var updateNameRequest = tasksStore.put( requestForItem.result.name, Number(requestForItem.result.id))
console.log("-------------", updateNameRequest.transaction) // doesn't get to this line
updateNameRequest.onerror = function() {
console.log("something went wrong")
console.log(updateNameRequest.error)
};
updateNameRequest.onsuccess = function() {
console.log("here")
$(".editInput").val("")
getTasks();
};
});
};
Indexed DB transactions auto-commit when all requests have been completed and no further requests were made before control returns to the event loop. Put another way - you can make new requests in the success or error callback from a previous request, but not in other asynchronous callbacks such as event handlers.
You need to start a new transaction within the click handler, because any previous transaction will have autocommitted.
I’m brand new to programming and I’m currently working on a MySQL database. I’m using Visual Studio Code for all of my JavaScript, HTML, and CSS files.
I have a JavaScript server file that is giving me issues. Our professor gave us his code for the server JavaScript file (which is posted below), his client JavaScript file (which is named contacts.js), and his HTML file.
He told us to open the server JavaScript file, open a terminal and type: node contacts.js. However, doing this gives me error messages that say that the document is not defined.
Occasionally, I'll even get "module not found" errors.
We just did a similar project last week and the terminal worked just fine with a similar node.js command, but I’m running into issues now and don’t know what to do. Hours on Google haven’t helped at all and my professor can’t be contacted for the entire week.
I’m not sure how to get beyond this “document not defined” error. Any help would be appreciated.
Below is the server JavaScript file:
// The following statements are for database connection and queries
var mysql = require('mysql'); // use the msql libraries. Must use 'npm install msyql --save' before using
var bodyParser = require('body-parser'); // use the body-parser library for JSON use. Must 'npm install body-parser --save'
// Set up the SQL connection to the MYSQL database. This will all need to match what you set up in your DB
var connection = mysql.createConnection({
host:'localhost',
user: 'mike',
password: '********',
database: 'contacts'
});
// do the actual connecting by calling the connect method and log the result
connection.connect();
console.log("After connection to DB established in server, setting up web server");
//The following are for web server setup - we are using the express library that makes this all pretty easy
const express = require('express'); // use express library. must use 'npm install express --save'
const cors = require('cors'); // use cors library. must use 'npm install cors --save'
const app = express(); // get the express application object
const path = require('path'); // use the path library for managing paths. must use 'npm install path --save'
const port = 3000; // constant for the port we're using.
// set up the use of JSON url-encoding. Allows us to put all the arguments in the url
app.use(bodyParser.urlencoded({extended: false}));
app.use(bodyParser.json());
// We want to also serve static pages. This command sets that up. In my case, I created a subdirectory called 'public'
// and put the main html page (contacts.html), the javascript client file (contacts.js) and the CSS file (contacts.css)
// in that directory, and therefore I can get everything I need by just goint to (localhost:3000) and it all just works
app.use(express.static('public'));
// This is our main save handler (express calls these 'middleware'). The request coming from the client is a post
// and all the parameters/values are in the query object in the request object (req).
// All the field names here must match the names in the form (name='blah') which we use when we craft the request
// in the javascript saveContact().
// NOTE: we have to use the cors() method to make this all work. Look up cors (Cross-Origin Resource Sharing) to learn about it
app.post('/save', cors(), function (req, res) {
console.log("trying to save contact (post)"); // Log what we're doing
console.log(req); // log the actual request
var curId = req.query.Id; // Get the Id from the query object
var firstName = req.query.fname; // get the fname from the query object
var lastName = req.query.lname; // get the lname from the query object
var age = req.query.age; // etc. etc. etc.
var phone = req.query.phone;
var email = req.query.email;
// We can use the same handler for both cases of saving information:
// 1)we INSERT the new contact in the DB - the Id is 0 in this case
// 2)we UPDATE an existing contact in the DB - the Id is the correct Id for the contact we're updating
// Here we're crafting the appropriate SQL statements using the values above - either INSERT or UPDATE
if (curId > 0) {
var sql = `UPDATE contacts SET fname = '${firstName}', lname = '${lastName}', age = ${age}, phone = '${phone}', email = '${email}' WHERE Id = ${curId}`;
} else {
var sql = `INSERT INTO contacts (fname, lname, age, phone, email) VALUES ('${firstName}', '${lastName}', '${age}', '${phone}', '${email}')`;
}
// Here we're creating the query and the callback function for when we get a response from the DB asynchronously
// This same method executes the SQL call to the database connection we established earlier (above)
connection.query(sql, function (err, result) {
console.log("Trying to save contact into DB"); // log what we're doing
if (err) throw err; // If we get an error, send the error along
console.log(result.affectedRows + " record(s) saved");
res.status(201).send(result); // set the status code (201 = successful add) and send it
console.log(`result of post is: ${result}`); // log the result
console.log(result);
});
});
// This handler is for deleting a user given a valid Id.
// NOTE: we have to use the cors() method to make this all work. Look up cors (Cross-Origin Resource Sharing) to learn about it
app.post('/delete', cors(), function (req, res) {
console.log("trying to delete contact (post)"); // log what we're doing
console.log(req); // log the actual request we received
var curId = req.query.Id; // get the Id from the query object
console.log(curId); // log the Id
// As long as we have a valid Id (in variable curId), we craft the sql statement and execute the query
// all the SQL commands are asynchronous so we provide a callback function
if (curId > 0) {
var sql = `DELETE FROM contacts WHERE Id = ${curId}`; // This is the right SQL statement
connection.query(sql, function (err, result) {
console.log("Trying to delete contact from DB"); // log what we're trying to do
if (err) throw err; // if we get an error, pass it along to the client
console.log(result.affectedRows + " record(s) deleted");
res.status(200).send(result); // otherwise set the status to success (200) and send the result to the client
console.log(`result of post is:`); // log the result
console.log(result);
});
}
});
// THis is our static GET Handler if you just open a browser and type in 'http://localhost:3000'. the '/' means root
// and so this our default page (often called 'index.html' but in this case it's our 'contacts.html')
// Simply send the contacts.html page by getting the default path (wherever we have this javascript file)
app.get('/', function (req, res) {
console.log(req.params);
res.sendFile(path.join(__dirname + '\\contacts.html'));
});
// This is our handler for getting the full list of contacts
// NOTE: we have to use the cors() method to make this all work. Look up cors (Cross-Origin Resource Sharing) to learn about it
app.get('/list', cors(), function (req, res) {
console.log(`inside list GET function, req object is ${req}`);
console.log(req);
// Craft the simple select statement that just gets everything in the contacts table
var sql = `SELECT * FROM contacts`;
//Create the query and execute it, sending the appropriate result back to the client
connection.query(sql, function (err, result) {
console.log(`Trying to get list from DB - result is ${result}`);
console.log(`Inside get list - result first row is ${result[0]}`);
if (err) throw err; // if we get an error, pass it along to the client
res.send(result); // simply send the result of the query to the client.
console.log(`result of GET to list is: ${result}`);
});
});
// This is what actually starts the express server, listening on the port constant we defined at the beginning
// of the file (in this case I'm using 3000) and logging what we're doing.
app.listen(port, () => console.log(`Example app listening on port ${port}!`));
Below is the client JavaScript file:
// This JavaScript file is in support of the contacts application.
// Users can see and manage all their contacts, where a contact is [Id, firstname, lastname, age, phone, email]
// There are functions to manage the http interactions with the server backend
// and to manage the screen/user experience
// This is a globally available array of contacts we get back from the server. Set it initially to an empty array
document.contactList = [];
// This is the function queries the server (using an HTTP GET) to get the list of contacts
// We save the contacts to a globable variable in the document (contactList) and we fill
// both the table at the bottom of the document and a drop-down list used for management
// both of those functionality are function calls ('fillContactTable()' and 'fillContactSelect()')
function getContacts() {
console.log(`Getting contact list from server`); // log what we're doing
var xhttp = new XMLHttpRequest(); // create a variable for HTTP protocol
xhttp.onreadystatechange = function() { // callback function for when a response occurs
console.log(this.responseText); // log the response
// readyState is the XMLHttpRequest state that means we're done. Status is what is returned from the server
// a status code anywhere in the 200's is success. SO if we're done and get a success return code, then we're good!
if (this.readyState == 4 && (this.status >= 200 && this.status < 300)) {
console.log(`Got the contact list successfully`); // Log that we're good
document.contactList = this.responseText; // The response is the actual list of contacts.Set to globabl var in document
fillContactTable(); // Fill the table
fillContactSelect(); // Fill the drop down list (select element)
} else {
console.log(`failed to get contact list`); // Log failure if that's what we got
}
}
// We've set the callback function that handles the result. This is the actual setting up the http request (the open method)
// and the actual sending of the http request (send method)
xhttp.open("GET", "http://localhost:3000/list", true);
xhttp.send();
}
// Given an Id of a contact, get the rest of the contact information and return it
// The pLocal parater is in case we want to get the contact information from the server instead of getting it from
// the global variable (document.contactList). Default is to be local.
function getContactById(pId, pLocal = true) {
console.log(`Getting contact by ID = ${pId}`); // log what we're doing
if (pLocal) { // If we're local, get the data from document.contactList
var contactsJSON = JSON.parse(document.contactList); // parse the contactList into JSON format - easier to deal with
// Loop through all the contacts in the JSON formated list of contacts to look for the one we want (by Id)
for (loopIndex = 0; loopIndex < contactsJSON.length; loopIndex++) {
if (contactsJSON[loopIndex].Id == pId) { // if Id's match, we're good but log what we found
console.log(`Found contact in getContactById. Index = ${loopIndex}`);
console.log(contactsJSON[loopIndex]);
return contactsJSON[loopIndex]; // Return the found contact
}
}
console.log(`Did not find the contact in getContactById`); // log the fact that we didn't find it and return null
return null;
// For now, if we're not local just return null. Will add the code to get the data from the server later
} else {
return null;
}
}
// Simple function that just clears the form that we use for showing, creating new, and updating contacts
function clearEditForm() {
console.log("clearing the contact form"); // Log what we're doing
// Set all the values to empty (or 0 for the Id - that has to be a number)
document.getElementById('contact_id').value = 0;
document.getElementById('contact_fname').value = "";
document.getElementById('contact_lname').value = "";
document.getElementById('contact_age').value = "";
document.getElementById('contact_phone').value = "";
document.getElementById('contact_email').value = "";
// Now control the user experience. Hide the ID fields and change the name of the button to "Insert"
document.getElementById('contact_id').hidden = true;
document.getElementById('contact_id_label').hidden = true;
document.getElementById('save_button').innerHTML = "Insert Contact";
document.getElementById('save_button').name = "Insert Contact";
}
// Main function that saves the contact form. We have two cases to deal with:
// 1) We're inserting a new contact. In that case, the Id (curId below) will be 0
// 2) We're updating an exesting contact. In that case, the Id will NOT be 0
// If the ID is not a number >= 0, we have a problem so we don't do anything
function saveContact() {
console.log("Attempting to save contact"); // Log what we're doing
// Get all the values from the elements in the form by name.
var curId = document.getElementById('contact_id').value;
var curFName = document.getElementById('contact_fname').value;
var curLName = document.getElementById('contact_lname').value;
var curAge = document.getElementById('contact_age').value;
var curPhone = document.getElementById('contact_phone').value;
var curEmail = document.getElementById('contact_email').value;
console.log(`Trying to save contact in saveContact. Id = ${curId}`);
// As long as we have a valid Id (number at least 0) we'll make the http request (a POST)
if (curId >= 0) {
var xhttp = new XMLHttpRequest(); // Create a new HTTP object and put it in xHTTP variable
// As in all of our interactions implementing http, we supply a callback function for when we actually get a response
// Remember, all http request/responses should be asynchronous, and so we have to use callbacks
xhttp.onreadystatechange = function() {
console.log(this.responseText); // log what's happening
// If readyState shows we're done (value == 4) and status code is in the 200's we got a success response
if (this.readyState == 4 && (this.status >= 200 && this.status < 300)) {
console.log(`saved the contact successfully`); // Log our success
getContacts(); // Re-get our contact list since it has changed
} else {
console.log(`failed to save contact `); // Log our failure response
console.log(this.status); // log the actual status code
console.log(this.responseText); // log the actual response text
}
}
// Here we're crafting the http POST request with all the parameters urlencoded. Look up url encoding to understand it
// As usual, the open method is used to set up the call, and the send method actually sends the request
xhttp.open("POST", `http://localhost:3000/save?Id=${curId}&fname=${curFName}&lname=${curLName}&age=${curAge}&phone=${curPhone}&email=${curEmail}`, true);
xhttp.send();
}
}
// Function to delete a contact by creating the right server http request (a POST)
// We'll pass the Id of the contact we want to delete in the url (url-encoded)
// We'll get the name of the contact to be deleted and prompt the user to verify that they want to really delete the contact
// look up the window method 'confirm' to understand how that works
function deleteContact() {
console.log("Attempting to delete contact"); // Log what we're doing
var contactList = document.getElementById('contacts_list'); // get the drop-down select element in the form
var curId = contactList.value; // get the value of the form, which will be an Id of the contact to be deleted
var curIndex = contactList.selectedIndex; // We need the index of the option chosen to get the name for prompting the user
var curName = contactList.options[curIndex].text; // get the name from the option list based on the index
console.log(`Trying to verify delete. curid = ${curId}, curIndex = ${curIndex}, and curName = ${curName}`);
// Prompt the user to confirm using the window.confirm method. If they say ok, confirm returns true
// if they say cancel, confirm returns false. We're checking for the false, thus the not (!) at the beginning of the condition
if (!confirm(`Are you sure you want to delete contact: ${curName}?`)) {
return; // If we're here they said cancel, so just return out of here
}
console.log(`Trying to delete contact in fillEditForm. Id = ${curId}`);
if (curId.length > 0) { // Make sure we have a good Id
var xhttp = new XMLHttpRequest(); // create the http object
// Here's our callback for the asynchronous return. As long as we get a good status code, we update the form appropriately
xhttp.onreadystatechange = function() {
console.log(this.responseText); // log the actual response
// readyState 4 means we're done, and status in the 200's means success, so re-get the contact list from the server
if (this.readyState == 4 && (this.status >= 200 && this.status < 300)) {
console.log(`deleted the contact successfully`);
clearEditForm(); // clear the form since we deleted the contact
getContacts(); // get the contacts from the server
} else {
console.log(`failed to delete contact list`);
}
}
// Create the actual request and send it.
xhttp.open("POST", `http://localhost:3000/delete?Id=${curId}`, true);
xhttp.send();
}
}
// Simple function to clear the table element. We delete all the rows backwards. Make the function generic by allowing
// a parameter (pTable) which is the name of the table to be reset if there is more than one on the form
function tableDeleteRows(pTable = "") {
var curTable;
if (pTable.length == 0) {
curTable = document.getElementById('contacts_table');
} else {
curTable = document.getElementById(pTable);
}
// We start at the end of the rows (rows[length-1]), deleting backwards until we delete all of them
for (i = curTable.rows.length - 1; i >= 0; i--) {
curTable.deleteRow(i);
}
}
// Simple function to clear a drop-down select element. We delete all the rows backwards. Make the function generic by allowing
// a parameter (pSelect) which is the name of the select element to be reset if there is more than one on the form
function selectDeleteOptions(pSelect = "") {
var curSelect;
if (pSelect.length == 0) {
curSelect = document.getElementById('contacts_list');
} else {
curSelect = document.getElementById(pSelect);
}
// Go backward from the end of the list of options in the select, removing them until we remove all of them
for (i = curSelect.length - 1; i >= 0; i--) {
curSelect.remove(i);
}
}
// Assuming we have a contact chosen in the drop-down select element, fill the edit form with all the values for that contact
function fillEditForm() {
console.log("filling the contact form"); // Log what we're doing
var contactList = document.getElementById('contacts_list'); // get the drop-down list
var curId = contactList.value; // the selected element Id is the value of the list
console.log(`Trying to find contact in fillEditForm. Id = ${curId}`);
var curContact = getContactById(curId); // Get the whole contact by calling the function
console.log(curContact); // log the contact we're using to fill the form
// As long as we have a good contact, we fill the form
if (curContact != null) {
document.getElementById('contact_id').value = curContact['Id'];
document.getElementById('contact_fname').value = curContact['fname'];
document.getElementById('contact_lname').value = curContact['lname'];
document.getElementById('contact_age').value = curContact['age'];
document.getElementById('contact_phone').value = curContact['phone'];
document.getElementById('contact_email').value = curContact['email'];
}
// after we fill the form, we set elements appropriate to things like update and delete instead of add new
document.getElementById('contact_id').hidden = false;
document.getElementById('contact_id').disabled = true;
document.getElementById('contact_id_label').hidden = false;
document.getElementById('save_button').innerHTML = "Update Contact";
document.getElementById('save_button').name = "Update Contact";
}
// This function fills the table at the bottom of the document with all the contacts and all the information
function fillContactTable() {
console.log("Filling the contacts table in the form"); // Log what we're doing
tableDeleteRows("contacts_table"); // Reset the table
// if we don't have anything in the global contact list - forget it and return
if (document.contactList.length == 0) {
console.log("the contact list/array is empty!");
return;
}
// We have contacts in the global array, so first parse the array into JSON and process it
var contactsJSON = JSON.parse(document.contactList);
var properties = ['Id', 'fname', 'lname', 'age', 'phone', 'email']; // we need the property names
var tr, curRow; // variables for table properties
var contactTable = document.getElementById("contacts_table"); // get the table element
// cycle through the rows in the contacts array
for (var rowIndex = 0; rowIndex < contactsJSON.length; rowIndex++) {
console.log(`Creating table rows, rowindex is ${rowIndex}`);
tr = document.createElement('tr'); //create a new table row element
curRow = contactsJSON[rowIndex]; // get the current row from the array
console.log(curRow); // log the data in the current row
// Cycle through the columns - defined in the property array above and add column elements to the row in the table
for (var i = 0; i < properties.length; i++) {
console.log(`Creating table columns for row ${i}, property is ${properties[i]} value is ${curRow[properties[i]]}`);
var td = document.createElement('td'); // create a data element for the column
td.appendChild(document.createTextNode(curRow[properties[i]])); //Append the property data to the new data element
tr.appendChild(td); // append the new data element to the row element
}
contactTable.appendChild(tr); // append the row element to the table element
}
console.log("Finished procesing contacts list");
}
// Fill the drop-down select. First reset the select (removing all options), then recreate it
function fillContactSelect() {
console.log("Filling the contacts drop down select in the form"); // log what we're doing
selectDeleteOptions("contacts_list"); // Reset the select element clearing all options
// if we don't have anything in the global contact list - forget it and return
if (document.contactList.length == 0) {
console.log("the contact list/array is empty!");
return;
}
// We have contacts in the global array, so first parse the array into JSON and process it
var contactsJSON = JSON.parse(document.contactList);
var properties = ['Id', 'fname', 'lname']; // only need Id, fname, lname for drop down
var option, curRow; // variables for table properties
var contact = document.getElementById("contacts_list"); // get the select drop down element
// cycle through the rows in the contacts array
for (var rowIndex = 0; rowIndex < contactsJSON.length; rowIndex++) {
console.log(`Creating select items, rowindex is ${rowIndex}`);
option = document.createElement('option'); //create a select option element
curRow = contactsJSON[rowIndex]; // get the current row from the array
console.log(curRow); // log the data in the current row
// the value of this option will be the id, since that's what we'll use to get a contact. The text is fname + lname
option.value = curRow["Id"];
option.appendChild(document.createTextNode(`${curRow['fname']} ${curRow['lname']}`));
contact.appendChild(option); // append the option to the select element
}
console.log("Finished procesing contacts list");
}
// When the windo first loads, get the list of contacts which will also fill the table and drop down list
window.onload = function() {
getContacts();
};
You are running the wrong JS file. You want to do:
node contactserver.js
At the moment you are running contacts.js, which is client-side code.
To expand on this, you're seeing that error because document is a global variable available in browsers, but not in Node.js. Frontend code designed to run in a browser often relies in browser APIs that simply don't exist on a server, so attempting to run a client-side only file in a server environment will throw errors when it can't find global objects that only exist in a browser.
(Props to #Jon Church for the explanation from the comments below)
Turns out that contacts.js was the wrong file. Typing into the terminal: node contactsserver.js was the solution.
I am trying to send a Push Notification through Parse Cloud Code when a certain object has been modified - "dirty"
I think I am almost there, but received an error because I believe am creating a new user instead of querying for one.
Parse.Cloud.beforeSave("Fact", function(request, response) {
var dirtyKeys = request.object.dirtyKeys();
for (var i = 0; i < dirtyKeys.length; ++i) {
var dirtyKey = dirtyKeys[i];
if (dirtyKey === "isValid") {
//send push
// Creates a pointer to _User with object id of userId
var targetUser = new Parse.User();
// targetUser.id = userId;
targetUser.id = request.object.userID;
var query = new Parse.Query(Parse.Installation);
query.equalTo('user', targetUser);
Parse.Push.send({
where: query,
data: {
alert: "Your Fact was approved :)"
}
});
return;
}
}
response.success();
});
I found this post related to my problem. My question now is how to integrate the user query in my beforeSave block. Ideally I would create another function for the user query and place that in my beforeSave block.
**5/14 Update
I took #toddg's advice and fixed the before save. Here is a clearer picture of what I am trying to do and the new error.
A couple points (as #Subash noted in the comments) before I get into the code:
Parse.Push.send is an async operation, so you'll want to ensure you call response.success() after your push send completes. I'm going to deal with this using Promises, as I think they are more flexible than callbacks. If you're not familiar, read about them here
The return in your if statement will likely prevent the response.success() from being called.
Here's my recommended way of doing it:
Parse.Cloud.beforeSave("Fact", function(request, response) {
// Keep track of whether we need to send the push notification
var shouldPushBeSent = false;
var dirtyKeys = request.object.dirtyKeys();
for (var i = 0; i < dirtyKeys.length; ++i) {
var dirtyKey = dirtyKeys[i];
if (dirtyKey === "isValid") {
shouldPushBeSent = true;
}
}
if (shouldPushBeSent) {
//send push
// Creates a pointer to _User with object id of userId
var targetUser = new Parse.User();
// targetUser.id = userId;
targetUser.id = request.object.userId;
var query = new Parse.Query(Parse.Installation);
// We want to pass the User object to the query rather than the UserId
query.equalTo('user', targetUser);
Parse.Push.send({
where: query, // Set our Installation query
data: {
alert: "Your fact was approved"
}
}).then(function(){
// Now we know the push notification was successfully sent
response.success();
}, function(error){
// There was an error sending the push notification
response.error("We had an error sending push: " + error);
});
} else {
// We don't need to send the push notification.
response.success();
}
});
By the way, I'm assuming that you have a column on your Installation class that tracks which user is associated with each Installation.
I am developing my app, and one of the features will be messaging within the application. What I did, is I've developed 'send message' window, where user can send message to other user. The logic behind it is as following:
1. User A sends message to User B.
2. Firebase creates following nodes in 'Messaging':
"Messaging"->"User A"->"User B"->"Date & Time"->"UserA: Message"
"Messaging"->"User B"->"User A"->"Date & Time"->"UserA: Message"
Here is the code that I am using for sending messages:
sendMsg: function(receiver, content) {
var user = Auth.getUser();
var sender = user.facebook.id;
var receiverId = receiver;
var receiverRef = $firebase(XXX.firebase.child("Messaging").child(receiverId).child(sender).child(Date()));
var senderRef = $firebase(XXX.firebase.child("Messaging").child(sender).child(receiverId).child(Date()));
receiverRef.$set(sender,content);
senderRef.$set(sender,content);
},
(picture 1 in imgur album)
At the moment, I am trying to read the messages from the database, and sort them in according to date. What I've accomplished so far, is that I have stored the content of "Messaging/UserA/" in form of an Object. The object could be seen in the picture I've attached (picture 2).
http://imgur.com/a/3zQ0o
Code for data receiving:
getMsgs: function () {
var user = Auth.getUser();
var userId = user.facebook.id;
var messagesPath = new Firebase("https://xxx.firebaseio.com/Messaging/");
var Messages = messagesPath.child(userId);
Messages.on("value", function (snapshot) {
var messagesObj = snapshot.val();
return messagesObj;
}, function (errorObject) {
console.log("Error code: " + errorObject.code);
});
}
My question is: how can I read the object's messages? I would like to sort the according to the date, get the message and get the Id of user who has sent the message.
Thank you so much!
You seem to be falling for the asynchronous loading trap when you're reading the messages:
getMsgs: function () {
var user = Auth.getUser();
var userId = user.facebook.id;
var messagesPath = new Firebase("https://xxx.firebaseio.com/Messaging/");
var Messages = messagesPath.child(userId);
Messages.on("value", function (snapshot) {
var messagesObj = snapshot.val();
return messagesObj;
}, function (errorObject) {
console.log("Error code: " + errorObject.code);
});
}
That return statement that you have in the Messages.on("value" callback doesn't return that value to anyone.
It's often a bit easier to see what is going on, if we split the callback off into a separate function:
onMessagesChanged(snapshot) {
// when we get here, either the messages have initially loaded
// OR there has been a change in the messages
console.log('Inside on-value listener');
var messagesObj = snapshot.val();
return messagesObj;
},
getMsgs: function () {
var user = Auth.getUser();
var userId = user.facebook.id;
var messagesPath = new Firebase("https://xxx.firebaseio.com/Messaging/");
var Messages = messagesPath.child(userId);
console.log('Before adding on-value listener');
Messages.on("value", onMessagesChanged);
console.log('After adding on-value listener');
}
If you run the snippet like this, you will see that the console logs:
Before adding on-value listener
After adding on-value listener
Inside on-value listener
This is probably not what you expected and is caused by the fact that Firebase has to retrieve the messages from its servers, which could potentially take a long time. Instead of making the user wait, the browser continues executing the code and calls your so-called callback function whenever the data is available.
In the case of Firebase your function may actually be called many times, whenever a users changes or adds a message. So the output more likely will be:
Before adding on-value listener
After adding on-value listener
Inside on-value listener
Inside on-value listener
Inside on-value listener
...
Because the callback function is triggered asynchronously, you cannot return a value to the original function from it. The simplest way to work around this problem is to perform the update of your screens inside the callback. So say you want to log the messages, you'd do:
onMessagesChanged(snapshot) {
// when we get here, either the messages have initially loaded
// OR there has been a change in the messages
console.log('Inside on-value listener');
var i = 0;
snapshot.forEach(function(messageSnapshot) {
console.log((i++)+': '+messageSnapshot.val());
});
},
Note that this problem is the same no matter what API you use to access Firebase. But the different libraries handle it in different ways. For example: AngularFire shields you from a lot of these complexities, by notifying AngularJS of the data changes for you when it gets back.
Also see: Asynchronous access to an array in Firebase
I am quite new to SignalR.
My first assignment is to make simple chat app.
I have been browsing and reading and finally made a page where you come and chat and it works fine.
Now I need to show a list of connected clients. To achieve this I have wrote the following code.
This is my HUB.
public class ChatHub: Hub
{
chatEntities dc = new chatEntities();
public void Send(string message,string clientName)
{
Clients.addMessage(message,clientName);
}
// I want to save the user into my database, when they join
public void Joined(string userId,string userName)
{
CurrentChattingUsers cu = new CurrentChattingUsers();
cu.ConnectionId = userId;
cu.UserName = userName;
dc.CurrentChattingUsers.AddObject(cu);
dc.SaveChanges();
Clients.joins(userId, userName);
}
// This will return a list of connected user from my db table.
public List<ClientModel> GetConnectedUsers()
{
var query = (from k in dc.CurrentChattingUsers
select new ClientModel()
{
FullName = k.UserName,
UserId = k.ConnectionId
}).ToList();
return query;
}
}
And thats it...Now what??
Am I going to the right direction? If, I am then how to call this methods from the view?
Some good suggestions will really help me out.
cheers
EDIT:
I have added the following script when the hub start
$.connection.hub.start(function () {
chat.getConnectedUsers();
});
This is the method that returns the client names in my Hub
public List<ClientModel> GetConnectedUsers()
{
var data = (from k in dc.Users
select new ClientModel()
{
FullName = k.UserName
}).ToList();
Clients.loadUsers(data);
return data;
}
in firebug i can see it returns something as follows;
{"State":{},"Result":[{"FullName":"mokarom","UserId":null}, {"FullName":"aka8000","UserId":null},{"FullName":"johnnyno5","UserId":null},{"FullName":"reza","UserId":null},{"FullName":"amyo","UserId":null},{"FullName":"rezatech","UserId":null}],"Id":"0","Error":null,"StackTrace":null}
But, how would I display that in my view??
EDIT:
this the complete view so far
<script type="text/javascript">
var chat;
var myClientName
$(document).ready(function(){
myClientName = '#Request.Cookies["ChatterName"].Value';
// Created proxy
chat = $.connection.chatHub;
// Assign a function to be called by the server
chat.addMessage = onAddMessage;
// Register a function with the button click
$("#broadcast").click(onBroadcast);
$('#message').keydown(function (e) {
if (e.which == 13) { //Enter
e.preventDefault();
onBroadcast();
}
});
// Start the connection
$.connection.hub.start(function () {
chat.getConnectedUsers();
});
chat.loadUsers = function (data) {
loadUsers(data);
};
});
function onAddMessage(message,clientName) {
// Add the message to the list
$('#messages').append('<div class="chatterName">' + clientName + ' </div><div class="chatterMessage"> ' + message + '</div><div class="clear">');
}
function onBroadcast() {
// Call the chat method on the server
chat.send($('#message').val(), myClientName);
$('#message').val('');
}
function loadUsers(data) {
$('#clientList').html(data.Result[0].FullName);
}
</script>
Problem: don't see anything here: $('#clientList').html(data.Result[0].FullName);
firebug says 'data is not defined'
JavaScript
var chats = $.connection.chatHub;
chats.loadUsers = function (data) { loadUsers(data); };
var connectedUserCount = 0;
$.connection.hub.start(function ()
{ chats.getConnectedUsers(); });
function loadUsers = = function (data) {
console.log(data); //so you can see your data in firebug.etc
//which signal r will have converted to json so you could try
var numberOfUsers = data.length;
}
Once hub is started chats would have all the public functions of your hub available as javascript functions. This is what the signalr/hubs creates using the best available connection method between client and server.
In reverse your C# hub will have access to any javascripts functions you setup, e.g.
Clients.loadUsers(query);
//add this to you server side just before you return query
ps - you might also consider using OnConnectedAsync, though of course you might still persist these. I'm also waiting for full support for web farm support using sql, which is in the pipeline.