Monday, October 6, 2014

Bootstrap Loading-State Button on AJAX call

The sample code on http://www.tutorialrepublic.com/codelab.php?topic=bootstrap&file=stateful-button shows how to change the state of a button from its normal state to a loading state.

I thought I'd use the use on the code on my previous post to have a button that makes an AJAX request, change the content to 'loading data...' and revert back to its content after the AJAX response.

To see if I can get the effect I've set a five second delay on the backend PHP. If my approach is correct, I should be seeing 'Loading data...during the delay'. The PHP code on the backend is listed below:
    <?php
$time =  array("hr1"=>"1:00", "hr2"=>"2:00", "hr3"=>"3:00", "hr4"=>"4:00", "hr5"=>"5:00", "hr6"=>"6:00", "hr7"=>"7:00", "hr8"=>"8:00", "hr9"=>"9:00", "hr10"=>"10:00", "hr11"=>"11:00", "hr12"=>"12:00", "hr13"=>"13:00", "hr14"=>"14:00", "hr15"=>"15:00", "hr16"=>"16:00", "hr17"=>"17:00", "hr18"=>"18:00", "hr19"=>"19:00", "hr20"=>"20:00", "hr21"=>"21:00", "hr22"=>"22:00", "hr23"=>"23:00", "hr24"=>"00:00");

$action = '';
if(isset($_GET['action'])) $action = $_GET['action'];
$return = array();
switch ($action){
 case "init":
  $length = count($time);
  $return = array("return_param" => $length);
  break;
 case "get-time":
  $return = array("return_param" => ($time));
  break;
 default:
  $return = array("return_param" => "Error");
}
sleep(5);
echo json_encode($return);
?>

On my first attempt, I did not exactly get the effect I wanted. The 'Loading data' on the button reverted back to its initial content before the content was displayed. I realized then that it was the asynchronous part of the AJAX that continued with the execution of the script while it was making a request.

Using anonymous function approach, I was able to sync the change of the button contents during and after AJAX has completed the request and loaded the response.

The button to make the AJAX request has an id of 'loadhrs' and data-loading-text of 'Loading data...'

The adjustments in the code consists of a bind of the button id '#loadhrs' to the click event. The method queue() allows the button to display the loading text while AJAX is pulling the request. To sync with AJAX, I used anonymous function which accepts the button as an object passed as parameter.
The rest of the code is left unchanged.

To test, the html page with the code is loaded.. The button in its initial state displays 'Load Hours'

When the button is clicked, the initial text is replaced with the loading text. The button is likewise in its deactivated state.

After AJAX has pulled the response and displayed the contents, the button is back to its initial state.

The full code is listed below:
<!DOCTYPE html>
<html lang="en">
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="description" content="">
    <meta name="author" content="">

    <title>Bootstrap Pagination</title>
 
 <link href="css/bootstrap.min.css" rel="stylesheet">

    <!-- Just for debugging purposes. Don't actually copy these 2 lines! -->
    <!--[if lt IE 9]><script src="../../assets/js/ie8-responsive-file-warning.js"></script><![endif]-->
    <script src="js/ie-emulation-modes-warning.js"></script>

    <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
    <!--[if lt IE 9]>
      <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
      <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
    <![endif]-->
 <!--nivo stylesheets-->
 <link rel="stylesheet" href="css/nivo-slider.css" type="text/css" />
 <link rel="stylesheet" href="css/default.css" type="text/css" />
 <script src="js/jquery.min.js"></script>
    <script src="js/bootstrap.min.js"></script>
    <script src="js/docs.min.js"></script>
 <script>
  
  $(document).ready(function (){
   var itemsperpage = 5;
   function bindli(itemsperpage, jlength, pages){
    $("ul.pagination li a").bind("click",function(){
      var showPage = this.id;
      if(showPage == 'first') showPage = 1;
      if(showPage == 'last') showPage = pages;
      showli(parseInt(showPage), itemsperpage, jlength)
    });
   }
   function showli(page, itemsperpage, jlength){
    $('ul.pagination li').removeClass('active');
    var offset = page +1;
    var activePage = 'ul.pagination li:nth-child('+offset+')';
    $(activePage).addClass('active');
    $( "div#hours ul li" ).hide();
    var upto = parseInt(page * itemsperpage);
    var start = (upto + 1) - itemsperpage;
    if(upto > jlength) upto = jlength;
    
    for(i = start; i <= upto; i++){
     showitem = 'div#hours li:nth-child('+i+')';
     $(showitem).show();
    }
   }
   function populateli(json, itemsperpage, jlength, pages){
    html = '<ul class="list-group">';
    $.each(json, function (key, data) {
     $.each(data, function (index, data2) {
      html += '<li class="list-group-item">'+data2+'</li>';
     })
    })
    html += '</ul>';
    $('div#hours').html(html);
    showli(1, itemsperpage, jlength);
    bindli(itemsperpage, jlength, pages);
   }
   function displayli(json, itemsperpage){
    var jlength = Object.keys(json.return_param).length;    
    var pages = parseInt(jlength/itemsperpage);
    var remainder = parseInt(jlength % itemsperpage);
    if(remainder > 0) pages++;
    var html = '<li><a href="#" id="first">&laquo;</a></li>';
    for(i = 1; i <= pages; i++) html += '<li><a href="#" id="'+i+'" title="'+i+'">'+i+'</a></li>';
    html += '<li><a href="#" id="last">&raquo;</a></li>';
    $('ul.pagination').html(html);
    populateli(json, itemsperpage, jlength, pages);
   }
   function init(sychit){
    $.ajax({    
     url: 'gethours.php?action=get-time',  
     dataType: 'json',       
     success: function(json) {
      var objButton = $("#loadhrs");
      sychit(objButton);
      displayli(json, itemsperpage);
     },  
     error: function(xhr, ajaxOptions, thrownError) { 
      alert(thrownError + "\r\n" + xhr.statusText + "\r\n" + xhr.responseText + "\r\n" + "Err-01");  
     }  
    });    
   }
   $("#loadhrs").bind("click",function(){
    $(this).button('loading').delay(1000).queue(function() {
     init(function(objButton){
      //this just forces a synch to reset/dequeue the button object
      objButton.button('reset');
      objButton.dequeue();
     });
    });
   });
  })
 </script>
</head>
<body>
 <div class="container">
  <div class="row">
   <div class="col-xs-12">
    <ul class="pagination">     
    </ul>
   </div>
  </div>
  <div class="row">
   <div class="col-xs-12">
    <div id="hours">
    hours
    </div>
   </div>
  </div>
   <button id='loadhrs' type="button" class="btn btn-primary" data-loading-text="Loading data...">Load Hours</button>
 </div>
</body>
</html>

Need help on some web coding task? Let me know about it. Email me at jun.dolor@gmail.com and I'll be more than happy to blog about it with my thoughts.

Hosting of the sample pages were provided for by the following:

No comments:

Post a Comment