diff --git a/package.json b/package.json index b01e7075b..2af563215 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@11ty/eleventy", - "version": "1.0.0-beta.0", + "version": "1.0.0-twok.0", "description": "Transform a directory of templates into HTML.", "publishConfig": { "access": "public" diff --git a/src/Plugins/Pagination.js b/src/Plugins/Pagination.js index 6fa062d3b..53c58672b 100755 --- a/src/Plugins/Pagination.js +++ b/src/Plugins/Pagination.js @@ -6,98 +6,73 @@ const config = require("../Config"); class PaginationError extends EleventyBaseError {} -class Pagination { - constructor(data) { - this.config = config.getConfig(); +class PaginationGroup { + constructor(options) { + if (!("size" in options)) { + throw new Error("Missing pagination size in front matter data."); + } + this.size = options.size; + this.alias = options.alias; + this.before = options.before; + this.reverse = options.reverse; + this.filter = options.filter; + this.resolve = options.resolve; + this.data = options.data; + this.target = null; + this.items = null; + } - this.setData(data); + _getDataKey() { + return this.data; } - static hasPagination(data) { - return "pagination" in data; + resolveItems(data) { + this.target = this._resolveItems(data); + this.items = this.pageItems(this.target); } - hasPagination() { - if (!this.data) { - throw new Error("Missing `setData` call for Pagination object."); + isFiltered(value) { + if (this.filter) { + let filtered = this.filter; + if (Array.isArray(filtered)) { + return filtered.indexOf(value) > -1; + } + + return filtered === value; } - return Pagination.hasPagination(this.data); + + return false; } - circularReferenceCheck(data) { + circularReferenceCheck(data, template) { if (data.eleventyExcludeFromCollections) { return; } - let key = data.pagination.data; + let key = this.data; let tags = data.tags || []; for (let tag of tags) { if (`collections.${tag}` === key) { throw new PaginationError( `Pagination circular reference${ - this.template ? ` on ${this.template.inputPath}` : "" + template ? ` on ${template.inputPath}` : "" }, data:\`${key}\` iterates over both the \`${tag}\` tag and also supplies pages to that tag.` ); } } } - setData(data) { - this.data = data || {}; - this.target = []; - - if (!this.hasPagination()) { - return; - } - - if (!data.pagination) { - throw new Error( - "Misconfigured pagination data in template front matter (YAML front matter precaution: did you use tabs and not spaces for indentation?)." - ); - } else if (!("size" in data.pagination)) { - throw new Error("Missing pagination size in front matter data."); - } - this.circularReferenceCheck(data); - - this.size = data.pagination.size; - this.alias = data.pagination.alias; - - this.target = this._resolveItems(); - this.items = this.pagedItems; - } - - setTemplate(tmpl) { - this.template = tmpl; - } - - _getDataKey() { - return this.data.pagination.data; - } - doResolveToObjectValues() { - if ("resolve" in this.data.pagination) { - return this.data.pagination.resolve === "values"; + if (this.resolve) { + return this.resolve === "values"; } return false; } - isFiltered(value) { - if ("filter" in this.data.pagination) { - let filtered = this.data.pagination.filter; - if (Array.isArray(filtered)) { - return filtered.indexOf(value) > -1; - } - - return filtered === value; - } - - return false; - } - - _resolveItems() { + _resolveItems(data) { let notFoundValue = "__NOT_FOUND_ERROR__"; let key = this._getDataKey(); - let fullDataSet = lodashGet(this.data, key, notFoundValue); + let fullDataSet = lodashGet(data, key, notFoundValue); if (fullDataSet === notFoundValue) { throw new Error( `Could not resolve pagination key in template data: ${key}` @@ -115,190 +90,288 @@ class Pagination { let result = keys.slice(); - if ( - this.data.pagination.before && - typeof this.data.pagination.before === "function" - ) { + if (this.before && typeof this.before === "function") { // we don’t need to make a copy of this because we already .filter() above - result = this.data.pagination.before(result); + result = this.before(result); } - if (this.data.pagination.reverse === true) { + if (this.reverse === true) { result = result.reverse(); } - if (this.data.pagination.filter) { + if (this.filter) { result = result.filter((value) => !this.isFiltered(value)); } return result; } - get pagedItems() { - if (!this.data) { - throw new Error("Missing `setData` call for Pagination object."); - } - - return lodashChunk(this.target, this.size); - } - - // TODO this name is not good - // “To cancel” means to not write the original root template - cancel() { - return this.hasPagination(); - } - - getPageCount() { - if (!this.hasPagination()) { - return 0; - } - - return this.items.length; + pageItems(data) { + return lodashChunk(data, this.size); } - async getPageTemplates() { - if (!this.data) { - throw new Error("Missing `setData` call for Pagination object."); - } - - if (!this.hasPagination()) { - return []; - } - - if (this.pagesCache) { - return this.pagesCache; + getPaginationData(prevPaginationData) { + const items = this.items; + if (!items) { + throw new Error( + `Cannot get pagination data before items have been resolved.` + ); } - - let pages = []; - let items = this.items; - let tmpl = this.template; - let templates = []; - let links = []; - let hrefs = []; - let overrides = []; - - for (let pageNumber = 0, k = items.length; pageNumber < k; pageNumber++) { - let cloned = tmpl.clone(); - - // TODO maybe also move this permalink additions up into the pagination class - if (pageNumber > 0 && !this.data[this.config.keys.permalink]) { - cloned.setExtraOutputSubdirectory(pageNumber); - } - - templates.push(cloned); - - let override = { - pagination: { - data: this.data.pagination.data, - size: this.data.pagination.size, - alias: this.alias, - - pages: this.size === 1 ? items.map((entry) => entry[0]) : items, - - // See Issue #345 for more examples - page: { - previous: - pageNumber > 0 - ? this.size === 1 - ? items[pageNumber - 1][0] - : items[pageNumber - 1] - : null, - next: - pageNumber < items.length - 1 - ? this.size === 1 - ? items[pageNumber + 1][0] - : items[pageNumber + 1] - : null, - first: items.length + return this.items.map((item, pageNumber, items) => { + const pagination = { + data: this.data, + size: this.size, + alias: this.alias, + + pages: this.size === 1 ? items.map((entry) => entry[0]) : items, + + // See Issue #345 for more examples + page: { + previous: + pageNumber > 0 ? this.size === 1 - ? items[0][0] - : items[0] + ? items[pageNumber - 1][0] + : items[pageNumber - 1] : null, - last: items.length + next: + pageNumber < items.length - 1 ? this.size === 1 - ? items[items.length - 1][0] - : items[items.length - 1] + ? items[pageNumber + 1][0] + : items[pageNumber + 1] : null, - }, - - items: items[pageNumber], - pageNumber: pageNumber, + first: items.length + ? this.size === 1 + ? items[0][0] + : items[0] + : null, + last: items.length + ? this.size === 1 + ? items[items.length - 1][0] + : items[items.length - 1] + : null, }, + + items: items[pageNumber], + pageNumber, + }; + const obj = { + ...prevPaginationData, + pagination: prevPaginationData + ? [...[].concat(prevPaginationData.pagination), pagination] + : pagination, }; if (this.alias) { lodashSet( - override, + obj, this.alias, this.size === 1 ? items[pageNumber][0] : items[pageNumber] ); } - overrides.push(override); - cloned.setPaginationData(override); + return obj; + }); + } +} - // TO DO subdirectory to links if the site doesn’t live at / - let [outputLink, outputHref] = await Promise.all([ - cloned.getOutputLink(), - cloned.getOutputHref(), - ]); - links.push("/" + outputLink); - hrefs.push(outputHref); - } +class Pagination { + constructor(data) { + this.config = config.getConfig(); - // we loop twice to pass in the appropriate prev/next links (already full generated now) - templates.forEach( - function (cloned, pageNumber) { - let pageObj = {}; + this.setData(data); + } - // links are okay but hrefs are better - pageObj.previousPageLink = - pageNumber > 0 ? links[pageNumber - 1] : null; - pageObj.previous = pageObj.previousPageLink; + static hasPagination(data) { + return "pagination" in data; + } - pageObj.nextPageLink = - pageNumber < templates.length - 1 ? links[pageNumber + 1] : null; - pageObj.next = pageObj.nextPageLink; + hasPagination() { + if (!this.data) { + throw new Error("Missing `setData` call for Pagination object."); + } + return Pagination.hasPagination(this.data); + } - pageObj.firstPageLink = links.length > 0 ? links[0] : null; - pageObj.lastPageLink = - links.length > 0 ? links[links.length - 1] : null; + setData(data) { + this.data = data || {}; - pageObj.links = links; - // todo deprecated, consistency with collections and use links instead - pageObj.pageLinks = links; + if (!this.hasPagination()) { + return; + } - // hrefs are better than links - pageObj.previousPageHref = - pageNumber > 0 ? hrefs[pageNumber - 1] : null; - pageObj.nextPageHref = - pageNumber < templates.length - 1 ? hrefs[pageNumber + 1] : null; + if (!data.pagination) { + throw new Error( + "Misconfigured pagination data in template front matter (YAML front matter precaution: did you use tabs and not spaces for indentation?)." + ); + } - pageObj.firstPageHref = hrefs.length > 0 ? hrefs[0] : null; - pageObj.lastPageHref = - hrefs.length > 0 ? hrefs[hrefs.length - 1] : null; + this.dataPaginationGroups = Array.isArray(data.pagination) + ? data.pagination.map((item) => new PaginationGroup(item)) + : [new PaginationGroup(data.pagination)]; - pageObj.hrefs = hrefs; + this.dataPaginationGroups.forEach((group) => { + group.circularReferenceCheck(this.data, this.template); + group.resolveItems(this.data); + }); + } - // better names - pageObj.href = { - previous: pageObj.previousPageHref, - next: pageObj.nextPageHref, - first: pageObj.firstPageHref, - last: pageObj.lastPageHref, - }; + get pagedItems() { + return this.dataPaginationGroups + ? this.dataPaginationGroups.length === 1 + ? this.dataPaginationGroups[0].items + : this.dataPaginationGroups.map((group) => group.items) + : []; + } - Object.assign(overrides[pageNumber].pagination, pageObj); + _resolveItems() { + return this.dataPaginationGroups + ? this.dataPaginationGroups.length === 1 + ? this.dataPaginationGroups[0].target + : this.dataPaginationGroups.map((group) => group.target) + : []; + } - cloned.setPaginationData(overrides[pageNumber]); + setTemplate(tmpl) { + this.template = tmpl; + } - pages.push(cloned); - }.bind(this) + // TODO this name is not good + // “To cancel” means to not write the original root template + cancel() { + return this.hasPagination(); + } + + getPageCount() { + if (!this.hasPagination()) { + return 0; + } + + return this.dataPaginationGroups.reduce( + (acc, curr) => acc * curr.items.length, + 1 ); + } - this.pagesCache = pages; + async getPageTemplates() { + if (!this.data) { + throw new Error("Missing `setData` call for Pagination object."); + } - return pages; + if (!this.hasPagination()) { + return []; + } + + return ( + this.pagesCache || + (this.pagesCache = ( + await Promise.all( + this.dataPaginationGroups + .reduce((overrides, group) => { + return !overrides + ? group.getPaginationData() + : overrides.flatMap((pageData) => + group.getPaginationData(pageData) + ); + }, null) + .map(async (data, ii) => { + // const pageNumber = pageData.pagination.pageNumber; //wont work + let clonedTemplate = this.template.clone(); + + // TODO maybe also move this permalink additions up into the pagination class + if (ii > 0 && !this.data[this.config.keys.permalink]) { + clonedTemplate.setExtraOutputSubdirectory(ii); + } + + clonedTemplate.setPaginationData(data); + + // TO DO subdirectory to links if the site doesn’t live at / + var [outputLink, outputHref] = await Promise.all([ + clonedTemplate.getOutputLink(), + clonedTemplate.getOutputHref(), + ]); + + return { + outputLink: "/" + outputLink, + outputHref, + clonedTemplate, + data, + }; + }) + ) + ).map(({ clonedTemplate, outputHref, outputLink, data }, i, pages) => { + function fixupPagination(pagination, paginationGroup = 0) { + const pageNumber = pagination.pageNumber; + const pagesInGroup = pages + .filter((page) => { + return Array.isArray(page.data.pagination) + ? page.data.pagination.every( + (otherPaginationItem, index) => + index === paginationGroup || + data.pagination[index].pageNumber === + otherPaginationItem.pageNumber + ) + : true; + }) + .sort((a, b) => { + return Array.isArray(data.pagination) + ? a.data.pagination[paginationGroup].pageNumber - + b.data.pagination[paginationGroup].pageNumber + : 0; + }); + const links = pagesInGroup.map((item) => item.outputLink); + const hrefs = pagesInGroup.map((item) => item.outputHref); + let pageObj = {}; + + // links are okay but hrefs are better + pageObj.previous = pageObj.previousPageLink = + pageNumber > 0 ? links[pageNumber - 1] : null; + + pageObj.next = pageObj.nextPageLink = + pageNumber < pages.length - 1 ? links[pageNumber + 1] : null; + + pageObj.firstPageLink = links.length > 0 ? links[0] : null; + pageObj.lastPageLink = + links.length > 0 ? links[links.length - 1] : null; + + // todo pageLinks is deprecated, consistency with collections and use links instead + pageObj.pageLinks = pageObj.links = links; + + // hrefs are better than links + pageObj.previousPageHref = + pageNumber > 0 ? hrefs[pageNumber - 1] : null; + pageObj.nextPageHref = + pageNumber < pages.length - 1 ? hrefs[pageNumber + 1] : null; + + pageObj.firstPageHref = hrefs.length > 0 ? hrefs[0] : null; + pageObj.lastPageHref = + hrefs.length > 0 ? hrefs[hrefs.length - 1] : null; + + pageObj.hrefs = hrefs; + + // better names + pageObj.href = { + previous: pageObj.previousPageHref, + next: pageObj.nextPageHref, + first: pageObj.firstPageHref, + last: pageObj.lastPageHref, + }; + + return Object.assign({}, pagination, pageObj); + } + + if (Array.isArray(data.pagination)) { + data.pagination = data.pagination.map((item, index) => + fixupPagination(item, index) + ); + } else { + data.pagination = fixupPagination(data.pagination); + } + + clonedTemplate.setPaginationData(data); + + return clonedTemplate; + })) + ); } } diff --git a/src/TemplateMap.js b/src/TemplateMap.js index 6d1b81807..d1967eca9 100644 --- a/src/TemplateMap.js +++ b/src/TemplateMap.js @@ -253,7 +253,17 @@ class TemplateMap { if ( counter === 0 || (map.data.pagination && - map.data.pagination.addAllPagesToCollections) + (map.data.pagination.addAllPagesToCollections || + (Array.isArray(map.data.pagination) && + (Array.isArray(page.data.pagination) + ? page.data.pagination.every( + (pagination, ii) => + !pagination.pageNumber || + map.data.pagination[ii].addAllPagesToCollections + ) + : map.data.pagination.every( + (pagination) => pagination.addAllPagesToCollections + ))))) ) { if (!map.data.eleventyExcludeFromCollections) { // TODO do we need .template in collection entries? diff --git a/test/PaginationTest.js b/test/PaginationTest.js index 0d36ab94c..a247a37d9 100644 --- a/test/PaginationTest.js +++ b/test/PaginationTest.js @@ -714,3 +714,232 @@ test("Pagination make sure pageNumber is numeric for {{ pageNumber + 1 }} Issue t.is(templates[0].data.pagination.pageNumber, 0); t.not(templates[0].data.pagination.pageNumber, "0"); }); + +test("Paginate multiple data in frontmatter", async (t) => { + let tmpl = new Template( + "./test/stubs/paged/pagedinlinemultipledata.njk", + "./test/stubs/", + "./dist" + ); + + let data = await tmpl.getData(); + let pages = await tmpl.getTemplates(data); + t.is(pages.length, 4); + + t.is(pages[0].outputPath, "./dist/paged/pagedinlinemultipledata/index.html"); + t.is( + (await pages[0].template.render(pages[0].data)).trim(), + `
  1. item1
  2. item2
  3. item3
  4. item4
+
  1. cool1
  2. cool2
  3. cool3
  4. cool4
` + ); + + t.is( + pages[1].outputPath, + "./dist/paged/pagedinlinemultipledata/1/index.html" + ); + t.is( + (await pages[1].template.render(pages[1].data)).trim(), + `
  1. item1
  2. item2
  3. item3
  4. item4
+
  1. cool5
  2. cool6
  3. cool7
  4. cool8
` + ); + + t.is( + pages[2].outputPath, + "./dist/paged/pagedinlinemultipledata/2/index.html" + ); + t.is( + (await pages[2].template.render(pages[2].data)).trim(), + `
  1. item5
  2. item6
  3. item7
  4. item8
+
  1. cool1
  2. cool2
  3. cool3
  4. cool4
` + ); + + t.is( + pages[3].outputPath, + "./dist/paged/pagedinlinemultipledata/3/index.html" + ); + t.is( + (await pages[3].template.render(pages[3].data)).trim(), + `
  1. item5
  2. item6
  3. item7
  4. item8
+
  1. cool5
  2. cool6
  3. cool7
  4. cool8
` + ); +}); + +test("Alias to multiple page data", async (t) => { + let tmpl = new Template( + "./test/stubs/paged/pagedaliasmultipledata.njk", + "./test/stubs/", + "./dist" + ); + + let data = await tmpl.getData(); + let pages = await tmpl.getTemplates(data); + + t.is(pages.length, 6); + t.is(pages[0].outputPath, "./dist/pagedalias/item1/other1/index.html"); + t.is(pages[1].outputPath, "./dist/pagedalias/item1/other2/index.html"); + t.is(pages[3].outputPath, "./dist/pagedalias/item2/other1/index.html"); + + t.is((await pages[0].template.render(pages[0].data)).trim(), "item1 other1"); + t.is((await pages[1].template.render(pages[1].data)).trim(), "item1 other2"); +}); + +test("Permalink with pagination variables (numeric) multiple data", async (t) => { + let tmpl = new Template( + "./test/stubs/paged/pagedaliasmultipledatalinks.njk", + "./test/stubs/", + "./dist" + ); + + let data = await tmpl.getData(); + let pages = await tmpl.getTemplates(data); + + const pickTestProperties = ({ + pageNumber, + firstPageHref, + firstPageLink, + lastPageHref, + lastPageLink, + nextPageHref, + nextPageLink, + previousPageLink, + previousPageHref, + href, + hrefs, + items, + links, + }) => ({ + pageNumber, + firstPageHref, + firstPageLink, + lastPageHref, + lastPageLink, + nextPageHref, + nextPageLink, + previousPageLink, + previousPageHref, + href, + hrefs, + items, + links, + }); + + t.deepEqual(pages[0].data.pagination.map(pickTestProperties), [ + { + firstPageLink: "/pagedalias/item1/other1/index.html", + firstPageHref: "/pagedalias/item1/other1/", + lastPageLink: "/pagedalias/item3/other1/index.html", + lastPageHref: "/pagedalias/item3/other1/", + nextPageLink: "/pagedalias/item2/other1/index.html", + nextPageHref: "/pagedalias/item2/other1/", + previousPageLink: null, + previousPageHref: null, + pageNumber: 0, + links: [ + "/pagedalias/item1/other1/index.html", + "/pagedalias/item2/other1/index.html", + "/pagedalias/item3/other1/index.html", + ], + items: ["item1"], + hrefs: [ + "/pagedalias/item1/other1/", + "/pagedalias/item2/other1/", + "/pagedalias/item3/other1/", + ], + href: { + first: "/pagedalias/item1/other1/", + last: "/pagedalias/item3/other1/", + next: "/pagedalias/item2/other1/", + previous: null, + }, + }, + { + pageNumber: 0, + firstPageHref: "/pagedalias/item1/other1/", + firstPageLink: "/pagedalias/item1/other1/index.html", + lastPageHref: "/pagedalias/item1/other3/", + lastPageLink: "/pagedalias/item1/other3/index.html", + nextPageHref: "/pagedalias/item1/other2/", + nextPageLink: "/pagedalias/item1/other2/index.html", + previousPageLink: null, + previousPageHref: null, + href: { + first: "/pagedalias/item1/other1/", + last: "/pagedalias/item1/other3/", + next: "/pagedalias/item1/other2/", + previous: null, + }, + hrefs: [ + "/pagedalias/item1/other1/", + "/pagedalias/item1/other2/", + "/pagedalias/item1/other3/", + ], + items: ["other1"], + links: [ + "/pagedalias/item1/other1/index.html", + "/pagedalias/item1/other2/index.html", + "/pagedalias/item1/other3/index.html", + ], + }, + ]); + + t.deepEqual(pages[4].data.pagination.map(pickTestProperties), [ + { + firstPageHref: "/pagedalias/item1/other2/", + firstPageLink: "/pagedalias/item1/other2/index.html", + href: { + first: "/pagedalias/item1/other2/", + last: "/pagedalias/item3/other2/", + next: "/pagedalias/item3/other2/", + previous: "/pagedalias/item1/other2/", + }, + hrefs: [ + "/pagedalias/item1/other2/", + "/pagedalias/item2/other2/", + "/pagedalias/item3/other2/", + ], + items: ["item2"], + lastPageHref: "/pagedalias/item3/other2/", + lastPageLink: "/pagedalias/item3/other2/index.html", + links: [ + "/pagedalias/item1/other2/index.html", + "/pagedalias/item2/other2/index.html", + "/pagedalias/item3/other2/index.html", + ], + nextPageHref: "/pagedalias/item3/other2/", + nextPageLink: "/pagedalias/item3/other2/index.html", + pageNumber: 1, + previousPageHref: "/pagedalias/item1/other2/", + previousPageLink: "/pagedalias/item1/other2/index.html", + }, + { + firstPageHref: "/pagedalias/item2/other1/", + firstPageLink: "/pagedalias/item2/other1/index.html", + href: { + first: "/pagedalias/item2/other1/", + last: "/pagedalias/item2/other3/", + next: "/pagedalias/item2/other3/", + previous: "/pagedalias/item2/other1/", + }, + hrefs: [ + "/pagedalias/item2/other1/", + "/pagedalias/item2/other2/", + "/pagedalias/item2/other3/", + ], + items: ["other2"], + lastPageHref: "/pagedalias/item2/other3/", + lastPageLink: "/pagedalias/item2/other3/index.html", + links: [ + "/pagedalias/item2/other1/index.html", + "/pagedalias/item2/other2/index.html", + "/pagedalias/item2/other3/index.html", + ], + nextPageHref: "/pagedalias/item2/other3/", + nextPageLink: "/pagedalias/item2/other3/index.html", + pageNumber: 1, + previousPageHref: "/pagedalias/item2/other1/", + previousPageLink: "/pagedalias/item2/other1/index.html", + }, + ]); +}); + +test.todo("Test getPageCount with multiple pagination"); diff --git a/test/stubs/paged/pagedaliasmultipledata.njk b/test/stubs/paged/pagedaliasmultipledata.njk new file mode 100644 index 000000000..6e1c252c7 --- /dev/null +++ b/test/stubs/paged/pagedaliasmultipledata.njk @@ -0,0 +1,18 @@ +--- +pagination: + - data: items + size: 1 + alias: font.test + - data: otheritems + size: 1 + alias: other.test +items: + - item1 + - item2 +otheritems: + - other1 + - other2 + - other3 +permalink: pagedalias/{{ font.test }}/{{ other.test }}/index.html +--- +{{ font.test }} {{ other.test }} diff --git a/test/stubs/paged/pagedaliasmultipledatalinks.njk b/test/stubs/paged/pagedaliasmultipledatalinks.njk new file mode 100644 index 000000000..a82667744 --- /dev/null +++ b/test/stubs/paged/pagedaliasmultipledatalinks.njk @@ -0,0 +1,19 @@ +--- +pagination: + - data: things + size: 1 + alias: font.test + - data: otherthings + size: 1 + alias: other.test +things: + - item1 + - item2 + - item3 +otherthings: + - other1 + - other2 + - other3 +permalink: pagedalias/{{ font.test }}/{{ other.test }}/index.html +--- +foo \ No newline at end of file diff --git a/test/stubs/paged/pagedinlinemultipledata.njk b/test/stubs/paged/pagedinlinemultipledata.njk new file mode 100644 index 000000000..693630c3c --- /dev/null +++ b/test/stubs/paged/pagedinlinemultipledata.njk @@ -0,0 +1,27 @@ +--- +pagination: + - data: testdata1 + size: 4 + - data: testdata2 + size: 4 +testdata1: + - item1 + - item2 + - item3 + - item4 + - item5 + - item6 + - item7 + - item8 +testdata2: + - cool1 + - cool2 + - cool3 + - cool4 + - cool5 + - cool6 + - cool7 + - cool8 +--- +
    {% for item in pagination[0].items %}
  1. {{ item }}
  2. {% endfor %}
+
    {% for item in pagination[1].items %}
  1. {{ item }}
  2. {% endfor %}