Tutorial 9 - Paginating documents

Your event app currently allows you to add new user accounts, as well as create, edit, delete, sort and filter event documents as a specific user, which only that user can access through the API.

While your app can now filter out and sort documents in a collection retrieved from your Datastore service, large numbers of documents could still be retrieved such that extensive page scrolling is required to access all these documents.

In this tutorial, you will extend the event app functionality (utilising the JS SDK) to implement document pagination features in collection retrieval.

This tutorial takes around 20-30 minutes to complete.

Implementing pagination

Datastore natively provides server-side document pagination capabilities, which allows collections of documents to be paginated as they are retrieved from your Datastore service.

Next and previous pages of documents are managed and handled easily through the JS SDK.

Like server-side sorting and filtering, server-side pagination can be performed much more rapidly than client-side pagination, especially as the number of documents in a collection increases.

Copy the 'tutorial 9' files over to your event-app directory

Copy the next level of your app’s functionality (which handles sorting) from the source tutorial repository over to your event-app directory.

  1. From the event-app directory, copy the content of the step9 directory to the event-app directory:

    $ cp -r ../tutorial/step10/*.* .
  2. Refresh or re-open the initial index.html file in the event-app directory.

Test the updated app

  1. Ensure you have logged in with an account you created in a previous tutorial, and notice the Prev and Next buttons underneath your event app’s list of events.

  2. Ensure you have created at least three events, with at least one event whose Do you have a ticket? value is Yes and the same or another event whose Cost value is between 0 to 50 (dollars), inclusively.

  3. Click the Next and Prev buttons to see your paginated events list flip between the next and previous pages.

    Your event app has its pagination size set to 2, so that only a maximum of two events are shown per page.

    If you reach the end of the list of event documents, the Next button is disabled. Similarly, if you reach the start of the events document list, the Prev button is disabled.

The JS SDK limit() and other pagination methods

The JS SDK includes the following pagination methods (which operate on a collection() method that returns a collection request object), to paginate the list of documents in a collection retrieved from the Datastore service:

  • limit( …​ ) - accepts a mandatory single integer value as its argument, where this value represents the number of documents to present on a page. Once this method is called and its value set, you can then call the following additional pagination methods, all of which do not take any arguments (and also operate directly on the collection() method):

    • hasNextPage() - returns a value of true if, from the collection’s current page, there are still more documents that can be presented on the next page. Otherwise, if there are no more documents towards the end of the collection after the current page, this method returns false.

    • getNextPage() - returns the list of documents on the next page.

    • hasPrevPage() - returns a value of true if, from the collection’s current page, there are still more documents that can be presented on the previous page. Otherwise, if the current page is at the start of the collection (and there are no more documents to present on the previous page), this method returns false.

    • getPrevPage() - returns the list of documents on the previous page.

Examine the revised getEvents function in the main.js file

In the event-app directory, open the main.js file and examine the revised const getEvents …​ function definition towards the top of the file.

The following code snippet shows the JS SDK call to retrieve a list of paginated event documents from a specific user’s events collection.

...

const pageLimit = 2; (1)

...

/**
 * Gets all events for My Event Manager
 */
