1
votes

I'm trying to create a table with multi-pages using spreadsheet data as follow:

In the code.gs, I get the data from the spreadsheet like this:

function getData() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Sheet1");
var data = sheet.getRange(2, 1, sheet.getLastRow()-1, 3).getValues();
return data;
}

Then in the HTML file, the above data is generated in the table like this:

<table id="myTable">

<thead>
<tr class="header">
<th>First Name</th>
<th>Second Name</th>
<th>Third Name</th>
</tr>
</thead>

<tbody id="table-body">
</tbody>
        
</table>

<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded',function(){ generateTableBody(data); });
google.script.run.withSuccessHandler(generateTableBody).getData();

function generateTableBody(dataArray) {
var table = document.getElementById("myTable");
var tbody = document.getElementById("table-body");

dataArray.forEach(function(r){
var row = document.createElement("tr");
var col1 = document.createElement("td");
col1.textContent = r[0];
var col2 = document.createElement("td");
col2.textContent = r[1];
var col3 = document.createElement("td");
col3.textContent = r[2];
row.appendChild(col1);
row.appendChild(col2);
row.appendChild(col3);
tbody.appendChild(row);
});

}
</script>

Next, I added the below script to the HTML file in order to create the pages out of the data at which max. 10 rows per each page:

<script>
$('#myTable').each(function() {

var currentPage = 0;
var numPerPage = 10;
var $table = $(this);

$table.bind('repaginate', function() {
$table.find('tbody tr').hide().slice(currentPage * numPerPage, (currentPage + 1) * (numPerPage-1)).show();
});

$table.trigger('repaginate');

var numRows = $table.find('tbody tr').length;
var numPages = Math.ceil(numRows / numPerPage);
var $pager = $('<div class="pager"></div>');

for (var page = 0; page < numPages; page++) {
$('<span class="page-number"></span>').text(page + 1).bind('click', {
newPage: page
}, function(event) {
currentPage = event.data['newPage'];
$table.trigger('repaginate');
$(this).addClass('active').siblings().removeClass('active');
}).appendTo($pager).addClass('clickable');
}

$pager.insertBefore($table).find('span.page-number:first').addClass('active');

});
</script>

At this point, I have 2 issues:

this part $table.bind('repaginate', function() { $table.find('tbody tr').hide().slice(currentPage * numPerPage, (currentPage + 1) * (numPerPage-1)).show();}); is supposed to return all the rows under the table body, hide all, then slice to show only the first 10 rows in the first page but it does not work (and my guessing is that because the generateTableBody(dataArray) function is completed after the query function was completed and so $table.find('tbody tr') returned nothing first then the generateTableBody(dataArray) returned all the rows down to the interface instead of just the first 10 rows

Based on that, I added this part $("tbody#table-body").find("tr:gt(0)").hide().slice(0, 9).show(); to the bottom of the generateTableBody(dataArray) function and the issue was solved, but

The second issue was var numRows = $table.find('tbody tr').length; also returned nothing most probably for the same reason, so I tried to populate the number of rows like that:

In the code.ge I added:

function fetchLastRow(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Sheet1");
var lr = sheet.getLastRow()-1;
return lr;
}

And in the HTML file I added a hidden input to read the lr returned like this:

<input type="hidden" id="numRows">
<script>
window.addEventListener('load', populate());
function populate(){ google.script.run.withSuccessHandler(displayValue).fetchLastRow(); }
function displayValue(lr){ document.getElementById("numRows").value=lr; }
</script>

I was able to read the number of rows successfully in the hidden input, so the next step was that:

var numRows = document.getElementById('numRows').value;

or

var numRows = document.getElementById('numRows').getValue();

or

var numRows = document.getElementById('numRows').innerHTML;

but (in all cases) the var numRows = 0 (I think that because of the window.addEventListener('load', populate()); is also completed after the query)

So I think if I was able to run the query function at last (after generating table data and reading the max active rows in the spreadsheet), that will solve the two issues explained.

1
I don't know about anyone else but I'm not going to read this unless you make the effort to create minimal reproducible example - Cooper

1 Answers

2
votes

I believe your goal as follows.

  • You want to retrieve the values from Google Spreadsheet with google.script.run.withSuccessHandler(generateTableBody).getData(), and create a table.
  • You want to separate the table by the pages with 10 items.

Modification points:

  • I think that your Google Apps Script is correct.
  • At HTML&Javascript side,
    • I think that an error occurs at document.addEventListener('DOMContentLoaded',function(){ generateTableBody(data); }).
    • In your case, I think that it is required to execute $('#myTable').each(function() {###}) after generateTableBody(). In this case, put $('#myTable').each(function() {###}) in a function like nextStep() and execute it after generateTableBody().

When above points are reflected to your script, it becomes as follows.

Modified script:

<table id="myTable">

  <thead>
    <tr class="header">
      <th>First Name</th>
      <th>Second Name</th>
      <th>Third Name</th>
    </tr>
  </thead>

  <tbody id="table-body">
  </tbody>

</table>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
<script>
  //document.addEventListener('DOMContentLoaded',function(){ generateTableBody(data); });  // Removed
  google.script.run.withSuccessHandler(generateTableBody).getData();

  function generateTableBody(dataArray) {
    var table = document.getElementById("myTable");
    var tbody = document.getElementById("table-body");
    dataArray.forEach(function(r) {
      var row = document.createElement("tr");
      var col1 = document.createElement("td");
      col1.textContent = r[0];
      var col2 = document.createElement("td");
      col2.textContent = r[1];
      var col3 = document.createElement("td");
      col3.textContent = r[2];
      row.appendChild(col1);
      row.appendChild(col2);
      row.appendChild(col3);
      tbody.appendChild(row);
    });
    nextStep(); // Added
  }

  function nextStep() { // Added

    // Below script is yours.
    $('#myTable').each(function() {
      var currentPage = 0;
      var numPerPage = 10;
      var $table = $(this);
      $table.bind('repaginate', function() {
        $table.find('tbody tr').hide().slice(currentPage * numPerPage, (currentPage + 1) * (numPerPage - 1)).show();
      });
      $table.trigger('repaginate');
      var numRows = $table.find('tbody tr').length;
      var numPages = Math.ceil(numRows / numPerPage);
      var $pager = $('<div class="pager"></div>');
      for (var page = 0; page < numPages; page++) {
        $('<span class="page-number"></span>').text(page + 1).bind('click', {
          newPage: page
        }, function(event) {
          currentPage = event.data['newPage'];
          $table.trigger('repaginate');
          $(this).addClass('active').siblings().removeClass('active');
        }).appendTo($pager).addClass('clickable');
      }
      $pager.insertBefore($table).find('span.page-number:first').addClass('active');
    });
  }
</script>
  • By above modification, when the HTML is opened, the separate pages can be seen. When you click the page number at the top left, the page is moved.