0

STEP 1. Background
I currently have a working query system for dynamically updating orders from a backend system. When updates are requested, a table is created, which is then updated as the status of an order is received.
1. I query how many orders are open from MySQL and then create a table based on this info. The key ingredient is a div which has the info on the orders that need to be updated (updateref)
2. When the page is loaded, a script is used to call a page that does the actual order query and possible update.

Order list snippet:
Usage: fetch orders that need to be updated to a table, using PHP and MySQL. There can be from 0 to many updates, typically ca. 15-30 updates/rows/div elements.

            while($row = $results->fetch_assoc()) {
                print '<tr><td>'.$row['reference'].'</td><td><div id="updateref'.$row['reference'].'"><h6>Update queued</h6></div></td></tr>';
            }

Script for updating the list:
Usage: fetches the updated status (using orders_updatestatus.php with the div reference created above) for each div.

<script type="text/javascript">
    // Update status for each order
    $("[id^=updateref]").each(function(index, element) {
        $.ajax({
            'url': "orders_updatestatus.php?reference=" + element.id.replace(/\D/g, ""),
            'success': function(data) {
                element.innerHTML = data;
            }
        });
    });
</script>

Please note that everything up to this point is working great!

STEP 2. What is needed
I need to create a progress bar that would dynamically update when each of the divs are completed (when they have been replaced by orders_updatestatus.php using the script mentioned above)

Progress bar script:
I use a standard Bootstrap progress bar, which is static.

<div class="progress">
  <div class="progress-bar progress-bar-striped" role="progressbar" aria-valuemin="0" aria-valuemax="100" style="width: 10%"></div>
</div>

The above script displays exactly the kind of progress bar that I need, however I need to make it dynamic, as in update the width: xx% when each div element has been updated.

What I'm thinking is that I need a counter for every div element, add those up and then calculate a percentage based on that that would be then fed as the new width to the progress bar.

Also, it seems that a number of people have asked a similar questions related to progress bars, but the other questions didn't have hardly any answers. Therefore any help on this would be greatly appreciated, thank you.

UPDATE - Nearly working!
Wow, thank you so much for the help @Roamer-1888 , I nearly got the dynamic progress bar working!

// Then aggregate `promises` using $.when()
$.when.apply($, promises)
// .then's callback will fire when all the promises have successfully settled.
.then(function() {
// Set up truthy (completed query)
var spans = $( "span" );
var completed = elements.reduce(function(runningTotal, div) {
    if($("h6").find(spans).css( "color", "yellow" )) { // some jQuery expression that returns truthy for "completed", otherwise falsy.
        return runningTotal + 1;
    } else {
        return runningTotal;
    }
}, 0);
var n = $("[id^=updateref]").length; // total number of "order" divs
$progressBar.css({width: (100 * completed / n)+"%"}, 100);
});

As you can see from the code above, I used span and h6 tags as the 'thruthys'.
What is working:
- The progress bar goes from 0-100% (the calculations are working)
Problem / what is not working:
- The updates happen only after every query (~30) have been completed. I added a .css( "color", "yellow" ) as a debug to the code and it confirms that the text color inside the completed queries (h6 tags) change to yellow only after ALL of them have been loaded.

So all in all the only thing I still need is to get updates happening as soon as one of the queries is completed and not the updates (changing of text color to yellow and progress bar update) only happens once. Is there a glitch in the .when.apply perhaps?

UPDATE - one glitch still remains
I added an id tag to the progress-bar and went to check whether the completed -number updates correctly as that is the key ingredient for the progress bar itself.

document.getElementById("progressbar").innerHTML = n + " / " +completed;

