How can I use 11ty to generate one page per json file in _data subfolders?

2.2k views Asked by At

I'm trying to use 11ty to generate pages for a movie review site. The structure of my _data folder is as follows.

_data
  movies
    2017
      title1.json
      title2.json
    2018
    2019

I'm hoping for output like this.

_site
  movies
    2017
      title-1.html
      title-2.html
    2018

I would settle for output like this.

_site
  movies
    title-1.html
    title-2.html

But I can't work out how to get anything close! Any ideas? Here's my nunjucks template. TitleWithYear is a property in each .json file.

---
pagination:
    data: movies
    size: 1
    alias: movie
    resolve: keys
permalink: "movies/{{ year??? }}/{{ movie.TitleWithYear | slug }}/index.html"
---
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title></title>
  </head>
  <body>
    <h1>{{ movie.TitleWithYear }}</h1>
  </body>
</html>

Edit: here's a repo showing the problem.

https://github.com/edmondbramhall/11ty-test1

Without the permalink line in, it does work although not with my desired folder structure in the output.

Edit: with Luke's help here's the pagination property I ended up with.

  permalink: "movies/{{ movie.ReleaseYear }}/{{ movie.Id }}-{{ movie.Title | slug }}/index.html"

I also created a global filter for slugify to supply a couple of settings by creating a file .eleventy.js in the root folder of my project, with the following content.

const slugify = require('slugify');
module.exports = function(eleventyConfig) {
    eleventyConfig.addFilter("slug", function(value) {
        return slugify(value, { strict: true, lower: true });
    });
};
2

There are 2 answers

2
Luke Storry On BEST ANSWER

You are using data: movies in your pagination, but the way that eleventy will try to deal with your given data folder structure is to make a list of objects per year folder, then each json file as a child JS object, with the filename as the key:

> console.log(data.movies)

[
  {
    'An-American-Tail_1986_4978': {
      ...
      TitleWithYear: 'An American Tail (1986)',
      Tagline: 'Meet Fievel. In his search to find his family, he discovered America.',
      ...
    },
    'Barbra-Streisand-One-Voice_1986_31683': {
      ...
      TitleWithYear: 'Barbra Streisand: One Voice (1986)',
      Tagline: 'Barbra sings in her backyard for charity!',
      ...
    }
  },

  {
    'Alien-Predators_1985_52318': {
      ...
      TitleWithYear: 'Alien Predators (1985)',
      ...
    }
  }
]

To get the data into a format such that eleventy can paginate it properly, you need to munge it into a single large array of objects. The before callback functionality of eleventy is by far the easiest way of doing that (also possible via changing your data file structure, or by creating a custom collection in config).

This does require your frontmatter to be in a js format instead, but allows you to map for the actual movie objects and then flatten the resultant array.

Below is a full working example of the movie.njk file from your example repo.

---js
{
  pagination:{
    data: "movies",
    before: (data) => data.map(year => Object.values(year)).flat(),
    size: 1,
    alias: "movie",
},
  permalink: "movies/{{ movie.Id }}/",
}
---
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{{ title }}</title>
  </head>
  <body>
    <h1>{{ movie.TitleWithYear }}</h1>
    <p> {{ movie.Tagline}}
  </body>
</html>
0
Nikita Madeev On

Another workaround for structure:

_data
  movies.js
movies
  title1.json
  title2.json
  title3.json
movie.njk

use javascript data files

_data/movies.js:

const fs = require("fs");
const path = require("path");

const moviesFolder = path.resolve(__dirname, "../movies");

const movies = fs
    .readdirSync(moviesFolder)
    .filter(name => path.extname(name) === ".json")
    .map(name => ({
        key: path.parse(name).name,
        ...require(path.join(moviesFolder, name)),
    }));

module.exports = movies;

movie.njk:

---
pagination:
    data: "movies"
    size: 1
    alias: "movie"
permalink: "{{ movie.key }}/"
---
<h1>{{ movie.title }}</h1>