Firebase - Prevent child_added when delete with limitToLast - javascript

i want to build mini webchat - When view site i set show 5 messages and if view more, you can click button. All things are fine but when i remove 1 node, firebase auto add last node into, how can i prevent it?
Ex: I have node A,B,C,D,E,F,G. I had loaded list C,D,E,F,G but when i delete 1 in all, it auto add B into list.
<div id="messgesDiv">
<center><h3>Message</h3></center>
</div>
<div style="margin-top: 20px;">
<input type="text" id="nameInput" placeholder="Name">
<input type="text" id="messageInput" placeholder="Message" data-id="">
<input type="text" id="idproject" placeholder="ID Project">
</div>
<button id="delete">Delete Test</button>
<button id="edit">Edit</button>
<button id="loadmore">Load more</button>
<button id="showlastkey">Show last key</button>
My javascript
$('#loadmore').click(function() {
i = 0; old = first;
myDataRef.orderByKey().endAt(first).limitToLast(6).on('child_added', function (snapshot){
if( i == 0)
first = snapshot.key();
var message = snapshot.val();
if(snapshot.key() != old)
displayChatMessage(message.name, message.text, message.idproject, 'old');
i++;
console.log('myDataRef.orderByKey().endAt(first).limitToLast(6)');
});
});
$("#messageInput").keypress(function (e){
if(e.keyCode == 13){ //Enter
var name = $("#nameInput").val();
var text = $("#messageInput").val();
var idproject = $("#idproject").val();
if($("#messageInput").data("id")=='')
{
myDataRef.push({name: name, text: text, idproject: idproject});
}
else
{
myDataRef.child(key).update({name: name, text: text, idproject: idproject});
$('#messageInput').attr('data-id', '');
}
$("#messageInput").val("");
}
});
myDataRef.limitToLast(5).on('child_added', function (snapshot){
if( i == 0)
first = snapshot.key();
var message = snapshot.val();
displayChatMessage(snapshot.key(), message.name, message.text, message.idproject, 'new');
i++;
console.log(snapshot.key());
console.log(' myDataRef.limitToLast(5)');
});
function displayChatMessage(key, name, text, idproject, status){
//console.log(name + " -- " + text + " -- " +idproject);
if( status == 'new')
{
$('<div/>', { 'data-id': key , 'class' : 'test'}).text(text + " - ").prepend($('<em/>').text(name+": " )).append("IdProject: "+idproject).appendTo($("#messgesDiv"));
$("#messgesDiv")[0].scrollTop = $("#messgesDiv")[0].scrollHeight;
}
else
{
$('<div/>', { 'data-id': key , 'class' : 'test'}).text(text + " - ").prepend($('<em/>').text(name+": " )).append("IdProject: "+idproject).insertAfter($("center"));
$("#messgesDiv")[0].scrollTop = $("#messgesDiv")[0].scrollHeight;
}
}
$('#delete').click(function() {
myDataRef.child(key).remove();
$('#messgesDiv').filter('[data-id="'+key+'"]').remove();
});

Firebase limit queries act like a view on top of the data. So if you create a query for the 5 most recent messages, the Firebase client will ensure that you always have the 5 most recent messages.
Say you start with these messages:
message1
message2
message3
message4
message5
Now if you add a message6, you will get:
child_removed message1
child_added message6
So that your total local view becomes:
message2
message3
message4
message5
message6
Conversely when you remove message 6 again, you get these events:
child_removed message6
child_added message1 (before message2)
So that you can update the UI and end up with the correct list again.
There is no way to change this behavior of the API. So if you want to handle the situation differently, you will have to do this in your client-side code.
Your code currently only handles child_added. If you have add a handler for child_removed you'll see that you can easily keep the user interface in sync with the data.
Alternatively you can detect that the message is already in your UI by comparing the key of the message you're adding to the ones already present in the DOM:
function displayChatMessage(key, name, text, idproject, status){
var exists = $("div[data-id='" + key + "']").length;
if (status == 'new' && !exists) {
$('<div/>', { 'data-id': key , 'class' : 'test'}).text(text + " - ").prepend($('<em/>').text(name+": " )).append("IdProject: "+idproject).appendTo($("#messgesDiv"));
$("#messgesDiv")[0].scrollTop = $("#messgesDiv")[0].scrollHeight;
}
else {
$('<div/>', { 'data-id': key , 'class' : 'test'}).text(text + " - ").prepend($('<em/>').text(name+": " )).append("IdProject: "+idproject).insertAfter($("center"));
$("#messgesDiv")[0].scrollTop = $("#messgesDiv")[0].scrollHeight;
}
}

