Pagination for module_data

Updated on 20-October-2016 at 10:16 AM

Business Catalyst End of life announcement - find out more details.

The logic

Let's assume you're using module_data to output all your customers. More often than not, there will be a lot of them, so the need to paginate results becomes obvious.

Liquid alows you to paginate results without the need of javascript. It's achieved purely through liquid logic, and the logic is simple:

  • Count the total number of items outputed by your module_data,
  • Calculate the numbers of pages neded based on the limit of items per page that you want to define,
  • Create the pagination links, where each link basically changes the "skip" filter of your module_data instance, causing it to render all items Past that point.

So what that means is that for this standard module_data instance:

{ module_data resource="customers" version="v3" template="" limit="5" skip="0" collection="cst" }

where the output is represented by the first 5 elements in the collection(as per limit parameter set to 5, and skip set to 0), when you click to advance to page 3 of your results, you're basically reloading the module_data instance, with the skip filter updated:

{ module_data resource="customers" version="v3" template="" limit="5" skip="10" collection="cst" }

where the output is now still limited to 5 items, but the ones displayed are actually those from the range 11-15, as per your skip parameter set to 10 (meaning that it jumps over the first 10 results).

Implementation

Choose the page where you want to display the items (catalogs, products, customers, etc. We'll take customers for example). On that page, let's first define the variables that we'll use for interacting with both the pagination and the module_data instance:

{% if globals.get.limit %}
	{% assign limit = globals.get.limit | convert: "number" %}
{% else %}
	{% assign limit = 3 %}
{% endif %}

{% if globals.get.skip %}
	{% assign skip = globals.get.skip | convert: "number" %}
{% else %}
	{% assign skip = 0 %}
{% endif %}

{% assign resource = "customers" %}

So we'll use the variables "limit" and "skip" to modify the module_data instance through the url. If these variables are not passed through the url(usually initial page load), some default values are assigned.

Warning! We also collect the "resource" value through a variable, but make sure this is not actually passed through the URL! Anyone that figures out such an implementation can subsequently gain access to all the resources on your site, reachable through module_data!

Lets's build the module_data instance now:

{ module_data resource="{{ resource }}" version="v3" template="" limit="{{ limit }}" skip="{{ skip }}" collection="cst" }

So you can now see how the module_data is now built to dinamicaly configure itself based on the previously defined variables. This makes it very flexible, as you can change the resource filter to any data-type you want to access, be it products, catalogs, etc.

Next, Let's start building the pagination:


{% assign moduloCheck =  cst.totalItemsCount | modulo :limit %}
{% if moduloCheck == 0 %}
{% assign totalPages = cst.totalItemsCount | divided_by: limit | minus: 1 %}
{% else %}
{% assign totalPages = cst.totalItemsCount | divided_by: limit %}
{% endif %}

{% assign currentPage =  globals.get.skip|divided_by:limit %}

{% comment %} Previous page {% endcomment %}
{% if currentPage > 0 -%}
<a href="{{ globals.get.id }}?skip={{ currentPage | minus: 1 | times:globals.get.limit }}&limit={{ limit }}" title="Previous page"> &lsaquo;</a>
{% endif -%}

{% for pageNum in (0..totalPages) %}
               {% if pageNum == currentPage %}
               {{ pageNum | plus: 1}}
               {% else %}
<a href="{{ globals.get.id }}?skip={{ pageNum | times: globals.get.limit }}&limit={{ limit }}">{{ pageNum | plus: 1 }}</a>
               {% endif %}
{% endfor %}

{% comment %} Next page {% endcomment %}
{% if currentPage != totalPages -%}
<a href="{{ globals.get.id }}?skip={{ currentPage | plus: 1 | times: globals.get.limit }}&limit={{ limit }}" title="Next page"> &rsaquo;</a>
{% endif -%}

<hr />

{{ cst | json }}

<hr />

{% for pageNum in (0..totalPages) %}
               {% if pageNum == currentPage %}
               {{ pageNum | plus: 1 }}
               {% else %}
<a href="{{ globals.get.id }}?skip={{ pageNum|times:globals.get.limit }}&limit={{ limit }}">{{ pageNum | plus: 1 }}</a>
               {% endif %}
{% endfor %}

What we've done above is the following:

  • Counted total items and divided by the assigned limit
    If the remainder is 0, then the number of pages we'll use is (the total number of pages divided by the limit) minus 1, otherwise it's just the total number divided by the limit
  • Build the links of the pagination through a FOR, manually constructing each link from the already available resources, plus {{globals.get.id}}, which outputs the current url without the host
  • Output the module_data collection
  • Repeat the pagination

Here's how this will output on an actual page:

http://liquidexamples.testsecure.com/pagination?skip=0&limit=3

And here's how the code should look on your page:

Conclusion

Before the introduction of Liquid, this would not have been possible without custom coding. Now, using very basic logic and implementation, you get an easy and flexible way to extract, filter and paginate data..