Eleventy: Pagination with .11ty.js templates

Created on 14 Mar 2020  ·  12Comments  ·  Source: 11ty/eleventy

The pagination examples are excellent for Nunjucks and Liquid, but translating them to .11ty.js form is beyond me. My use case is listing my site's posts. I am using the prescribed way of handling the front matter (desired result is pagination with a reverse-order list of posts, five listed per page)...

exports.data = {
  layout: 'layouts/_default/base.11ty.js',
  tags: '- nav',
  navtitle: 'Posts',
  title: 'Posts',
  pagination: { 
    data: 'collections.post',
    size: '5',
    reverse: 'true',
    alias: 'posts'
  }
}

...but can't quite get anywhere from there. I can make a list of all the pages readily enough, but quite frankly am not JS-savvy enough to translate these items (shown below) from the "Put it all together" example from Nunjucks to .11ty.js:

{%- for pageEntry in pagination.pages %}
    <li><a href="{{ pagination.hrefs[ loop.index0 ] }}"{% if page.url == pagination.hrefs[ loop.index0 ] %} aria-current="page"{% endif %}>Page {{ loop.index }}</a></li>
{%- endfor %}

Any thoughts, anyone? And feel free to chide me on my JS ignorance. :-)

documentation

All 12 comments

@brycewray Sorry, I haven't used 11ty.js templates so this might not be the best approach (I usually channel @reubenlillie's excellent https://gitlab.com/reubenlillie/reubenlillie.com site for 11ty template inspiration), but curious if something like this might work:

https://github.com/pdehaan/11ty-11tyjs-test/blob/3a56057f950abe212bb8316f1e9ca62542c3d1f3/src/pages/_users.11ty.js#L24-L32

I didn't have a collection or something handy, so I created a src/_data/users.js file that I paginated and then set dynamic permalinks+nav.

But I think that correctly sets the ARIA props for the nav:

<main>
  <h2>Hello Earl!</h2>
  <nav>
    <ul>
      <li><a href="/cast/">Page 1</a></li>
      <li><a href="/cast/2/">Page 2</a></li>
      <li><a href="/cast/3/" aria-current="page">Page 3</a></li>
    </ul>
  </nav>
</main>
<footer>nav</footer>

If you build the site, you should get an output directory like this:

tree www/

www/
├── cast/
│   ├── index.html
│   ├── 2/
│   │   └── index.html
│   └── 3/
│       └── index.html
└── pages
    └── index.html

4 directories, 4 files

@pdehaan Thanks! Have been following the @reubenlillie repo as well (excellent, indeed), but didn’t find any pagination happening there, hence my cry for help. :) Will give your suggestion a try.

@pdehaan On my first attempt to run this (after minimally altering the user-related things to be post-related), got an error message that pageEntry is undefined, and that does square with the color-coding I'm seeing in VS Code. Where does pageEntry come from?

pageEntry was just the arbitrary variable name in my .map() loop:

  render(data) {
    const nav = data.pagination.pages.map((pageEntry, idx) => {
      const href = data.pagination.hrefs[idx];
      const ariaCurrent = data.page.url === data.pagination.hrefs[idx] ? `aria-current="page"` : "";
      return `<li><a href="${ href }" ${ariaCurrent}>Page ${ idx + 1 }</a></li>`;
    }).join("");
    const userString = data.user.map(user => user.name);
    return `<h2>Hello ${userString.join(" and ")}!</h2><nav><ul>${ nav }</ul></nav>`;
  }

Is your repo public? Might be easier if I clone your repo and look at your code.

Thanks again!

