Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pagination is very confusing. #455

Closed
freshyill opened this issue Mar 18, 2019 · 21 comments
Closed

Pagination is very confusing. #455

freshyill opened this issue Mar 18, 2019 · 21 comments

Comments

@freshyill
Copy link

freshyill commented Mar 18, 2019

I'm finding pagination to be very confusing with Eleventy. There's two parts to this that I see:

  1. Actually splitting something up into multiple pages
  2. Generating links to all of those pages (even just "next/previous" links would be something).

Breaking list up into pages:

  1. I've tried out lots of examples. Using this in my blog.njk:
pagination:
  data: collections.posts
  size: 25
  alias: posts
<ol>
{% for post in posts %}
  <li><a href="{{ post.url | url }}">{{ post.data.title }}</a></li>
{% endfor %}
</ol>

It sort of works, but…

  1. What does size mean? I have absolutely no idea. When I specify 1, nothing happens. When I specify 25, I get 18 results, offset by 18. I'm so confused.
  2. This doesn't produce /blog/2, /blog/3, etc. Granted, this code doesn't look like it should, but what I pasted above is a variation on the same code I see over and over again, including in the docs. What code would produce the actual paginated pages?

Pagination element

In #345, @ajmalafif asked about how to create an actual pagination element, which is definitely one part of this.

50046160-88879180-00d9-11e9-8e9c-74333a2c1159

  1. Is what @ajmalafif is asking about even possible? He closed this under the assumption that it's not, but nobody ever confirmed it. I don't think this is a very good resolution..
  2. If it is possible, can somebody please just share some working code showing how to produce something like what @ajmalafif was trying to produce?
    5, If it's not possible, can someone share something similar? How close to this can you get?

@ajmalafif did you ever find a solution?

@ajmalafif
Copy link

hey @freshyill, I don't find a solution. I literally given up on it as you can find in some of the issues I posted, been asking quite a long time ago to no avail.

I am reiterating my site in Gatsby but to be honest, has the same challenges as well (but at least I know its possible given others achieved it).

@kleinfreund
Copy link
Contributor

size refers to the number of items per page.

I use the following for my pagination and that works perfectly fine. I don’t know how pagination aliases work but it could be that you need to access posts.items instead of just posts, but I don’t know. I don’t use an alias in the below example.

pagination:
  data: collections.posts
  size: 5
  reverse: true
{%- for post in pagination.items -%}
<article class="post">
  <a href="{{ post.link }}">{{ post.data.title }}</a>
</article>
{%- endfor -%}

{% if pagination.pageLinks.length > 1 %}
<nav class="pagination">
  {% if pagination.previousPageLink %}
    <a class="pagination__item" href="{{ pagination.previousPageHref | url }}">Newer Articles</a>
  {% else %}
    <span class="pagination__item">Newer Articles</span>
  {% endif %}

  {% if pagination.nextPageLink %}
    <a class="pagination__item" href="{{ pagination.nextPageHref | url }}">Older Articles</a>
  {% else %}
    <span class="pagination__item">Older Articles</span>
  {% endif %}
</nav>
{% endif %}

@freshyill
Copy link
Author

@kleinfreund Your code works for me, just like all the other examples work. When I specify size: 5, that produces three links. (Why not five?). The next/previous links appear, but they have nothing to link to.

Can you tell me how you generate the actual pages?

@freshyill
Copy link
Author

@ajmalafif Thanks for checking back in! Hopefully we can both find a resolution soon!

@kleinfreund
Copy link
Contributor

kleinfreund commented Mar 18, 2019

@freshyill One requirement I haven’t mentioned: You need to have documents that are actually part of the collections posts (in my example that is).

I have a directory called posts and in it, there is a posts.json file that defines the layout and permalink style for all documents in the posts directory plus:

posts/posts.json:

{
  "tags": [
    "posts"
  ]
}

For me, all posts are part of that collection now and are built into a paginated blog index. No additional steps are necessary for me. I think your case would benefit from a test repository with which we can reporduce this issue.

