Getting Double response in socket.io on emit - javascript

I am making a chat application in node js and socket.io . What I have don is, when a user click a link to go to the inbox of a user he or she chatted with, on that click even I send the id of that current user who clicked target chat to open its messages and the id of the target user who he or she chatted with through these two ids I am retrieving the messages of every user. But the problem is, on the first click the messages retrieved is the array of objects which works fine, but when the same target has been clicked again the messages array retrieved is twice in number and each time it is clicked it double the number of arrays and retrieves them.
TO Better understand , have a look at the snap shot. You can see that on each click the arrays retrieved are double in number.
My Code - NodeJs One
socket.on("get_user_messages",(data)=>{
console.log(data);
var GET_CLICKED_USER_MESS = "SELECT mess_txt,mess_time,mess_to FROM (select mess_to as user_id,mess_txt,mess_time,mess_id,mess_to from messages where mess_from = '"+data.active+"' AND mess_to='"+data.clicked+"' UNION select mess_to as user_id,mess_txt,mess_time,mess_id,mess_to from messages where mess_from = '"+data.clicked+"' AND mess_to='"+data.active+"' ORDER BY mess_id ASC) sq join users on users.id = sq.user_id";
con.query(GET_CLICKED_USER_MESS,(err,res)=>{
if(err) throw err;
socket.emit("get_ret_messages",result);
})
});
JavaScript where it is received
var d = document, _id, mess, messs, chatBody = document.getElementsByClassName("indChatBody")[0], result;
d.addEventListener("click",(e)=>{
e.preventDefault();
var event = e.target;
console.log("clicked");
if(event.parentNode.className=="indUser" || event.parentNode.className=="ind_user_messages"){
if(event.parentNode.className=="ind_user_messages"){
_id = event.parentNode.getAttribute("data-id");
} else if(event.parentNode.className=="indUser"){
_id = event.getAttribute("data-id");
}
socket.emit("get_user_messages",{clicked:_id,active:data.active_user});
socket.on("get_ret_messages",(data)=>{
result = [];
/*for(let i = 0; i<data.length;i++){
if(data[i].mess_to == data.active_user){
mess = '<span class="mess fromMess"><div class="textTime"><span class="messTime">'+data[i].mess_time+'</span></div><p class="senderText">'+data[i].mess_txt+'</p></span>';
chatBody.insertAdjacentHTML("beforeend",mess);
}
messs = '<span class="mess toMess"><div class="textTime"><span class="messTime">'+data[i].mess_time+'</span></div><p class="senderText">'+data[i].mess_txt+'</p></span>';
chatBody.insertAdjacentHTML("beforeend",messs);
}*/
result = data;
console.log(result);
});
}
})