const getEvents = () => {
    eventsCollection = datastore.collection('users').doc(currentUser.userid).collection('events'); (2)

    // Sorting.
    const sortSettings = document.querySelector('#sortEvents').value;
    if (sortSettings) {
        ...
        // Code for handling the current sort settings.
        ...
    }

    // Filtering.
    if (filterSettings) {
        ...
        // Code for handling the current filter settings.
        ...
    }

    // Pagination.
    eventsCollection.limit(pageLimit); (3)

    eventsCollection.get().then( (4)
        (events) => {
            updatePagination(); (5)
            if (events.length === 0) {
                ...
                // Code to handle what happens when there are no events.
                ...
            } else {
                events.forEach((event) => {
                    printEvent(event);
                });
            }
        }
    );
};
1 Set the pageLimit size to a value of 2.
2 The JS SDK call to create a collection request object, which in turn is used to prepare a GET request for a list of events (belonging to the current user) from your Datastore service.
The final collection() method in this line prepares this collection request on the events collection, within the current user’s user document within the users collection defined in the modified API specification in tutorial 5.
The collection request object is assigned to eventsCollection.
3 Sets pagination on the eventsCollection object with a page size of two documents (per page). This is done by specifying the limit(pageLimit) method on this object, where pageLimit's value is defined near the top of main.js.
4 The remainder of the JS SDK call to get the list of events.
The get() method is finally called on the collection request object (eventsCollection) to send a GET request to your Datastore service that retrieves the events collection’s array, which has been paginated to two event document objects.
This method uses the API’s get definition within /users/{userid}/events, defined in the modified API specification in tutorial 5, to retrieve the requested collection of event documents (associated with your app’s currentUser.userid value) from your Datastore service.
5 The updatePagination() function is defined towards the end of the main.js file, and its purpose is to instruct the app to present appropriate Previous and Next buttons, based on whether previous or next pages exist for the current page.

Examine the getNextPage and getPrevPage functions in the main.js file

Keep the main.js file and examine the new const getNextPage …​ and const getPrevPage function definitions about 1/4 of the way through the file.

The following code snippet shows the JS SDK calls to retrieve the next or previous pages of event documents from the current user’s events collection.

/**
 * Turn to next page.
 */
const getNextPage = () => { (1)
    if (eventsCollection.hasNextPage()) { (2)
        eventsCollection.getNextPage().then((events) => { (3)
            clearEvents();
            updatePagination();
            events.forEach((event) => {
                printEvent(event);
            });
        });
    }
};

/**
 * Turn to prev page.
 */
const getPrevPage = () => { (4)
    if (eventsCollection.hasPrevPage()) { (5)
        eventsCollection.getPrevPage().then((events) => { (6)
            clearEvents();
            updatePagination();
            events.forEach((event) => {
                printEvent(event);
            });
        });
    }
};
1 This JavaScript function is triggered by clicking the Next button in the event app (defined in the index.html below) to retrieve the next page of events.
2 The JS SDK call (hasNextPage()) on the eventsCollection collection request object (defined in the getEvents function above), to check if the current page of events has more documents to present on a next page, and if so …​
3 Get the next page of events as a collection from the Datastore service, using the JS SDK call getNextPage() on this collection request object.
4 Likewise, this JavaScript function is triggered by clicking the Previous button in the event app (defined in the index.html below) to retrieve the previous page of events.
5 The JS SDK call (hasPrevPage()) on the eventsCollection collection request object (defined in the getEvents function above), to check if the current page of events is not at the start of the collection, and has more documents to present on a previous page. If so …​
6 Get the previous page of events as a collection from the Datastore service, using the JS SDK call getPrevPage() on this collection request object.

Examine the pagination functionality in the index.html file

In the event-app directory, open the index.html file and scroll about 1/2 of the way through the file to the start of the <!-- Pagination --> section.

The following code snippet shows how the HTML onclick parameters of the <li/> elements are used to trigger the getPrevPage and getNextPage functions defined in the main.js file above, whenever these buttons are clicked in your event app:

    <!-- Pagination -->
    <nav class="mt-4 pull-right">
        <ul class="pagination pagination-lg">
            <li id="prev-page-li" class="page-item"><a id="prev-page-button" class="page-link" href="#" onclick="getPrevPage()">Previous</a></li>
            <li id="next-page-li" class="page-item"><a id="next-page-button" class="page-link" href="#" onclick="getNextPage()">Next</a></li>
        </ul>
    </nav>
    <!-- end Pagination -->

Congratulations!

You have completed the Datastore tutorial series and should now know how to map the needs of a blueprint to the functionality of a client-side application.

Run the following command to stop all blueprint containers to free up resources until you want to use them again, this command can also resume individual blueprints:

$ dxp datastore simulator pause

If you want continue using the blueprint at a later stage, run following command:

$ dxp datastore simulator resume