The problem is that as soon as the first one of the 29 test orders get updates, the completed -number goes to 29 / 29 and the progress bar jumps to 100%, but it takes a number of seconds for all of the 29 lines to update. This is very peculiar, since there is no way that the if($("h6").find(spans) -code can find any h6 or span tags, since they are created seconds later.

What on earth is going on? :-/

THIRD UPDATE

Well, just like before everything is nearly working with the exception of the glitch described above.

Here's my current code:

   // First, create an array from the jQuery collection returned by $("[id^=updateref]").
    var $progressBar = $('.progress-bar');
    var elements = $("[id^=updateref]").get();
    var n = elements.length;
    // Set up truthy (completed query)
    var spans = $( "span" );
    // Then map `elements` to an array of jQuery promises; each promise deriving an $.ajax() request.
    var promises = elements.map(function(element) {
        return $.ajax({
            'url': "orders_updatestatus.php?reference=" + element.id.replace(/\D/g, ""),
            'success': function(data) {
                element.innerHTML = data;
                // here use `Array.prototype.reduce()` to scan the elements and summate those that are "completed"
                var completed = elements.reduce(function(runningTotal, div) {
                    if ($("span").find("h6").css({"color": "red", "border": "2px solid red"})) { // some jQuery expression that returns truthy for "completed", otherwise falsy.
                        return runningTotal + 1;
                    } else {
                        return runningTotal;
                    }
                }, 0);
                document.getElementById("progressbar").innerHTML = n + " / " +completed;
                $progressBar.css({width: (100 * (completed / n))+"%"});

            }
        });
    });

The code now updates all the 29 order cells at the correct time (border to red), and if I remove the +1 from the runningTotal inside the progress bar, the progress bar does not move.
Therefore I have concluded that the if -statement that calculates the runningTotal is working. However, the completed count goes up before the updates are even created.

And to make sure this isn't a cache issue, I added the following caching parameters to the header, just in case. No luck though.

  header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0");
  header("Cache-Control: post-check=0, pre-check=0", false);
  header("Pragma: no-cache");

@Roamer-1888 , if you or anyone else could spare any more help on this matter, I'd greatly appreciate it. Thank you.

Mr Panther
  • 35
  • 10

1 Answers1

1

Your uncertainty appears to be knowing when a full round of asynchronous updates has completed.

It's hard to give a definitive answer since, despite your best efforts, the question is rather sketchy.

Assuming my understanding of the question to be correct, here's an outline detailing a general approach ...

// First, create an array from the jQuery collection returned by $("[id^=updateref]").
var elements = $("[id^=updateref]").get();
var n = elements.length; // total number of "order" divs
// Then map `elements` to an array of jQuery promises; each promise deriving an $.ajax() request.
var promises = elements.map(function(element) {
    return $.ajax({
        'url': "orders_updatestatus.php?reference=" + element.id.replace(/\D/g, ""),
        'success': function(data) {
            element.innerHTML = data;
        }
    });
});
// Then aggregate `promises` using $.when()
$.when.apply($, promises)
// .then's callback will fire when all the promises have successfully settled.
.then(function() {
    // here use `Array.prototype.reduce()` to scan the elements and summate those that are "completed"
    var completed = elements.reduce(function(runningTotal, div) {
        if($(div).find(...)..???... == ???) { // some jQuery expression that returns truthy for "completed", otherwise falsy.
            return runningTotal + 1;
        } else {
            return runningTotal;
        }
    }, 0);
    $('#myProgressBar')... ??? ...(100 * completed / n); // some jQuery/bootstrap expression that updates the progress bar.
});

This is as far as I can take it from the information available. The missing ...???... parts should be fairly trivial for you to complete. They are just standard synchronous javascript/jQuery.

Edit 1

To update the progress bar as each ajax response arrives, just move the var completed = elements.reduce(...) etc block inside the $.ajax() success handler.

// First, create an array from the jQuery collection returned by $("[id^=updateref]").
var elements = $("[id^=updateref]").get();
var n = elements.length;
// Then map `elements` to an array of jQuery promises; each promise deriving an $.ajax() request.
var promises = elements.map(function(element) {
    return $.ajax({
        'url': "orders_updatestatus.php?reference=" + element.id.replace(/\D/g, ""),
        'success': function(data) {
            element.innerHTML = data;
            // here use `Array.prototype.reduce()` to scan the elements and summate those that are "completed"
            var completed = elements.reduce(function(runningTotal, div) {
                if($("h6").find(spans).css( "color", "yellow" )) { // some jQuery expression that returns truthy for "completed", otherwise falsy.
                    return runningTotal + 1;
                } else {
                    return runningTotal;
                }
            }, 0);
            $progressBar.css({width: (100 * completed / n)+"%"}, 100);
        }
    });
});
// Optionally, aggregate `promises` using $.when()
$.when.apply($, promises)
// .then's callback will fire when all the promises have successfully settled.
.then(function() {
    // if required, display a "complete" message.
});

Edit 2

With luck, it's as simple as ...

// First, create an array from the jQuery collection returned by $("[id^=updateref]").
var elements = $("[id^=updateref]").get();
var n = elements.length;
// Then map `elements` to an array of jQuery promises; each promise deriving an $.ajax() request.
var promises = elements.map(function(element) {
    return $.ajax({
        'url': 'orders_updatestatus.php?reference=' + element.id.replace(/\D/g, ''),
        'success': function(data) {
            element.html(data);
            var n_ = $(elements).find('h6 span').css('color', 'yellow').length;
            $progressBar.css({width: (100 * n_ / n) + '%'}, 100);
        }
    });
});
// Optionally, aggregate `promises` using $.when()
$.when.apply($, promises)
// .then's callback will fire when all the promises have successfully settled.
.then(function() {
    // if required, display a "complete" message.
});

I'm not sure the .find('h6 span') selector is correct as I dont't have sight of the inserted HTML. You may need to debug that bit.

The progress bar will go to 100% only when all elements contain<h6><span>...</span></h6> (or whatever is correct).

Roamer-1888
  • 19,138
  • 5
  • 33
  • 44
  • Thanks for your help @Roamer-1888. It took me a few hours of fiddling with the code, but I got it nearly working at the end. The problem I'm experiencing is that the progress bar only updates at the very end. I'll clarify my question with the code I used. I'd appreciate any more help on the matter. Thanks! – Mr Panther Dec 12 '17 at 22:02
  • thanks for the update. I saw one typo in the code (elemetns.length -> elements.length) and the var =$( "span " ) was also missing. After these updates things are almost working 100%. Now I'm experiencing that the h6 text of the queries does not change color (debug yellow) and the progress bar seems to go to 100% a bit too quickly. Other than that the code is working 100%. – Mr Panther Dec 12 '17 at 23:20
  • Sorry for the typo. Are you sure you don't want the test to be `if($(div).find("h6 span").css("color") == "yellow")`? – Roamer-1888 Dec 13 '17 at 00:09
  • And BTW, I think `.css('color')` in all the major browsers will return an `rgb(...)` value these days. (older versions of IE used to return hex values). The whole issue is explored quite well [here](https://stackoverflow.com/questions/1740700/how-to-get-hex-color-value-rather-than-rgb-value); – Roamer-1888 Dec 13 '17 at 00:19
  • thanks once again for the reply @Roamer-1888. Unfortunately the if($(div).find("h6 ... ) didn't work. I added a " document.getElementById("progressbar").innerHTML = completed; " before the " $progressBar.css({width: (100 * (completed / n))+"%"}); " and it confirms that when the first element is completed, the completed -value jumps immediately to 29 (I'm testing with 29 orders). I spent three hours on this bug today, but unfortunately couldn't find anything wrong with the code. Could you still help me out with this? Much appreciated, thank you! – Mr Panther Dec 16 '17 at 23:01
  • The bug is almost certainly in the `if($(div) .... "yellow")` test. You'll have to bebug it yourself. Without sight of the HTML, I can't really help. – Roamer-1888 Dec 17 '17 at 00:28
  • Hi, great to hear from you. I updated the code to if ($("span").find("h6").css({"color": "red", "border": "2px solid red"})) and now the updates for the cell updates work, but the completed number still updates too quickly I'm afraid. See my third update for details. Thanks! – Mr Panther Dec 17 '17 at 00:31
  • Update @Roamer-1888 , I just realized that in the piece of code var completed = elements.reduce(function(runningTotal, div) the div can be what ever and the code still behaves the same way (even when I replaced the div with SantaClause without quotes, it still worked the same way). Thoughts? – Mr Panther Dec 17 '17 at 00:36
  • Do I understand correctly that the `completed` statistic should reflect the orders' status? If so, what aspect of the HTML can be tested to determine "status". – Roamer-1888 Dec 17 '17 at 00:40
  • Hi @Roamer-1888 , yes you are correct. The progress bar is calculated by $progressBar.css({width: (100 * (completed / n))+"%"}); where the completed are the cells where the status is updated (in this case there's an h6 tag inside the span) and the n comes from var elements = $("[id^=updateref]").get(); var n = elements.length; , which also works correctly. Thanks! – Mr Panther Dec 17 '17 at 01:05
  • So are you saying the difference between updated and not-updated is the presence/absence of an `
    ` tag?
    – Roamer-1888 Dec 17 '17 at 01:16
  • Hi @Roamer-1888, Yes, you are correct. When the update requests are fired, the cell has non-h6 text inside it (saying the status request is in queue). When the response has been received from the backend Warehouse Management System (WMS) via XML requests, the cell is updated with span and h6 tags. Now the problem is that when the first one of the responses is received witht he span and h6 tags, it seems that the completed -value jumps to 29 (out of 29). You can see in the cells that this is not the case and the actual updates take ~5-7 more seconds before the cells are updated. – Mr Panther Dec 17 '17 at 01:47
  • Is each `` nested inside an `
    ` or are they siblings?
    – Roamer-1888 Dec 17 '17 at 02:27
  • Hi @Roamer-1888. I went and simplified the code. When orders_updatestatus.php's are fired, I added a div id=updatedstatus along the response and modified the if statement to simply be if ($("#updatedstatus").css({"color": "red", "border": "2px solid red"}) . The most peculiar thing happened. Out of the 29 orders, about 1-3 orders get their cell borders updated with red, whereas the others do not. Which is more peculiar, this is random, although it seems to be one of the first 5-7 orders being requested. However, the completed count still goes to 29 immediately. Thoughts? – Mr Panther Dec 17 '17 at 10:25
  • I'm on my smartphone right now but will post something when I get back to my desktop machine. – Roamer-1888 Dec 17 '17 at 13:11
  • Did you see Edit 2? – Roamer-1888 Dec 18 '17 at 00:36
  • Whups, apologies @Roamer-1888, I did not! I tried a number of combinations, but it seems that even the orders_updatestatus.php's won't fire if I have the element.html(data) in the code instead of innerHTML. So unfortunately the Edit 2 example did not work :-( – Mr Panther Dec 18 '17 at 23:32
  • It can't be far away. I'm not sure I can do anything more for you. You'll have to debug it. – Roamer-1888 Dec 19 '17 at 01:19