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

Pass data object with page property to data method of JavaScript templates #723

Closed
fredrikekelund opened this issue Oct 8, 2019 · 3 comments

Comments

@fredrikekelund
Copy link

Is your feature request related to a problem? Please describe.
I recently discovered that Eleventy supports layout chaining. What a powerful feature! I put a JavaScript template in front of my Nunjucks template and was able to dynamically create an array of images in each of my pages' directories. I did this by calling fs.readdirSync in the render method of my JS template.

However, I ran into an issue of compilation order, where I tried to access these images arrays in the template of an index page. This didn't work, because the render of those pages had not been run yet.

The data method had been run, though. And after a bit of digging in the Eleventy code, I concluded that it looks like it would be fairly simple to run my existing logic in the data method of my JavaScript templates, if the existing front matter data was passed to that method.

Describe the solution you'd like
Basically, if a change could be made to Template#getData where TemplateContent#getFrontMatterData is called after the other methods for retrieving front matter data and we also pass a merged data object to Template#getData.

Perhaps the entire data object isn't even needed, but the data.page object definitely is, since I use the data.page.inputPath to read the contents of the current directory in my data method.

Describe alternatives you've considered
My suggested solution seems like a very simple change to the existing architecture, as opposed to implementing some solution for controlling the order in which pages are rendered, for instance.

Additional context
Here is my existing JavaScript template code, and an example of what it could look like if this change was implemented.

#642 also seems like a related issue, but in the context of JavaScript data files rather than JavaScript templates.

project.11ty.js

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

exports.data = () => {
    return {
        layout: "project.njk"
    };
};

exports.render = data => {
    let pageDir = path.dirname(data.page.inputPath);
    let imgDir = path.join(pageDir, "images");

    try {
        let images = fs.readdirSync(imgDir);
        images = images.filter(file => file !== ".DS_Store");

        data.images = images;
    } catch (error) {
        // An error will likely be thrown if the `images` directory doesn't
        // exist. In that case, do nothing
    }

    return data.content;
};

If this change was implemented:

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

exports.data = async data => {
    let pageDir = path.dirname(data.page.inputPath);
    let imgDir = path.join(pageDir, "images");
    let images = [];

    try {
        images = await fs.readdir(imgDir);
        images = images.filter(file => file !== ".DS_Store");
    } catch (error) {
        // An error will likely be thrown if the `images` directory doesn't
        // exist. In that case, do nothing
    }

    return {
        layout: "project.njk",
        images
    };
};
@fredrikekelund
Copy link
Author

I thought this over in order to come up with a concrete proposal for what this feature should look like to end users. Currently Eleventy takes the return result of the data() method and merges it with other sources to form the final data object. However, if we implemented my proposed change (pass the existing data object to the data() method), we might as well mutate the data object directly instead of working with a separate returned object. But because we need to deal with backwards compatibility, giving up the current functionality entirely might not be feasible.

So my proposal would be to continue merging the returned result of the data() method with the other sources, but also allow developers to mutate the data object directly. Even if developers return undefined from the data() method, or if they return the data object itself, or if they return a separate object (instead of mutating data directly), the result would still be the same.

Still, we should probably update the docs with a proposed method (return or mutate) to avoid confusion.

@fredrikekelund
Copy link
Author

@zachleat I think this might be a good candidate to consider fixing as part of the work to fix #915. Any thoughts?

@fredrikekelund
Copy link
Author

This was solved very elegantly in 0.11 with the new eleventyComputed feature. I didn't realize that there had been previous discussions around renderData etc. In any case, thanks for implementing what seems like a well thought out approach, @zachleat!

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

1 participant