Related

remove object from array when user clicks button

I am trying to store a users selected items into an object so that when the user clicks a "remove button" it will also remove that item from my object. I seem to be running into an error where only the first object in my array will be removed and nothing else. Could I have some assistance?
each li has a data-id value of i, where i is an integer that increments once for every item the user adds to their list for example:
user clicks add
data-id:1
user clicks add
data-id:2
etc etc, currently when the user clicks remove...it will only remove the object with id: 0.. However clicking on any of the other items in the list does not affect the exerciseDataArr
EDIT: Included my html file, this is a Python Flask app and im using Jinja templates, as well as Wtforms to generate the form as I have a dynamic select field that uses my database to pull exercise names from that database for the user to pick from to build a routine.
createRoutine.html
{% extends 'profileSignedInBase.html' %}
{% block content %}
<div>
<div id="media">
<div>
<form action="/workoutroutines">
{{form.hidden_tag()}}
{% for field in form if field.widget.input_type != 'hidden' %}
{{ field(placeholder=field.label.text) }}
{% endfor %}
<button id="createBtn">CREATE</button>
</form>
<div>
<button id="addBtn">Add Exercise To List</button>
</div>
<h1>This is what you have planned for your routine</h1>
<ol id="routineWishlist">
</ol>
</div>
</div>
<script src="/static/addExercise.js"></script>
{% endblock %}
addExercise.JS
let jsonData = {}
let exerciseDataArr = []
let i = 0;
// generate list of items the user has selected for their workout
document.querySelector("#media").addEventListener("click", function (e) {
//Add Item to
if (e.target.id == "addBtn") {
e.preventDefault();
var exerciseValue = $('#exerciseChoices').find(":selected").text();
var workoutName = $('#workoutName').val();
var workoutDescription = $('#description').val();
if (workoutName == "") {
console.log("please fill out all data")
alert("please add a name")
return;
}
if (workoutDescription == "") {
console.log("please fill out all data")
alert("please add a description")
return;
}
console.log("You clicked on the Add button")
var li = document.createElement("li");
var div = document.createElement("div")
var remove = document.createElement("button");
li.setAttribute("data-id", i)
div.setAttribute("id", `exercise${i}`)
remove.setAttribute("id", "removeBtn");
remove.innerText = 'Remove';
try {
jsonData['name'] = workoutName;
jsonData['description'] = workoutDescription;
exerciseDataArr.push({ 'exercise': exerciseValue,
id: i})
} catch (error) {
console.error(error)
}
i++;
console.log(jsonData) //{"name": "workout 1","description": "My favorites"}
console.log(exerciseDataArr) //After adding 2 exercises to the list {"exercise": "2 Handed Kettlebell Swing","id": 0}{"exercise": "2 Handed Kettlebell Swing","id": 1}
var t = document.createTextNode(exerciseValue);
div.append(li)
li.append(remove);
li.appendChild(t);
document.querySelector("#routineWishlist").appendChild(div);
}
if (e.target.id === "removeBtn") {
e.preventDefault();
exerciseName = $(e.target).closest('div').attr('id');
exerciseOrder = parseInt($(e.target).closest('li').attr('data-id'));
console.log("remove " + typeof(exerciseOrder) + " " + exerciseOrder + " at " + exerciseName )
console.log("inside " + typeof(exerciseDataArr)) //object
//remove from displayed list of exercises
$(e.target).closest('div').remove()
// remove from object
for(let val in exerciseDataArr){
val = parseInt(val)
console.log(`id: ${val}`)
// if the exerciseDataArr contains id: exerciseOrder delete from exerciseDataArr
if(exerciseDataArr.hasOwnProperty("id") == exerciseOrder ){// <---does not activate unless the first 'li' is clicked.
console.log("the object has been found, now delete it")
delete exerciseDataArr[exerciseOrder]
val = undefined;
}
}
console.log(exerciseDataArr)
}
Can You post some more code ? If Possible post the HTML as well and what is the exerciseDataArr in your code it is not clearly explained

Ajax call after reading the current text input value

I am following this answer https://stackoverflow.com/a/20698523/1695685 in order to read value from a handheld barcode scanner. The code works fine as mentioned in the answer (fiddle is attached in the above link)
However, what I am trying to do is to make some ajax calls based on the current value of text input after reading a barcode.
The problem I'm facing is that if I scan barcode multiple times I am making ajax calls the same number of times after I press a button (which triggers the ajax call). For e.g. If I read 4 barcodes, I am making the same ajax call( in my case http://localhost:51990/Home/DecodeScanner) 4 times. What I am after is to make only one call after pressing a button, but only read the latest value from the text input box.
Every time I can scan a barcode the text input box is showing the new value (previous values are overridden). However, the ajax call is firing all the previous scans as well on pressing #scanner-verify-button button.
This is my modified Fiddle with my custom ajax calls
/*
This code will determine when a code has been either entered manually or
entered using a scanner.
It assumes that a code has finished being entered when one of the following
events occurs:
• The enter key (keycode 13) is input
• The input has a minumum length of text and loses focus
• Input stops after being entered very fast (assumed to be a scanner)
*/
var inputStart, inputStop, firstKey, lastKey, timing, userFinishedEntering;
var minChars = 3;
// handle a key value being entered by either keyboard or scanner
$("#scanInput").keypress(function(e) {
// restart the timer
if (timing) {
clearTimeout(timing);
}
// handle the key event
if (e.which == 13) {
// Enter key was entered
// don't submit the form
e.preventDefault();
// has the user finished entering manually?
if ($("#scanInput").val().length >= minChars) {
userFinishedEntering = true; // incase the user pressed the enter key
inputComplete();
}
} else {
// some other key value was entered
// could be the last character
inputStop = performance.now();
lastKey = e.which;
// don't assume it's finished just yet
userFinishedEntering = false;
// is this the first character?
if (!inputStart) {
firstKey = e.which;
inputStart = inputStop;
// watch for a loss of focus
$("body").on("blur", "#scanInput", inputBlur);
}
// start the timer again
timing = setTimeout(inputTimeoutHandler, 500);
}
});
// Assume that a loss of focus means the value has finished being entered
function inputBlur() {
clearTimeout(timing);
if ($("#scanInput").val().length >= minChars) {
userFinishedEntering = true;
inputComplete();
}
};
// reset the page
$("#reset").click(function(e) {
e.preventDefault();
resetValues();
});
function resetValues() {
// clear the variables
inputStart = null;
inputStop = null;
firstKey = null;
lastKey = null;
// clear the results
inputComplete();
}
// Assume that it is from the scanner if it was entered really fast
function isScannerInput() {
return (((inputStop - inputStart) / $("#scanInput").val().length) < 15);
}
// Determine if the user is just typing slowly
function isUserFinishedEntering() {
return !isScannerInput() && userFinishedEntering;
}
function inputTimeoutHandler() {
// stop listening for a timer event
clearTimeout(timing);
// if the value is being entered manually and hasn't finished being entered
if (!isUserFinishedEntering() || $("#scanInput").val().length < 3) {
// keep waiting for input
return;
} else {
reportValues();
}
}
// here we decide what to do now that we know a value has been completely entered
function inputComplete() {
// stop listening for the input to lose focus
$("body").off("blur", "#scanInput", inputBlur);
// report the results
reportValues();
}
function reportValues() {
// update the metrics
$("#startTime").text(inputStart == null ? "" : inputStart);
$("#firstKey").text(firstKey == null ? "" : firstKey);
$("#endTime").text(inputStop == null ? "" : inputStop);
$("#lastKey").text(lastKey == null ? "" : lastKey);
$("#totalTime").text(inputStart == null ? "" : (inputStop - inputStart) + " milliseconds");
if (!inputStart) {
// clear the results
$("#resultsList").html("");
$("#scanInput").focus().select();
} else {
// prepend another result item
var inputMethod = isScannerInput() ? "Scanner" : "Keyboard";
$("#resultsList").prepend("<div class='resultItem " + inputMethod + "'>" +
"<span>Value: " + $("#scanInput").val() + "<br/>" +
"<span>ms/char: " + ((inputStop - inputStart) / $("#scanInput").val().length) + "</span></br>" +
"<span>InputMethod: <strong>" + inputMethod + "</strong></span></br>" +
"</span></div></br>");
$("#scanInput").focus().select();
inputStart = null;
// Some transformations
const barcodeString = $("#scanInput").val();
const productCode = barcodeString.substring(5, 19);
const serialNumber = barcodeString.substring(36, 46);
const batch = barcodeString.substring(29, 34);
const expirationDate = barcodeString.substring(21, 27);
// AJAX calls
$('#scanner-verify-button').click(function() {
$.ajax({
url: "DecodeScanner",
type: "POST",
data: {
productCode: productCode,
serialNumber: serialNumber,
batch: batch,
expirationDate: expirationDate,
commandStatusCode: 0
},
async: true,
success: function(data) {
$('#pTextAreaResult').text(data);
}
});
});
}
}
$("#scanInput").focus();
HTML
<form>
<input id="scanInput" />
<button id="reset">Reset</button>
</form>
<br/>
<div>
<h2>Event Information</h2> Start: <span id="startTime"></span>
<br/>First Key: <span id="firstKey"></span>
<br/>Last Ley: <span id="lastKey"></span>
<br/>End: <span id="endTime"></span>
<br/>Elapsed: <span id="totalTime"></span>
</div>
<div>
<h2>Results</h2>
<div id="resultsList"></div>
</div>
<div class="col-sm-12">
<button id="scanner-verify-button" type="submit">Verify</button>
</div>
As per the code, the click handler for the scanner button seems to be registered multiple times.
The click handler should be registered only once.
The reportValues() is called multiple times which registers the click handler multiple times. This means when you press the button, all the click handlers get called and ajax request gets triggered.
You need to put the click handler outside of any function which can get called multiple times.
Also as per the code, all the variables which need to be accessed by the click handler should be declared outside reportValues.
The problem was the ajax call inside block $('#scanner-verify-button').click(function() {} was attaching new listener to the button on everytime the reportValues() call waas made.
I was able to solve this by moving the ajax calls in its own block
// Check for any input changes
var productCode, serialNumber, batch, expirationDate;
$("#scanInput").on("change paste keyup", function () {
barcodeString = $("#scanInput").val();
productCode = barcodeString.substring(5, 19);
serialNumber = barcodeString.substring(36, 46);
batch = barcodeString.substring(29, 34);
expirationDate = barcodeString.substring(21, 27);
});
// Ajax calls
$("#scanner-verify-button").click(function () {
$.ajax({
url: "DecodeScanner",
type: "POST",
data: {
productCode: productCode,
serialNumber: serialNumber,
batch: batch,
expirationDate: expirationDate,
commandStatusCode: 0
},
async: true,
success: function (data) {
$('#pTextAreaResult').text(data);
}
});
});
Working Fiddle

Prototype - spiceworks bulk close tickets select

I am looking for guidance to look for a way to accomplish some difficulty in programming that I don't have the experience to create.
I am using Spiceworks with a custom plugin called Plugin Bulk Delete and Close and it works fine but I need an additional logic. I have a custom attribute called Master Number where I can specify relations on tickets screenshot.
I'm looking for a way that in case a ticket is selected to close all with the same Master Number. Well, my programming is very limited and I just started with prototype and reading about jquery for a month and a half now. Any help or direction is all I need this is possibly very simple but not for me.
SPICEWORKS.app.helpdesk.ready(function(){
if(document.URL.indexOf("Master_View") >= 0){
if ($('Multi-Toolbar') === null)
{
var toolbarDInsert = '<a class="delete toolbar_buton" href="#" id="plugin-Dactions">';
var toolbarCInsert = '<a class="clopen toolbar_buton" href="#" id="plugin-Cactions"><span class="inner icon">Close/Open Ticket(s)</span></a>';
var cbInsert = '<span style="float:left;"><input type="checkbox" class="plugin-operation" name="cbselection" /></span>';
var HdInsert = 'div#ticket-list-body table tr td.cell-status';
var thinsert = '<div id="Multi-Toolbar" class="sui-toolbar"><tr><th><input type="checkbox" id="plugin-all" /></th></tr></div>';
plugin.includeStyles();
$('primary').insert({top:thinsert});
$('Multi-Toolbar').insert(toolbarCInsert);
$('Multi-Toolbar').insert(toolbarDInsert);
var insertcheckbox = function(actions){
actions.style.width = '2.5em';
actions.insert({top:cbInsert});
};
$$(HdInsert).each(insertcheckbox);
$('plugin-Cactions').observe('click', function(event){
event.stop();
var entries = $$('input.plugin-operation').select(function(input){
return input.checked;
}).collect(function(input){
return input.up('tr').id.replace('ticket-', '');
});
if(entries.size() > 0){
var answer = confirm('You are about to Close / Open ' + entries.size() + ' Ticket(s) Relying on the Ticket(s) state. \nContinue ?') ;
if (answer){
entries.each(function(entry){ new Ajax.Request('/tickets/toggle_status/' + entry);
});
}}
if(entries.size() <= 0){
alert('No ticket(s) selected, please select at least one ticket');
}
});
$('plugin-all').observe('click',function (e) {
var toggle = $('plugin-all').checked;
$$('div#ticket-list-body input[type=checkbox]').each(function(check) {
check.checked = toggle;
});
});
}
}
});

Javascript/jQuery redirect to page

I am using simplecart js. It's a really simple shop with one product. I would like to automatically redirect to the cart page when someone clicks the add to cart button.
The code for adding a product:
<div class="simpleCart_shelfItem">
<h2 class="item_name"> Awesome T-shirt </h2>
<p> <input type="text" value="1" class="item_Quantity"><br>
<span class="item_price">$35.99</span><br>
<a class="item_add" href="javascript:;"> Add to Cart </a></p>
</div>
The listener in simpleCart.js:
/* here is our shelfItem add to cart button listener */
, { selector: 'shelfItem .item_add'
, event: 'click'
, callback: function () {
var $button = simpleCart.$(this),
fields = {};
$button.closest("." + namespace + "_shelfItem").descendants().each(function (x,item) {
var $item = simpleCart.$(item);
// check to see if the class matches the item_[fieldname] pattern
if ($item.attr("class") &&
$item.attr("class").match(/item_.+/) &&
!$item.attr('class').match(/item_add/)) {
// find the class name
simpleCart.each($item.attr('class').split(' '), function (klass) {
var attr,
val,
type;
// get the value or text depending on the tagName
if (klass.match(/item_.+/)) {
attr = klass.split("_")[1];
val = "";
switch($item.tag().toLowerCase()) {
case "input":
case "textarea":
case "select":
type = $item.attr("type");
if (!type || ((type.toLowerCase() === "checkbox" || type.toLowerCase() === "radio") && $item.attr("checked")) || type.toLowerCase() === "text") {
val = $item.val();
}
break;
case "img":
val = $item.attr('src');
break;
default:
val = $item.text();
break;
}
if (val !== null && val !== "") {
fields[attr.toLowerCase()] = fields[attr.toLowerCase()] ? fields[attr.toLowerCase()] + ", " + val : val;
}
}
});
}
});
// add the item
simpleCart.add(fields);
}
}
]);
});
From what I have read it is bad practice to use href="javascript:;" is it a good idea to change it to a click function that will add the item to the cart then go to the cart page or just add the redirect? How do I go about this? Thanks
I’m not sure how the simplecart APi works, but you can try something like:
// add the item
simpleCart.add(fields);
window.location='/cart/'; // change to your cart route
If the cart saves to a server cookie, you might need to put this in a callback.

How can I delete the selected messages (with checkboxes) in jQuery?

I'm making a messaging system and it has a lot of AJAX. I'm trying to add a bulk actions feature with check boxes. I've added the checkboxes, but my problem is that I don't know how to make something happen to the selected messages.
Here's my function that happens whenever a checkbox is clicked:
function checkIt(id) {
if ($('#checkbox_' + id).is(':checked')) {
$('#' + id).addClass("selected");
}
else {
$('#' + id).removeClass("selected");
}
}
But, I don't know where to go from there.
Here is some example markup for one of the lines [generated by PHP] of the list of messages:
<div class="line" id="33" >
<span class="inbox_check_holder">
<input type="checkbox" name="checkbox_33" onclick="checkIt(33)" id="checkbox_33" class="inbox_check" />
<span class="star_clicker" id="star_33" onclick="addStar(33)" title="Not starred">
<img id="starimg_33" class="not_starred" src="images/blank.gif">
</span>
</span>
<div class="line_inner" style="display: inline-block;" onclick="readMessage(33, 'Test')">
<span class="inbox_from">Nathan</span>
<span class="inbox_subject" id="subject_33">Test</span>
<span class="inbox_time" id="time_33" title="">[Time sent]</span>
</div>
</div>
As you can see, each line has the id attribute set to the actual message ID.
In my function above you can see how I check it. But, now what I need to do is when the "Delete" button is clicked, send an AJAX request to delete all of the selected messages.
Here is what I currently have for the delete button:
$('#delete').click(function() {
if($('.inbox_check').is(':checked')) {
}
else {
alertBox('No messages selected.'); //this is a custom function
}
});
I will also be making bulk Mark as Read, Mark as Unread, Remove Star, and Add Star buttons so once I know how to make this bulk Delete work, I can use that same method to do these other things.
And for the PHP part, how would I delete all them that get sent in the AJAX request with a mysql_query? I know it would have to have something to do with an array, but I just don't know the code to do this.
Thanks in advance!
How about this
$('#delete').click(function() {
var checked = $('.inbox_check:checked');
var ids = checked.map(function() {
return this.value; // why not store the message id in the value?
}).get().join(",");
if (ids) {
$.post(deleteUrl, {idsToDelete:ids}, function() {
checked.closest(".line").remove();
});
}
else {
alertBox('No messages selected.'); // this is a custom function
}
});
Edit: Just as a side comment, you don't need to be generating those incremental ids. You can eliminate a lot of that string parsing and leverage jQuery instead. First, store the message id in the value of the checkbox. Then, in any click handler for a given line:
var line = $(this).closest(".line"); // the current line
var isSelected = line.has(":checked"); // true if the checkbox is checked
var msgId = line.find(":checkbox").val(); // the message id
var starImg = line.find(".star_clicker img"); // the star image
Assuming each checkbox has a parent div or td:
function removeDatabaseEntry(reference_id)
{
var result = null;
var scriptUrl = './databaseDelete.php';
$.ajax({
url: scriptUrl,
type: 'post',
async: false,
data: {id: reference_id},
success: function(response)
{
result = response;
}
)};
return result;
}
$('.inbox_check').each(function(){
if ($(this).is(':checked')){
var row = $(this).parent().parent();
var id = row.attr('id');
if (id == null)
{
alert('My selector needs updating');
return false;
}
var debug = 'Deleting ' + id + ' now...';
if (console) console.log(debug);
else alert(debug);
row.remove();
var response = removeDatabaseEntry(id);
// Tell the user something happened
$('#response_div').html(response);
}
});

Categories

Resources