I want to hide a 'div' after a button is clicked. I don't want to use .remove() because when you refresh the app it comes back. I have the information about this div on the database and I wanna work with it.
I tried already creating an Ajax call to select the information that I'm looking at and then on the front-end I'm telling if it exist then delete it. But I feel like I'm missing something and I don't know why.
Frontend:
$('#deletePromo').on('click', function(res){
let success = function(res){
if (eventName && customerID){
$(`#promotion-container .promo${i}`).remove();
}
}
$.ajax({
type: 'POST',
url: '/api/promotions-done',
crossDomain: true,
//success: success,
dataType: 'json',
data: {
customerID : customerID,
eventName : eventName,
}
}).done(function(res){
console.log('res', res)
if (res != null){
$(`#promotion-container .promo${i}`).remove();
//$(`#promotion-container .promo${i}`).css('display', 'none')
}
})
})
})
Backend:
router.post('/promotions-done', function(req, res) {
let customerID = req.user.customer_id
let restaurantGroupId = req.user.restaurant_group_id
let eventName = req.body.eventName
db.task(t => {
return t.any(`SELECT * FROM promotions_redemption WHERE (customer_id = '${customerID}' AND event_name = '${eventName}' AND restaurant_group_id = ${restaurantGroupId})`).then(function(promotionsDone) {
return res.json({'promotionsDone': promotionsDone})
})
.catch((err) =>{
console.log(err)
})
})
})
What I'm trying to do here is saying if the customerID and eventName already exist on the table then remove div from the person. I don't have much experience working with backend so is there a way to tell the program to check the database for this information and if it exists then remove the div.
You probably have some HTML in a template file, or in the database that has the button there to start with. Since your AJAX code will only run when the button is clicked, you will need to either do 1 of 2 things:
Add an AJAX call on page load
Handle looking for the button and hide/show it in your templating language/platform (think asp.net, python django, php laravel etc) to avoid the AJAX request.
Since we don't know your platform, I will show you option 1
First, I would change the initial state of your HTML to NOT show the button by default. This would look something like this:
<div id="promotion-container">
<button class="promo" style="display: none" />
</div>
Otherwise you will have the button be shown for the amount of time the AJAX request takes.
Next, you will want to add this function call to the page. I have reversed the login in the done function to "show" the button or unhide it.
$(document).ready(function(){
let success = function(res){
if (eventName && customerID){
$(`#promotion-container .promo${i}`).remove();
}
}
$.ajax({
type: 'POST',
url: '/api/promotions-done',
crossDomain: true,
//success: success,
dataType: 'json',
data: {
customerID : customerID,
eventName : eventName,
}
}).done(function(res){
console.log('res', res)
if (res === null){
//$(`#promotion-container .promo${i}`).remove();
$(`#promotion-container .promo${i}`).css('display', 'block')
}
})
})
})
The easiest solution to your problem would be to use a client-side cookie. If you don't have a cookie package already, I'd recommend js-cookie.
On your html page:
<div id="promotion-container" hidden> //you want it hidden by default
//if it is shown by default there will be an annoying screen flicker when the pages loads
<script src="/path/to/js.cookie.js"></script>
On jQuery page:
$(document).ready( function() {
if (!Cookies.get('name1') || !Cookies.get('name2')) {
$('#promotion-container').show();
};
Cookies.set('name1', customerId, {expires:14} ); //must include expiration else the cookie will expire when the browser closes.
Cookies.set('name2', eventName, {expires:14} ); //you might need to make an eventId if eventName is too large
});
The second input for Cookies.set is the 'value' for the cookie. if 'value' = null, then Cookies.get('name') = null.
This is assuming you already have the means to set customerId and eventName for each user. Also you might need to modify where the Cookies are set based on when the customerId is created on the page.
EDIT:
There are 2 ways you can run a query the way you describe, both of which won't work they way you want unless you use a session cookie.
1) You have res.render inside of a query.
This would ensure the div is never shown to a user that has already clicked on it, but would significantly hurt your site's performance. The query would run every time the page is rendered, regardless of whether or not the client has a customerId. Your site would would be painfully slow with a large amount of traffic.
2) You run a POST request through client-side js with ajax and compare the customerId with your db; if a result is found, you remove the div.
This would function the way you want it to, and wouldn't hurt performance, but nothing is stopping a customer from using something like burp to intercept the POST request. They could change the data argument to whatever they want and make sure the div loads for them.
The only solution to these problems that I see would be to validate a user when they click on the div AND on the server with a session cookie. (for user validation I use passport and express-session).
I can show you how I set this up, but to make it specific to your needs I would need to know more about how your site is setup.
PS I misunderstood why you needed to hide the div, and using a client-side cookie would be a terrible idea in hindsight.
Related
I am working on a multiple-choice question app and I am a database where I am storing the users answers. I just want to store the first answer they select to store it in the database. After the first selection, they can still click on the other answers to run the checkAnswer() but the storeAnwserStats() should not run. How can I do that?
app.js
function storeAnwserStats(questionId, answerId){
$.ajax({
url : "/store_stats",
type : "POST",
data : JSON.stringify({
question_id: questionId,
answer_id:answerId,
}),
contentType: 'application/json; charset=utf-8',
dataType: 'json',
})
.done(function(data){
console.log("After " +data["correct_answer"]);
});
}
function checkAnswer(divSelected, answerId, questionId, isCorrect){
if(isCorrect == 1){
divSelected.style.backgroundColor = "green";
}
else{
divSelected.style.backgroundColor = "red";
}
storeAnwserStats(questionId, answerId)
}
[1]: https://i.stack.imgur.com/xyZfY.png
You can do multiple things for this.
METHOD 1
Disable the options: After the person has answered you can make the the sibling options disabled.
DOWNSIDE: the person can make the element enabled again by inspecting the element.
METHOD 2
Server side validation: check from server side if the answer has been answered or not on each call.
DOWNSIDE: server calls are increased.
METHOD 3
Remove the onclick along with onClick: After the person has answered you can make the the sibling options disabled AND remove the onClick call from them.
DOWNSIDE: Not sure of any yet.
EDIT 1
Server side validation is still important
Even after you do all those things above if the person really wants to be fishy
they can still send answers to your server by calling the function from console.
suppose in the OnClick function of the option you are calling your storeAnwserStats(), and since they are client side JS files I can easily check it and send the request by going to the console and typing storeAnwserStats(question_id, answer_id) (and question ID can be seen in the network tab)
good luck!
I need to make a list of web api calls(for eg: 20) on a user click. Each web api is to update particular user. I m looping through each user and calling the web api using $.ajax() method.
In order to show the progress, I m using a spinner. Below the sprnner I wanted to show progress of each web api call. That means in success of web api call I wanted to show the user name detials(for eg: John's Information updated). So the the text should change for every success call and set the html value. I wanted to show this as animated text one after the other.
John's Information updated
Next -> Sam's Information updated
Next -> Dan's Inforamtion updated
....so on
Note: Above line should be shown one at a time. its not append, its replace of name for every success call.
for(var i=0; i < userArray.Length; i++)
{
$.ajax(
url: '<some url>',
method: 'PUT',
data: userData,
sucess: function(response){
$('#displayText').html(`${userData.Name}'s Information Updated`);
}
);
}
The above code is not showing the user names one by one, its not changing the value of the displayText id in every service success call and showing the progress in anumated way.
I also tried using setInterval and setTimeOut inside the success but no luck.
Please help.
Ajax is async, so when you throw that into a loop you are actually calling all the requests in parallel and not sequentially. If you want to do a sequential requests you will want to use a queue/stack. See the example below.
function updateUser(user) {
return $.ajax({
url: '<some url>',
method: 'PUT',
data: user
});
}
function updateUsers(users, onUpdate) {
const queue = users.slice();
const dequeue = () => {
if (queue.length > 0) {
const user = queue.shift();
updateUser(user)
.then(function () {
onUpdate(user);
})
.always(function () {
dequeue();
});
}
};
dequeue();
}
updateUsers([1,2,3], function(user) {
$('#displayText').text(`${user.Name}'s Information Updated`);
});
Calling updateUsers with a list of users will queue an update for a single user and will wait for them to finish. After each successful update onUpdate is called with the user so that you can decouple user update logic with view specific things.
Also notice the use of .text() instead of html. Since you are using user input, a user can potentially have a name that is html such as <script>alert('hahaha')</script> and you will execute any code the malicious user wants. Please see xss
I'm retrieving some data into a JSON array, then display it into an HTML table which contains some data enclosed within hyper links. i.e. a couple of the columns' data are clickable, once clicked it displays another JSP page (say page #2) with some more data which was kept on the JSON array itself.
Now this page 2 has a 'Back' button functionality - the expected behavior is when user clicks the 'Back' button it should go back to page 1 where the HTML table data was displayed and user should be able to see the data which they first fetched too. i.e. there should be some way to remember the data fetched from my initial AJAX request and retrieve the same data which user fetched in page 1 when they go back to that page from the child page#2.
Th AJAX call is triggered when user enters an account# and the type of account - I fetch data accordingly and get the result in the 'response' object and neatly display it on html table, but after user moves from that page and again hits the back button I see the page#1 without the table. Now again I cannot ask the user to re-enter the details to see the data that they retrieved earlier. It's pretty annoying.
Please give me a solution to this problem. Thanks All.
Appreciate for taking time to read this.
Here's a part of the code:
$(document).ready(function () {
var flag = "1";
$('#accountType').bind('change', function (event) {
var accountType = $('#accountTypeSelect').val();
var account = $('#accountText').val();
jQuery.ajax({
type: 'POST',
url: '${pageContext.request.contextPath}' + "/Page1.spr", //request page
cache: false,
dataType: "json",
data: {
"accountType": accountType,
"account": account,
"flag": flag
}, //data sent to request page
success: function (response) {
// code to display the data into the html table
},
error: (function (message) {
console.log("error message : " + message);
}),
statusCode: {
404: function () {
alert("page not found");
}
}
});
});
You can save the data in HTML5 sessionStorage or localStorage using the setItem method as follows:
success: function(response) {
sessionStorage.setItem("result", response)
// code to display the data into the html table
}
And access it later using the getItem() When you come back to the page like
var prevResponse = JSON.parse(sessionStorage.getItem("result"));
if(prevResponse)
{
// code to be executed when old dats is found
}
else{
}
Ideally you code in first pages load will be something like
var prevResponse = JSON.parse(sessionStorage.getItem("result"));
if(prevResponse)
{
// data exists : code to be executed when old dats is found
}
else{
jQuery.ajax({}) // send the AJAX request to fetch data
}
You can read more about session and local Storage here
Browser support for web storage API.
Update
If you don't have HTML5 support, you could use jQuery cookie plugin (There are plenty of others as well) for storing data at client side.
You can store data into a cookie as follows:
$.cookie("result", response);
Then to get it from the cookie like:
$.cookie("result");
You maybe can use cookie via jquery. But user have to enable the browser's cookie. It usually enabled by default setting.
To add:
$.cookie("key1", data1);
$.cookie("key2", data2);
To read:
$.cookie("key1");
To delete:
$.removeCookie("key1");
So, you can try to read cookie to load table data, if no data, call ajax:)
Another way is to save it in a hidden input:
success: function(response){
$("#hiddenInput").val(JSON.stringify(response));
}
I want only execute my ajax post 1 time, i try to avoid to the user refresh the page and execute so much times the ajax post,
I thought in create a cookies, but i don't know, and i'm no sure, somebody know how?
This is my jquery.
var t = jQuery.noConflict();
t( document ).ready(function() {
t.cookie("example", "foo", { expires: 7 }); // Sample 2
console.log( "ready!" );
alert(t.cookie("example"));
var data = '<?php echo json_encode($json_full);?>';
t.ajax({
url: 'my api url',
type: 'POST',
success: function(r) { alert(JSON.stringify(r)) },
dataType: 'JSON',
data: { data: data, }
})
});
/I need run this AJAX only one time because is a checkout page to send the order, and if i refresh the page, send every time the same order, and this i don't want/
Thanks a lot!
Things like these can not be safely controlled on the client's browser. Any user with minimal knowledge in JavaScript will be able to open up the developers tools for their browser and manipulate the code or any values you might have stored (such as deleting the cookie you have set).
This limitation should be implemented on the server.
It really depends on the scope of your application. You might be able to limit the requests per IP address, but that might prevent multiple people from the same office for example loading the page at the same time.
Using user authentication and persistent server storage you'll be able to limit the effect of the request, but you probably won't be able to prevent the actual request from being sent as anyone can make that request even from outside the browser. You could store the user_id of the user that initiated the request and only allow the resulting action to occur if a certain time has passed since the last request.
A better solution to avoid double submits, is to use a POST query for the submit request and let the server respond with a redirect to a normal (harmless) receipt/thankyou page.
Then if the user refreshes the receipt page they will simply repeat the GET request to the receipt page and not the post.
You should still add some checks server side to avoid multiple POST requests somehow (using sessions, timestamps or something), in case a malicious user deliberately tries to resubmit.
This will only work on IE8 and above, but you can use localStorage:
var t = jQuery.noConflict();
t( document ).ready(function() {
t.cookie("example", "foo", { expires: 7 }); // Sample 2
console.log( "ready!" );
alert(t.cookie("example"));
if(localStorage['submitted'] === undefined){
var data = '<?php echo json_encode($json_full);?>';
t.ajax({
url: 'my api url',
type: 'POST',
success: function(r) {
localStorage['submitted'] = true;
alert(JSON.stringify(r));
},
dataType: 'JSON',
data: { data: data, }
})
}
});
This way the first time it will run the AJAX because you haven't set the localStorage variable, but upon success you do and it will not resubmit on page refresh.
If you wanted to have the ability to send again upon a future visit, just use sessionStorage instead of localStorage. Same syntax and everything.
There is one feature on my site: delete without page refresh. The user just presses 'delete' and the browser will send Ajax-request. It will load 'delete' script with id parameter.
All work well. But it is not very good because of referential integrity of the database. For example, It is possible to delete street, where some people are living.
I want to upgrade my script. I want to add a check to delete script and don't let delete data if some 'people' are connected to 'street' table.
jQuery handler of button click:
$('body').on('click', '.deleteStreet', function()
{
var id = $(this).attr('id');
var hideMe = $(this).parent().parent();
var dataString = 'id=' + id;
if(confirm("Are you sure you want to delete street? It is possible some people living there!"))
{
$.ajax({
type: "GET",
url: "/index.pl?mode=streets&action=delete",
data: dataString,
cache: false,
success: function(e)
{
hideMe.hide();
}
});
return false;
}
});
It will call script anyway and now will delete data anyway. I can add some checks to delete script now and it wouldn't delete, but jquery script would work anyway and will hide table row anyway (because request was send ok, without 404, etc)
1) Is it possible to see delete script result and hide or not hide row depending on it? For example, it will return true or false, js script will catch it and show message about deleting or not deleting of data depending on it.
2) This problem caused by structure of my site. There are some switches on index.pl and load appropriate scripts loading depending on query (mode=street then load street.pl, mode=user then load users.pl etc). So it will show all data loaded before delete.pl script and it will be impossible to check script returned true or false.
Any help? :) Thank you!
P.S.: I am very sorry for my awful english.
You can have the result of your ajax call in the first parameter of the success callback. ex:
$.ajax({
type: "POST",
url: "/index.pl?mode=streets&action=delete",
data: dataString,
cache: false,
success: function(e)
{
if(e === '1'){
hideMe.hide();
}
}
});
try to log your result in the console for tests: console.log(e).
For deleting data you should use a POST request ( or DELETE but not supported by all browsers).
I dont know how your datas (streets) looks like but an other way could it be to return all your existing streets in a json object on the delete request result. And refresh all lines.