@freshyill
Copy link
Author

@kleinfreund I do have that as well. All of this is based on the eleventy-netlify-boilerplate, and I haven't really done anything at all to the structure of things yet. I'll try to put up a repo tonight.

@kleinfreund
Copy link
Contributor

kleinfreund commented Mar 19, 2019

Please run eleventy like this for a global installation of Eleventy ...

DEBUG=Eleventy* eleventy

... or like this for a local installation of Eleventy:

DEBUG=Eleventy* ./node_modules/@11ty/eleventy/cmd.js

(These commands work in a terminal on Linux or macOS. In Windows, they would look different.)

This will print a lot of debugging information. I’m interested in lines starting with this string:

Eleventy:TemplateMap Collection: 

In particular, yours should contain one like this:

Eleventy:TemplateMap Collection: collections.post size: 4 +0ms

(That’s what https://github.com/danurbanowicz/eleventy-netlify-boilerplate produces if freshly cloned)

Note that the collection is called post in this project, not posts.

@freshyill
Copy link
Author

@kleinfreund I've determined that part of my issue is due to slugs The slug filter apparently doesn't strip a lot of characters, including apostrophes, exclamation marks, and periods(see #278).

I implemented the slugify filter on #278 and now size behaves better.

However, it still isn't properly generating the paginated blog pages or the pagination links. My main blog page compiles incorrectly, apparently because of something with the content, but it's inconsistent from one rebuild to the next. I'll ignore this for now and give the content a closer look soon separately.

The blog/1, blog/2, etc. pages aren't generated at all. Can you see anything wrong with this?

---
layout: layouts/base.njk
section: blog
permalink: blog/{% if pagination.pageNumber > 0 %}page-{{ pagination.pageNumber }}/{% endif %}index.html
pagination:
  data: collections.posts
  size: 25
  reverse: true
---

<article class="article article--index">
  <div class="article__inner">
    <h1>{{ title }}</h1>

    {{ layoutContent | safe }}
  </div>
</article>

{%- for post in pagination.items -%}
  <article class="post">
    <a href="{{ post.url }}">{{ post.data.title }}</a>
  </article>
{%- endfor -%}

<pre>{{ pagination.previousPageHref | url }}{{ pagination.nextPageHref  | url }}</pre>

{% if pagination.pageLinks.length > 1 %}
<nav class="pagination">
  {% if pagination.previousPageLink %}
    <a class="pagination__item" href="{{ pagination.previousPageHref | url }}">Newer Articles</a>
  {% else %}
    <span class="pagination__item">Newer Articles</span>
  {% endif %}

  {% if pagination.nextPageLink %}

    <a class="pagination__item" href="{{ pagination.nextPageHref | url }}">Older Articles</a>
  {% else %}
    <span class="pagination__item">Older Articles</span>
  {% endif %}
</nav>
{% endif %}

@kleinfreund
Copy link
Contributor

The template itself looks fine to me. However, I’m curious as to the front matter’s permalink key. I would suspect that this is creating problems.

Let’s go step by step. Have you run the command I posted? In particular, I want to know how many documents Eleventy thinks are in you collection. Can you post the output, please?

@d2s
Copy link
Contributor

d2s commented Mar 19, 2019

... or like this for a local installation of Eleventy:

DEBUG=Eleventy* ./node_modules/@11ty/eleventy/cmd.js

(These commands work in a terminal on Linux or macOS. In Windows, they would look different.)

cross-env package helps to run scripts that set and use environment variables, across platforms. Have been using it for running Eleventy on a local Windows machine. More details about cross-platform configuration from: josephdyer/skeleventy#2 (comment)

@freshyill
Copy link
Author

@kleinfreund I believe that's taken from the docs. I had originally had blog/index.html as the permalink and it didn't work either.

@freshyill
Copy link
Author

Correction, the permalink pattern is not from the docs, it's from here: #76 (comment)

@freshyill
Copy link
Author

@kleinfreund

Sorry I missed your comment with the debug instructions. The debug gives me this: Eleventy:TemplateMap Collection: collections.posts size: 391 +311ms

391 is the correct number of posts, for what it's worth.

@freshyill
Copy link
Author

A detail I noticed from the debug: It's writing blog/index.html over and over again. The exact number of times depends on the size I specify in pagination. If I tell it size: 10, It writes the page 40 times. If I tell it size: 100, it writes it four times. This is correct for having 391 posts.

It seems like I have something just slightly off. Instead of getting /blog/1, /blog/2, etc., I just get /blog 40 times.

I sort of suspect that my rendering errors are actually due to this overwriting. I don't know the specifics of how Node overwrites files, but it seems plausible based on what it actually looks like.

@zachleat
Copy link
Member

If blog/ is getting written a bunch of times it means something is wrong with your permalink:

permalink: blog/{% if pagination.pageNumber > 0 %}page-{{ pagination.pageNumber }}/{% endif %}index.html

You mentioned using slug in your size? That doesn’t make sense to me. Why would the slug filter apply to your pagination size?

@freshyill
Copy link
Author

For what it's worth, I used the slug in the permalink. I believe the issue there was that I had multiple posts that generated the same permalink… which ended up causing issues that affected how size was handled. It doesn't really matter. At any rate, I improved the post permalink structure and that part is fine now.

I just got this sorted out. I do think the docs are a bit unclear and could probably benefit from some more real-world examples. As they are now, seem a little too simplified. Most people probably aren't defining data in the front matter and looping over that for pagination.

I think I got hung up on the fact that the permalink is handed in the markdown frontmatter, but the pagination is in the template.

Here's roughly what I ended up with.

blog.njk:

---
layout: base
section: blog
pagination:
  data: collections.post
  size: 6
  alias: postslist
---
{% for post in postslist | reverse %}
  {% include "components/teaser.njk" %}
{% endfor %}

blog.md

---
layout: blog
title: Archive
tags:
  - nav
  - footnav
navtitle: Archive
date: 2019-01-11
permalink: archive/{% if pagination.pageNumber > 0 %}{{ pagination.pageNumber }}/{% endif %}index.html
---
… page content…

This gives me archive/index.html, /archive/1/index.html, and so on.

@clockshark
Copy link

clockshark commented Dec 6, 2019

We battled with this for weeks and i ended up finally coming up with this @freshyill

 <!-- front matter -->
---
permalink: "/blog/{% if pagination.pageNumber > 0 %}{{ pagination.pageNumber + 1 }}/{% endif %}index.html"
pagination:
  data: collections.blogposts
  size: 12
  alias: posts
---
 <!-- post loop -->
{% for post in posts %}
 {{ post.data.title }}
{% endfor %}

        <!-- Pagination links -->
        <div class="page-selector">
          {% if pagination.previousPageHref %}
            <a href="{{ pagination.previousPageHref }}" class="page-numbers">
              Previous
            </a>
          {% else %}
            <span class="page-numbers">Previous</span>
          {% endif %}
          <span class="page-numbers ">
            Page: {{ pagination.pageNumber + 1 }} of {{ pagination.links | length  }}
          </span>
          {% if pagination.nextPageHref %}
            <a href="{{ pagination.nextPageHref }}" class="page-numbers">Next</a>
          {% else %}
            <span class="page-numbers">Next</span>
          {% endif %}
        </div>


@clockshark
Copy link

@zachleat I need to know how to paginate the tags as well as the entire post collection? any direction??

@mjanthony
Copy link

Here's how I solved this issue, pretty hacky but it works!

<div class="article-pagination" aria-label="btn-toolbar">

    {# PREV #}
    <div class="btn-group pr-24">
        <a class="btn btn-primary {{ '' if pagination.href.previous else 'disabled' }}" href="{{ pagination.href.previous }}">Prev</a>
    </div>

    <div class="btn-group">

        {%- for pageEntry in pagination.pages %}

            {# FIRST PAGE #}
            {%- if pagination.href.first == pagination.hrefs[loop.index0]
                and page.url != pagination.hrefs[loop.index0 + 2]
                and page.url != pagination.hrefs[loop.index0 + 1]
                and page.url != pagination.hrefs[loop.index0] %}

                <a class="btn btn-link" href="{{ pagination.hrefs[ loop.index0 ] }}">{{ loop.index }}</a>

                {# ELLIPSIS FOR GAP IN PAGES #}
                {%- if page.url != pagination.hrefs[loop.index0 + 3] %}
                    <span class="btn disabled">&hellip;</span>
                {%- endif %}

            {%- endif %}

            {# CURRENT PAGE ± 2 #}
            {%- if page.url == pagination.hrefs[loop.index0 + 2]
                or page.url == pagination.hrefs[loop.index0 + 1]
                or page.url == pagination.hrefs[loop.index0]
                or page.url == pagination.hrefs[loop.index0 - 1]
                or page.url == pagination.hrefs[loop.index0 - 2] %}

                <a class="btn {{ 'btn-secondary disabled' if page.url == pagination.hrefs[loop.index0] else 'btn-link' }}" href="{{ pagination.hrefs[ loop.index0 ] }}">{{ loop.index }}</a>

            {%- endif %}

            {# LAST PAGE #}
            {%- if pagination.href.last == pagination.hrefs[loop.index0]
                and page.url != pagination.hrefs[loop.index0]
                and page.url != pagination.hrefs[loop.index0 - 1]
                and page.url != pagination.hrefs[loop.index0 - 2] %}

                {# ELLIPSIS FOR GAP IN PAGES #}
                {%- if page.url != pagination.hrefs[loop.index0 - 3] %}
                    <span class="btn disabled">&hellip;</span>
                {%- endif %}

                <a class="btn btn-link" href="{{ pagination.hrefs[ loop.index0 ] }}">{{ loop.index }}</a>

            {%- endif %}

        {%- endfor %}

    </div>

    {# NEXT #}
    <div class="btn-group pl-24">
        <a class="btn btn-primary {{ '' if pagination.href.next else 'disabled' }}" href="{{ pagination.href.next }}">Next</a>
    </div>
    
</div>

@jeffchiou
Copy link

jeffchiou commented Nov 22, 2020

Hey @ajmalafif and @freshyill,
I'm currently rewriting my website with 11ty and found this issue. Here's my implementation for the numerical part with emphasis for the current page.

{%- for pageUrl in pagination.hrefs %}
  {%- set isCurrentPage = pagination.pageNumber == loop.index0 -%}
  {%- if isCurrentPage %}<strong>{%- endif %}

  {%- if isCurrentPage 
  or loop.first 
  or loop.last
  or pagination.pageNumber in [loop.index0-1, loop.index0+1] -%}

    {%- if loop.last and pagination.pageNumber < (loop.length - 3) -%}
      ...
    {%- endif -%}

    <a href="{{ pageUrl }}">{{ loop.index }}</a>

    {%- if loop.first and pagination.pageNumber > 2 -%}
      ...
    {%- endif -%}

  {%- endif -%}
  
  {%- if isCurrentPage %}</strong>{%- endif %}
{%- endfor %}

I don't have many posts yet so the navigation window is only 3 pages wide. To make it 5 pages wide, change

  • [loop.index0-1, loop.index0+1] to [loop.index0-2, loop.index0-1, loop.index0+1, loop.index0+2]
  • pagination.pageNumber < (loop.length - 3) to pagination.pageNumber < (loop.length - 4)
  • pagination.pageNumber > 2 to pagination.pageNumber > 3

Style separately; you might have to deal with whitespace differently since I wrote it within a markdown file.

@shirooo39
Copy link

I agree, the pagination is so confusing

I'm dumping all of my post by using {% for post in collections.sortedPost %}, which I don't think it's the correct way, but the docs about pagination are so confusing for newbies.
it's linking to another page on the docs, which the next page is link to yet another page.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants