Show animated text for mulitple API calls using Jquery - javascript

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

Related

Hide div after click of button with mysql

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.

JQuery - Looping a .load() inside a 'for' statement

I'm not sure if this will actually be possible, since load() is an asynchronous method, but I need some way to basically Load several little bits of pages, one at a time, get some data included in them via JavaScript, and then send that over via Ajax so I can put it on a database I made.
Basically I get this from my page, where all the links I'll be having to iterate through are located:
var digiList = $('.2u');
var link;
for(var i=0;i<digiList.length;i++){
link = "http://www.digimon-heroes.com" + $(digiList).eq(i).find('map').children().attr('href');
So far so good.
Now, I'm going to have to load each link (only a specific div of the full page, not the whole thing) into a div I have somewhere around my page, so that I can get some data via JQuery:
var contentURI= link + ' div.row:nth-child(2)';
$('#single').load('grabber.php?url='+ contentURI,function(){
///////////// And I do a bunch of JQuery stuff here, and save stuff into an object
///////////// Aaaand then I call up an ajax request.
$.ajax({
url: 'insertDigi.php',
type: 'POST',
data: {digimon: JSON.stringify(digimon)},
dataType: 'json',
success: function(msg){
console.log(msg);
}
////////This calls up a script that handles everything and makes an insert into my database.
}); //END ajax
}); //END load callback Function
} //END 'for' Statement.
alert('Inserted!');
Naturally, as would be expected, the loading takes too long, and the rest of the for statement just keeps going through, not really caring about letting the load finish up it's business, since the load is asynchronous. The alert('Inserted!'); is called before I even get the chance to load the very first page. This, in turn, means that I only get to load the stuff into my div before I can even treat it's information and send it over to my script.
So my question is: Is there some creative way to do this in such a manner that I could iterate through multiple links, load them, do my business with them, and be done with it? And if not, is there a synchronous alternative to load, that could produce roughly the same effect? I know that it would probably block up my page completely, but I'd be fine with it, since the page does not require any input from me.
Hopefully I explained everything with the necessary detail, and hopefully you guys can help me out with this. Thanks!
You probably want a recursive function, that waits for one iteration, before going to the next iteration etc.
(function recursive(i) {
var digiList = $('.2u');
var link = digiList.eq(i).find('map').children().attr('href') + ' div.row:nth-child(2)';
$.ajax({
url: 'grabber.php',
data: {
url: link
}
}).done(function(data) {
// do stuff with "data"
$.ajax({
url: 'insertDigi.php',
type: 'POST',
data: {
digimon: digimon
},
dataType: 'json'
}).done(function(msg) {
console.log(msg);
if (i < digiList.length) {
recursive(++i); // do the next one ... when this is one is done
}
});
});
})(0);
Just in case you want them to run together you can use closure to preserve each number in the loop
for (var i = 0; i < digiList.length; i++) {
(function(num) { < // num here as the argument is actually i
var link = "http://www.digimon-heroes.com" + $(digiList).eq(num).find('map').children().attr('href');
var contentURI= link + ' div.row:nth-child(2)';
$('#single').load('grabber.php?url=' + contentURI, function() {
///////////// And I do a bunch of JQuery stuff here, and save stuff into an object
///////////// Aaaand then I call up an ajax request.
$.ajax({
url: 'insertDigi.php',
type: 'POST',
data: {
digimon: JSON.stringify(digimon)
},
dataType: 'json',
success: function(msg) {
console.log(msg);
}
////////This calls up a script that handles everything and makes an insert into my database.
}); //END ajax
}); //END load callback Function
})(i);// <-- pass in the number from the loop
}
You can always use synchronous ajax, but there's no good reason for it.
If you know the number of documents you need to download (you can count them or just hardcode if it's constant), you could run some callback function on success and if everything is done, then proceed with logic that need all documents.
To make it even better you could just trigger an event (on document or any other object) when everything is downloaded (e.x. "downloads_done") and listen on this even to make what you need to make.
But all above is for case you need to do something when all is done. However I'm not sure if I understood your question correctly (just read this again).
If you want to download something -> do something with data -> download another thing -> do something again...
Then you can also use javascript waterfall (library or build your own) to make it simple and easy to use. On waterfall you define what should happen when async function is done, one by one.

Controlling an $.ajax function within a js "for" loop?

Here's my issue. I have a js function that performs an $.ajax call to fetch some data from a server. When it gets that data back, I need to pass control back to the browser in order to show an update to a div.
The js function is itself within a for loop, and I need to ensure that the for loop does not advance until the js function has updated the div and allowed the Browser to display that update, at which point the for loop advances and the js function (with its ajax call) is called again, continuing until the for loop test causes the loop to end.
I've tried many different approaches - callbacks, promises etc, but to date I can't seem to get a handle on ensuring that the loop doesn't advance until the js function gets its server data, updates the div, causes the browser to display that update and fully completes.
Here's a simple stripped-down version of the function:
function myFunction (email) {
var request = $.ajax( {
url: 'getit.php',
cache: false,
async: false,
method: "post",
timeout: 1000,
data: "requesttype=getemailname&email="+encodeURIComponent(email)
});
request.done(function(response) {
$("#myDiv").html(response);
});
}
and here's part of the js that calls it:
.....
var emailscount = emails.length;
for(var i=0;i<emailscount;i++) {
myFunction (emails[i]);
}
.....
So, my issues are:
1) myFunction must allow the browser to display the updated div html - I'm not sure how to achieve that?
2) the for loop should only proceed when myFunction has received the data back from the server, updated the div html, AND allowed the browser to display that div.
At the moment, I have set the $.ajax call async flag set to "false" to stop execution until the data comes back, but how do I ensure the browser displays the new div content, and that the for loop does not proceed to call myFunction again until the previous myFunction call fully completes?
Any help you can give me would be very welcome, as right now I can't get this all to work!
Sounds like you need a recursive function, not a for loop with synchronous ajax calls
(function myFunction(i) {
$.ajax({
url: 'getit.php',
method: "post",
timeout: 1000,
data: {
requesttype : 'getemailname',
email : emails[i]
}
}).done(function(response) {
$("#myDiv").html(response);
if (emails[++i]) myFunction(i); // continue when this one is done
});
})(0);
Thanks for everyone's help! I'm making good progress (including taking care of JQuery deprecations!) but have run into a further problem. As I need to hand control back to the browser in order to show the refreshed div as I recurse, I'm calling a setTimeout as follows:
var nextBitOfWork = function () {
return myFunction(email);
};
setTimeout(nextBitOfWork, 0);
where myFunction (which recurses) now returns a promise when it's done doing it's $.ajax call.
If I simply call:
return myFunction(email);
without the setTimeout function construct above, the promise is passed through and all my promises are captured and allow me to get the array output I need and everything works great. But without the setTimeout I don't get the browser refresh. Using it as above I get the div update refresh displaying, but seem to lose the promise and so the script continues and I don't get to fill the array I use to capture values as I recurse.
Any thoughts on how to make sure the setTimeout passes on the promise?
Thanks

Meteor.call() - can it be aborted (stopped before it finishes)?

Let's say we have a grid, and the data comes from the server Meteor.call(). When user clicks on the pagination, or changes sorting or filtering options, some animation is shown, and the method is called. Once data comes back, we hide the animation and render the list. This works great, but we would also like to abort current call if the user changes their mind before data comes back and goes to another page, or changes another filter. Is there a way to do that? In AngularJS I used .abort() on ajax http requests, and that worked perfectly... is Meteor lacking a similar function?
You can use beforeSend option for that:
var requestObject;
HTTP.call("GET", url, {
beforeSend: req => requestObject = req
}, (error, result) => {
// do your stuff ...
});
requestObject.abort();
Note: at the time of writing this is only supported on client.

How to store/remember the data fetched from ajax call?

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));
}

Categories

Resources