skip to Main Content

I’ve encountered a problem, probably it’s not something huge. So basically I’m using Google Drive API to retrieve files from drive and I want to make pagination, I managed to do next page just fine, but the problem rised when I tried implementing Previous Page button. Imanaged to make it work, but now if there’s 2 pages it works just fine, but if there’s more than 2, for example, if there are 3 pages, if I go to 3rd page, click Previous Page button, it takes me to 2nd page, but then there’s no "previousPageToken" so button is not appearing at all. Should I store tokens in an array or something like that?
Here is the code:


let currentPageToken = null;
let previousPageToken = null;

async function listFiles(folderId, pageToken = null) {
  $('#table_content').empty();
  $('#table_content').remove();
  isListFilesInProgress = true; // Set the flag to indicate listFiles() is in progress

  // Disable the button during listFiles() execution
  $('#show_files_button').prop('disabled', true);

  const request = {
    q: `'${folderId}' in parents`,
    pageSize: 10,
    fields: 'nextPageToken, files(id, name, mimeType, thumbnailLink, webContentLink, modifiedTime)',
    pageToken: pageToken, // Pass the pageToken to retrieve the next page
  };

  gapi.client.drive.files
    .list(request)
    .then(function (response) {
      console.log(response);
      var files = response.result.files;
      if (files && files.length > 0) {
        var table = document.createElement('table');
        table.classList.add('table', 'table-striped');
        table.id = 'table_content';

        var thead = document.createElement('thead');
        var headerRow = document.createElement('tr');
        var header1 = document.createElement('th');
        header1.textContent = 'Thumbnail';
        var header2 = createSortableHeader('Name', 'name');
        var header3 = createSortableHeader('Type', 'mimeType');
        var header4 = createSortableHeader('Last Updated', 'modifiedTime');
        var header5 = document.createElement('th');
        header5.textContent = 'Download';

        headerRow.appendChild(header1);
        headerRow.appendChild(header2);
        headerRow.appendChild(header3);
        headerRow.appendChild(header4);
        headerRow.appendChild(header5);
        thead.appendChild(headerRow);
        table.appendChild(thead);

        var tbody = document.createElement('tbody');
        for (var i = 0; i < files.length; i++) {
          (function (file) {
            var fileName = file.name;
            var mimeType = file.mimeType;
            var thumbnailLink = file.thumbnailLink;
            var downloadLink = file.webContentLink;
            var lastUpdated = new Date(file.modifiedTime).toLocaleString();
            var fileId = file.id;

            var row = document.createElement('tr');

            var thumbnailCell = createThumbnailCell(thumbnailLink, mimeType, fileId);
            row.appendChild(thumbnailCell);

            var nameCell = document.createElement('td');
            nameCell.textContent = fileName;
            row.appendChild(nameCell);

            var typeCell = document.createElement('td');
            typeCell.textContent = mimeType;
            row.appendChild(typeCell);

            var lastUpdatedCell = document.createElement('td');
            lastUpdatedCell.textContent = lastUpdated;
            row.appendChild(lastUpdatedCell);

            if (mimeType != 'application/vnd.google-apps.folder') {
              var downloadCell = document.createElement('td');
              var downloadLinkElem = document.createElement('a');
              downloadLinkElem.href = downloadLink;
              downloadLinkElem.textContent = 'Download';
              downloadLinkElem.classList.add('btn', 'btn-primary');
              downloadLinkElem.target = '_blank';
              downloadCell.appendChild(downloadLinkElem);
              row.appendChild(downloadCell);
            }

            // Add click event listener to navigate into folders
            if (mimeType === 'application/vnd.google-apps.folder') {
              row.classList.add('folder-row');
              row.addEventListener('click', function () {
                var folderId = file.id;
                listFiles(folderId);
              });
            }

            tbody.appendChild(row);
          })(files[i]);
        }

        table.appendChild(tbody);
        document.getElementById('content').appendChild(table);

        // Remove existing Next Page and Previous Page buttons
        $('.next-page-button').remove();
        $('.previous-page-button').remove();

        // Add Next Page button if there is a nextPageToken
        const nextPageToken = response.result.nextPageToken;
        if (nextPageToken) {
          const nextPageButton = document.createElement('button');
          nextPageButton.textContent = 'Next Page';
          nextPageButton.classList.add('btn', 'btn-primary', 'next-page-button');
          nextPageButton.addEventListener('click', function () {
            listFiles(folderId, nextPageToken); // Call listFiles() with the nextPageToken
          });

          document.getElementById('content').appendChild(nextPageButton);
        }

        // Add Previous Page button if there is a pageToken
        if (pageToken) {
          const previousPageButton = document.createElement('button');
          previousPageButton.textContent = 'Previous Page';
          previousPageButton.classList.add('btn', 'btn-primary', 'previous-page-button');
          previousPageButton.addEventListener('click', function () {
            listFiles(folderId); // Call listFiles() without the pageToken to go back to the previous page
          });

          document.getElementById('content').appendChild(previousPageButton);
        }
      }
    })
    .finally(function () {
      isListFilesInProgress = false;
      $('#show_files_button').prop('disabled', false);
    });
}

To sum up, I can only go back one page and it stops there, therefore I can go to next page just fine, because there is always a token for next page.

Thank you in advance!

Edit:

So as suggested I tried keeping page tokens in an array, I push it into an array and on the next function call (when next page is clicked), token is taken from array and I checked with console.log() that token is assigned to previousPageToken, but for some reason on the next page when I press Previous Page button it now just refreshes same page, this is the code of button creation:

const previousPageToken = pageTokens[pageTokens.length - 1];

          console.log('next');
          console.log(nextPageToken);
          console.log('previous');
          console.log(previousPageToken);
          
          if (previousPageToken) {
            const previousPageButton = document.createElement('button');
            previousPageButton.textContent = 'Previous Page';
            previousPageButton.classList.add('btn', 'btn-primary', 'previous-page-button');
            previousPageButton.addEventListener('click', function () {
              listFiles(folderId, previousPageToken); // Call listFiles() with the previousPageToken
            });

            document.getElementById('content').appendChild(previousPageButton);
          }

2

Answers


  1. Chosen as BEST ANSWER

    So I finally managed to solve this problem, so I'll post an answer, maybe someone will find it helpful. First of all, I used array to store current page token, on every page. On "Previous Page" button click, I remove the last element in array. Here's the code:

    
    // Add Previous Page button if there is a previousPageToken
            var previousPageToken = pageTokens[pageTokens.length - 1];
            if (pageTokens.length != 0)
            {
              if (previousPageToken) {
                const previousPageButton = document.createElement('button');
                previousPageButton.textContent = 'Previous Page';
                previousPageButton.classList.add('btn', 'btn-primary', 'previous-page-button');
                previousPageButton.addEventListener('click', function () {
                  pageTokens = pageTokens.slice(0, -1);
                  listFiles(folderId, previousPageToken); // Call listFiles() with the previousPageToken
                });
                document.getElementById('content').appendChild(previousPageButton);
              }
              else if (previousPageToken == null) {
                const previousPageButton = document.createElement('button');
                previousPageButton.textContent = 'Previous Page';
                previousPageButton.classList.add('btn', 'btn-primary', 'previous-page-button');
                previousPageButton.addEventListener('click', function () {
                  pageTokens = [];
                  listFiles(folderId); // Call listFiles()
                });
                document.getElementById('content').appendChild(previousPageButton);
              }
            }
    
            // Add Next Page button if there is a nextPageToken
              const nextPageToken = response.result.nextPageToken;
              if (nextPageToken) {
                const nextPageButton = document.createElement('button');
                nextPageButton.textContent = 'Next Page';
                nextPageButton.classList.add('btn', 'btn-primary', 'next-page-button');
                nextPageButton.addEventListener('click', function () {
                  pageTokens.push(pageToken);
                  listFiles(folderId, nextPageToken); // Call listFiles() with the nextPageToken
                });
    
                document.getElementById('content').appendChild(nextPageButton);
              }
    
    

    Then I've realised, that I can't get first page token for some reason (it was stored as null in array). So I made seperate else if for the case then last element in array is null that meant it was 2nd page. So when I call my function without page token, which on default loads first page. I also made a check if (pageTokens.length != 0) so "Previous Page" button won't render if it is first page.


  2. Testing the overall logic of what you are trying to achieve, the recommended approach is indeed saving the nextPageToken in an array so it can be called later. In theory, just using an array.push() should suffice, this will also allow you to navigate the array index since you’ll know for sure the new ‘nextPageToken’ will be added at the end of the array.
    I can recommend reviewing Tanaike’s answer in this post to use as a reference for the token management:

        async function main(auth) {
      const drive = google.drive({ version: "v3", auth });
    
      const fileList = [];
      let NextPageToken = "";
      do {
        const params = {
          // q: "",  // In this case, this is not required.
          orderBy: "name",
          pageToken: NextPageToken || "",
          pageSize: 1000,
          fields: "nextPageToken, files(id, name)",
        };
        const res = await drive.files.list(params);
        Array.prototype.push.apply(fileList, res.data.files);
        NextPageToken = res.data.nextPageToken;
      } while (NextPageToken);
    
      console.log(fileList.length);  // You can see the number of files here.
    }
    

    As a side note, keep in mind that the nextPageTokens are short-lived, according to my experience and other Stackoverflow users (like Linda Lawton – DaImTo ), they last about an hour

    References:

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search