Your bug is that you are adding a new socket listener each time you click.
You need to move the socket listener outside of your click listener:
d.addEventListener("click",(e)=>{
};
socket.on("get_ret_messages",(data)=>{}

Related

Firebase remove() entire nested array in database

How does one delete an entire nested child from a Firebase database by referencing an entry to find the desired point of deletion?
For example, I have two entries nested under (list). (1) -KcxacywN4EkvwAzugfV and (2) -KcxaeBAIW-WgLAsajvV. I want to remove (2) -KcxaeBAIW-WgLAsajvV from the database using it's ID -KcxaeBAIW-WgLAsajvU-4-725391765511696 (See picture below).
I have a button setup for each database entry, to display a remove button. Each button, contains the data- or ID for each database entry.
rootRef.on("child_added", snap => {
var title = snap.child("title").val();
var link = snap.child("link").val();
var type = snap.child("type").val();
var id = snap.child("id").val();
$("#table_data").append("<div class='col-lg-4'><div class='card card-app'><div class='card-block'><h4 class='card-title'>"+ title +"</h4><small>"+ type +"</small><hr><a href='"+ link +"' target='_blank'>Download</a> <a class='user float-right' onclick='removeClick()' data-name='"+ id +"'>Remove</a> </div></div></div>");
});
The onClick event and associated id for the button, trigger this function. My mental idea, is to then take the ID from data- to delete its nested child, (2) -KcxaeBAIW-WgLAsajvV from the database. Same process for all other nested entries.
function removeClick() {
$(".user").click(function() {
rvm = $(this).attr("data-name");
});
alert(rvm);
firebaseRef.remove(rvm);
}
I've studied https://firebase.google.com/docs/database/web/read-and-write and can't seem to figure out the actual deletion of the nested entry. How can I use remove() or maybe another method to accomplish this?
I have been trying this, to get a better understanding.
firebaseRef.child(rvm).remove();
Since rootRef, is how I'm viewing data. I tried.
rootRef.child().remove();
This simply deletes the whole database...
Final running code:
function removeClick() {
$(".user").click(function() {
rvm = $(this).attr("data-name");
});
alert(rvm);
var query = rootRef.orderByChild("id").equalTo(rvm);
query.once("value", function(snapshot) {
snapshot.forEach(function(itemSnapshot) {
itemSnapshot.ref.remove();
});
});
}
You can only remove an item if you know its key. So given your current structure, you will first need to look up the key for the id you mention.
Assuming you have a variable ref that points to the root of the database:
var listRef = ref.child("list");
var query = listRef.orderByChild("id").equalTo("-KcxaeBAIW-WgLAsajvU-4-725391765511696");
query.once("value", function(snapshot) {
snapshot.forEach(function(itemSnapshot) {
itemSnapshot.ref.remove();
});
});
If you have only a single child with the id, you should consider restructuring your database to use the id as the key. If you do that, you could remove the item with:
listRef.child("-KcxaeBAIW-WgLAsajvU-4-725391765511696").remove()

Sorting data in Firebase with JavaScript, how to compare all register in an array of objects?

I found this method, with which I have ordered the hours what I store in my records, I wanted them to be sorted from highest to lowest, but when I test the code, I notice that only two values of the array are compared, which are the first registers.
I've seen other methods of comparison and the logic is the same, what am I doing wrong? I group the messages per user, using the id of the user as key of the array , then I save the rest of data. I do this for retrieve the current messages, since I want show a list of the last currently messages sent.
This is the code:
var ref = new Firebase('https://chatfbexample.firebaseio.com/all-messages');
ref.on("value", function (snapshot) {
var Messages = [];
var value = 0;
snapshot.forEach(function (snap) {
value = snap.val().id;
fecha = snap.val().id;
Messages[value] = [];
Messages[value]['fecha'] = snap.val().fechahora; //I receive a date with the format HH:MM:SS
Messages[value]['texto'] = snap.val().texto;
});
function compare(a, b) {
var time1 = a['fecha'].replace(/:/gi, '1');
var time2 = b['fecha'].replace(/:/gi, '1');
var data1 = parseInt(time1);
var data2 = parseInt(time2);
// alert(time1);
if (data1 > data2)
return -1;
if (data1 < data2)
return 1;
return 0;
}
Messages.sort(compare);
for (var i in Messages) {
console.log("Hour: " + Messages[i]['fecha'] + ' ' + ' Message: ' + Messages[i]['texto']);
}
});
the result is something like this
Hour: 12:11:13 Message: whats'up?
Hour: 11:38:44 Message: p
Hour: 11:49:01 Message: hey?
the second and the third messages are not being compared
an image of my Firebase database
First off it probably would be better for you to save a timestamp from Firebase instead of an actual time, which was probably created on the client side, inside your database. This way you could easily let Firebase handle the sorting.
You would need to use firebase.database.ServerValue.TIMESTAMP to save the exact time at which Firebase received your message inside fechahora.
Following you could simply query all of your messages ordered by their child fechahora.
ref.orderByChild("fechahora").on("value", function (snapshot) {
//Do stuff with your already sorted data
}
If you want your query to have a better performance set the .indexOn property of your node containing all the messages inside your Firebase Database rules to fechahora.
Bonus: If you want your data to be ordered newest to oldest instead of oldest to newest, you just need to use the negative value of the timestamp created by Firebase.
Instead of ordering client-side, you'll be better off asking the server to sort the messages:
ref.orderByChild("fechahora").on(...
Make sure that you define the proper index in your Firebase security rules:
{
"rules": {
"all-messages": {
".indexOn": "fechahora"
}
}
}
Without that the sorting will still work, but will happen on the client instead of the server.

CRM: Javascript to Set Multiple Email Addresses when Replying to an Email

On CRM 2013 I'm trying to insert multiple participants into the email "to" field when users click on "Reply All". However I need to remove certain email addresses from the To line. So I created an array to loop and get all the email addresses except the one that needs to be removed.
However the problem here is that it only works if there is only one participant left after removing the unwanted participants. If there are two or more participants the script will not populate any participants at all.
Is there a way to populate multiple email participants? Or is there a better approach than what I'm trying to do here?
Here's my code:
var toParty = Xrm.Page.getAttribute("to").getValue();
var partyListArray = new Array();
for (var indxAttendees = 0; indxAttendees < toParty.length; indxAttendees++) {
// using oData to get participant email address
var email = getParticipantEmail(
toParty[indxAttendees].entityType,
toParty[indxAttendees].id
);
if (email != "test#test.com") {
partyListArray[indxAttendees] = new Object();
partyListArray[indxAttendees].id = toParty[indxAttendees].id;
partyListArray[indxAttendees].name = toParty[indxAttendees].name;
partyListArray[indxAttendees].entityType = toParty[indxAttendees].entityType;
}
}
Xrm.Page.getAttribute("to").setValue(null);
Xrm.Page.getAttribute("to").setValue(partyListArray);
Instead of creating a whole new array, you can delete what you want from the array itself. Try this:
var emails = [{email: "add1#domain.com"}, {email: "add2#domain.com"}, {email: "address#toBe.Removed"}, {email: "add3#domain.com"}, {email: "add4#domain.com"}];
var removeIndex = emails.map(function(item) { return item.email; }).indexOf("address#toBe.Removed");
removeIndex > -1 && emails.splice(removeIndex, 1);

Need assistance delaying javascript while waiting for Sharepoint list item deletion.

I'm trying to automate updating of 8 region lists in SharePoint 2010. The process incudes 1) publishing a master list of all 8 regions from a spreadhseet, 2) deleting all list items in 8 region lists, 3) copying region specific items from the master list into the specific region lists. Each component of the process works flawlessly when executed individually. Now, I'm just trying to put the steps into loop to process all three steps for all 8 regions automatically.
One of the main problems is, once I submit a request to delete the list items from Region 1, I am unable to copy the new contents until the delete it finished. Of course my sript says it's done, but all it has done was submit the request to SharePoint. SharePoint may take anwhere from 15 seconds to 1 minute to actually delete the list items.
So, once I submit the delete, I enter another loop that queries the Region 1 list size. Once it confirms the list is down to zero items, I would like to continue with the copy. Now, the "query the size" loop bombs with different errors such as "the list does not exist", etc. Here's the extracted, pertinent pieces of my code. If anyone has suggestions for fix/improvement, I would greatly welcome the feedback.
//Loop through X (8) regions and delete all list items from 'Inventory Region X' List
function main() {
for (var i=1;i<9;i++)
{
alert("Enter Delete Loop " + i);
listname = "Inventory Region " + i;
// Submit the delete request
deletetheregionitems(i);
// Wait for the delete to complete
controllistcount = null;
alert("Begin delay cycle.");
while (controllistcount != 0)
{
countoflistitems(listname);
}
controllistcount = null;
alert("Exit loop!");
}
alert("All Region Lists should now be empty.");
}
function countoflistitems(listname) {
// Get the count of items in the requested list
var clientContext = new SP.ClientContext.get_current();
oList = clientContext.get_web().get_lists().getByTitle(listname);
var camlQuery = new SP.CamlQuery();
camlQuery.set_viewXml('<View><Query><Where><Geq><FieldRef Name=\'ID\'/><Value Type=\'Number\'>1</Value></Geq></Where></Query></View>');
this.listItems = oList.getItems(camlQuery);
clientContext.load(listItems, 'Include(Id)');
clientContext.executeQueryAsync(Function.createDelegate(this, this.countretrieveSucceeded), Function.createDelegate(this, this.queryFailed));
}
function countretrieveSucceeded(sender, args) {
controllistcount = listItems.get_count();
}
Use Promises to pause until the delete callback resolves before adding new. Don't repeatedly poll the server. Pseudo code:
deleteItems().then(){
addNewItems();
}
function deleteItems(){
var dfd=jQuery.deferred;
[JSOM code to delete items]
executeQueryAsync(function(){
dfd.resolve();
}, null);
return dfd.promise();
}
I'm typing this from memory on my phone so that isn't exactly right but its close and should give you the general idea.

client-side caching of jquery-ui-autocomplete: javascript/jquery regex to match a string "index" of an object

In my web app , i am using jquery ui autocomplete which data are stored in the database (Mysql).
In this application, i have the administration official data And i enable the users to enter their own data. Data will be displayed in the autocomplete drop down list .
The administration data are limited and never change thus are preloaded and cached in memory, however the user data aren't (since user data grows exponentially and there is no point of using a database if all is to be cached in memory).
So i am doing multi levels caching in my web app, of which, i am caching autocomplete's user data on the client side (instead of getting them from db on each search).
The scenario is as follow:
When the app loads, the admin data (that are preloaded in memory) are loaded and cached in a JS object to be used in the autocomplete mechanism.
Thus when a user searches for a word, it will check the admin data JS object and if it can't find it, it will go to db and find it in user data.
So for the case of searching user's data in db, What i am doing right now is the following:
$( "#search" ).autocomplete({
source:function(request, response) {
var results = $.ui.autocomplete.filter(data, request.term);
if (request.term.toUpperCase() in autocomplete_cache) {
response($.map(autocomplete_cache[request.term.toUpperCase()], function(item) {
return {value: item.members.name, md5: item.members.ID}
}))
return;
}
where autocomplete_cache is a javascript object:
var autocomplete_cache = {};
// ...it gets filled everytime a new $.post gets new data
...autocomplete_cache[request.term.toUpperCase()] = data;
For the moment, it is caching the exact term searched by a user.
For example, if a user writes "ABCD" and which is not already cached :
--> go to the database (MySQL) do :"WHERE term Like %ABCD%"
--> autocomplete_cache["ABCD"] = "data parsed from mysql"
So if the same USER now retypes "ABCD" it will get it from the autocomplete_cache Object. However if the USER type "ABC" it won't get it from the cache object because of the line:
if (request.term.toUpperCase() in autocomplete_cache)
So what i would like to do is to match what the User types in the cache, so in this scenario if the User types "ABC" (which is contained in cache's "ABCD" String) , the autocomplete will build a drop down list containing "ABCD".
Is there a way of doing that? the reason for this requirement instead of finding the exact String index in the cache object , it will find a wider range and thus limit probabilities of needing to go to db to find the new matches.
Instead of looking for a specific key, test each key whether the term is a substring:
var term = request.term.toUpperCase();
for (var key in autocomplete_cache) {
if (key.indexOf(term) > -1) {
// term is contained in key
}
}
And instead of calling response on the first match, collect all matches’ items in an additional array and call response at the end:
var term = request.term.toUpperCase(),
items = [];
for (var key in autocomplete_cache) {
if (key.indexOf(term) > -1) {
items.merge($.map(autocomplete_cache[key], function(item) {
return {value: item.members.name, md5: item.members.ID}
});
}
}
if (items.length > 0) response(items);
You might also want to remove any duplicates in items and sort the resulting items before calling response:
var term = request.term.toUpperCase(),
items = [],
index = {};
for (var key in autocomplete_cache) {
if (key.indexOf(term) > -1) {
$.each(autocomplete_cache[key], function(item) {
var id = item.members.ID;
if (!index[id]) {
items.push({value: item.members.name, md5: item.members.ID});
index[id] = true;
}
});
}
}
items.sort(function(a, b) { return a.name.localeCompare(b.name); });
if (items.length > 0) response(items);

Categories

Resources