After further tinkering, think I've decided to concede defeat and continue letting the posts list template be Nunjucks. Also, wasn't able to figure out how to translate Max Böck's amazingly effective webmentions code from Nunjucks to .11ty.js, so am going to return to using Talkyard for commenting. Still, the other pages will now be .11ty.js-based. Perhaps in time I'll learn enough to go fully .11ty.js-based, but for now this will have to do. I did notice a tweet from @reubenlillie saying even he still has a smidge of non-.11ty.js-based templating in his site (although he's working on resolving that), so that makes me feel slightly better. 😃

@brycewray and @pdehaan,

First of all, thank you for your patience with me chiming in here. Second, thank you for your repeated kind endorsements about my site. (I’m still figuring out “best practices” for *.11ty.js-style Eleventy, but I’m glad it’s been helpful to you.)

Now, I was able to make this snippet of a chained archive layout based on the Nunjucks example in the docs using previous–next navigation.

// _includes/layouts/archive.11ty.js
/**
 * @file Defines the chained template for a collection archive page
 * @author Reuben L. Lillie <[email protected]>
 * @see {@link https://www.11ty.dev/docs/layouts/#layout-chaining Layout chaining in 11ty}
 * @see {@link https://www.11ty.dev/docs/pagination/#paging-a-collection Paging a collection in 11ty}
 */

/**
 * Acts as front matter in JavaScript templates
 */
exports.data = {
  layout: 'layouts/base'
}

/**
 * The content of the template
 * @method
 * @name render()
 * @param {Object} data 11ty’s data object
 * @return {String} HTML template literal
 * @see {@link https://www.11ty.dev/docs/pagination/ Pagination in 11ty}
 */
exports.render = function (data) {
  return `<header>
      <h2>${data.title}</h2>
    </header>
    ${data.content}
    <section id="archive">
      <ul>
        ${data.pagination.items.map(function (item) {
          return `<li>
              <a href="${item.data.page.url}">${item.data.title}</a>
            </li>`
          }).join('')
        }
      </ul>
    <nav aria-labelledby="pagination">
      <h2 id="pagination" hidden>Pagination</h2>
      ${data.pagination.href.previous
        ? `<a href="${data.pagination.href.previous}">
            ${data.pagination.reverse
              ? 'Previous'
              : 'Next'
            }
          </a>`
        : ''
      }
      ${data.pagination.href.next
        ? `<a href="${data.pagination.href.next}">
            ${data.pagination.reverse
              ? 'Next'
              : 'Previous'
            }
          </a>`
        : ''
      }
    </nav>
    </section>`
}

Then, as far as the Nunjucks example for pagination navigation which @brycewray mentioned above, the crucial detail is that data.pagination.pages is an _array_. So, I would actually reach for Array.prototype.map() with the optional index parameter in the callback (i.e., loop.index0 in Nunjucks, so I’ve learned) _instead of a for...in loop_ as in Nunjucks (see Array.prototype.map() in MDN):

/**
 * Eleventy pagination navigation starter example
 * translated from Nunjucks to vanilla JavaScript
 * @see {@link https://www.11ty.dev/docs/pagination/nav/#starter-example 11ty docs}
 */
exports.render = function (data) {
  return `<nav aria-labelledby="my-pagination">
    <h2 id="my-pagination">This is my Pagination</h2>
    <ol>
      ${data.pagination.pages.map(function (item, index) {
        return `<li>
          <a href="${data.pagination.hrefs[index]}"
            ${data.pagination.hrefs[index]
              ? 'aria-current="page"'
              : ''
            }>Page ${index + 1}</a>
        </li>`
      }).join('')}
   </ol>
  </nav>`
}

@pdehaan, you bring up a good (related?) stylistic point. Is caching variables that are only used once in a given return statement better for readability and/or performance, especially for newcomers to *.11ty.js? In other words, is a list of variables followed by a pithy return statement easier to read or faster than a longer, top-to-bottom return statement? Or is it simply a matter of taste? (PS, I still think taste is important.)

@reubenlillie @pdehaan As I noted on Twitter, I decided to revert to all-.njk templating (and webmentions) for now since that better fits my limited skill set, but I remain intrigued by the potential advantages of an all-.11ty.js approach. Perhaps someday, someone will have time to flesh out the official Eleventy docs for each of the supported templating languages rather than just supplying intriguing examples for some of them — notably Nunjucks and Liquid, it would seem. That would be incredibly helpful for folks like me who find an all-JS approach a wonderful concept but lack (in my case) the training and expertise to make it roll.

@brycewray, that settles it. I am resolved, with blessing from @zachleat, to translate more examples in the docs into JavaScript. And, if you're looking for recommendations to dig into vanilla JavaScript, I cannot speak too highly about the stuff @cferdinandi publishes.

… the stuff @cferdinandi publishes.

Being at https://gomakethings.com/ and sundry URLs referenced within that realm.

@brycewray, that settles it. I am resolved, with blessing from @zachleat, to translate more examples in the docs into JavaScript. And, if you're looking for recommendations to dig into vanilla JavaScript, I cannot speak too highly about the stuff @cferdinandi publishes.

Thanks for that (and for the subsequent link you provided)! I have also found https://eloquentjavascript.net to be extraordinarily helpful, although it obviously requires more free time than one might have, especially when the need is simply to find a quick answer to “Aw, man, now how do I do that?” without risking the ego deflation that comes from asking a question on Stack Overflow that is remotely similar to something somebody asked eons ago. :)

@brycewray, yes—that self-respecting reference is
https://vanillajstoolkit.com/ by @cferdinandi, followed by
https://gomakethings.com/search/.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

robdodson picture robdodson  ·  3Comments

AjitZero picture AjitZero  ·  3Comments

smaimon picture smaimon  ·  3Comments

veleek picture veleek  ·  3Comments

DirtyF picture DirtyF  ·  3Comments