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

Order of operation between 11tydata files and collections #2529

Open
cfjedimaster opened this issue Aug 14, 2022 · 9 comments
Open

Order of operation between 11tydata files and collections #2529

cfjedimaster opened this issue Aug 14, 2022 · 9 comments

Comments

@cfjedimaster
Copy link

Describe the bug
Imagine a directory, named posts, and inside there I'm using a data directory file to dynamically set the tags value based on front matter:

module.exports = {
	eleventyComputed: {
		permalink: data => {
			if(data.draft) return false;
		},
		tags: data => {
			if(!data.draft) return 'posts';
			return '';
		}
	}
}

Specifically the idea here is - if I included draft: true in front matter, I don't want to publish it, and if not, I want the post to be assigned to the tags value.

In my front end, I then tried to loop over it:

{% for post in collections.posts %}
a post: {{ post.url }}, title {{ post.data.title}}, tags: {{ post.data.tags }}<br/>
{% endfor %}

And nothing was output. But if I switch to all:

{% for post in collections.all %}
page: {{ post.url }}, title {{ post.data.title}}, tags: {{ post.data.tags }}<br/>
{% endfor %}

I correctly see my blog post and it correctly shows the right value for tags.

I then switched to using a custom collection in .eleventy.js:

eleventyConfig.addCollection("blogPosts", function(collectionApi) {
    return collectionApi.getFilteredByTag("posts");
});

But this also didn't work. I had to switch to this:

eleventyConfig.addCollection("blogPosts", function(collectionApi) {
	let initial = collectionApi.getFilteredByGlob("posts/*.md");
	return  initial.filter(i => {
		return i.data.tags && i.data.tags === 'posts';
	});
});

So I guess the collection stuff ran before the processing of the posts, but if collections are based on reading files and their tags, why wouldn't my posts.11tydata.js run properly there?

To Reproduce

See what I said above. Source may be found here: https://github.com/cfjedimaster/eleventy-demos/tree/master/eleventy_draft_test

Expected behavior
I'd expect collections.tags to have the right results based on the custom logic in my directory data file.

Environment:

  • OS and Version: WSL, latest
  • Eleventy Version [via eleventy --version or npx @11ty/eleventy --version] 1.0.1
@pdehaan
Copy link
Contributor

pdehaan commented Aug 14, 2022

FWIW, when using the draft: true pattern (or permalink: false), I like to also set eleventyExcludeFromCollections: true to prevent unpublished drafts from still loitering around in collections: https://www.11ty.dev/docs/collections/#how-to-exclude-content-from-collections

@pdehaan
Copy link
Contributor

pdehaan commented Aug 14, 2022

Ah, and I think the other issue is that if you dynamically set tags data, it doesn't do the same auto-collection creation as it does if you set tags via front matter.
Let me try cloning your repo and see if we can find a solution/workaround versus my regular unhelpful theories.

@pdehaan
Copy link
Contributor

pdehaan commented Aug 14, 2022

Oh fascinating... I can reproduce the issue, but interestingly it might be a difference between string tags: 'posts' (:-1:) versus array tags: ['posts'] (👍) syntax.

For example, this worked for me:

module.exports = {
	eleventyComputed: {
		permalink: data => {
			if(data.draft) return false;
		},
		tags: data => {
-			if(!data.draft) return 'posts';
-			return '';
+			if(!data.draft) return ['posts'];
+			return [];
		}
	}
}

Although, then I had to modify your .eleventy.js config file to work w/ arrays (using i.data.tags?.includes("posts") as a weak array check w/ null/undefined tags[] support):

module.exports = function(eleventyConfig) {
	//this doesnt work
	eleventyConfig.addCollection("blogPosts2", function(collectionApi) {
		const res = collectionApi.getFilteredByTag("posts");
		console.log(`blogPosts2:`, res.length);
		return res;
	});

	eleventyConfig.addCollection("blogPosts", function(collectionApi) {
		let initial = collectionApi.getFilteredByGlob("posts/*.md");
		return  initial.filter(i => i.data.tags?.includes("posts"));
	});
};

But also, by switching to using the nicer array syntax (IIRC, Eleventy converts strings to arrays behind the scenes eventually anyways), your original "blogPosts" collection worked (renamed to "blogPosts2" to avoid naming conflicts).

TL;DR: Use array syntax when setting tags.

@cfjedimaster
Copy link
Author

So in theory, if I return the array and it works, I do not need the custom collection at all, right?

@cfjedimaster
Copy link
Author

cfjedimaster commented Aug 14, 2022

Nope, so I tried your modified logic for tags, and collections.posts is still empty.

@cfjedimaster
Copy link
Author

Ah, to be clear, creating the custom collection in .eleventy.js DID work. This still feels half way fixed though - why is collections.posts still failing in index.liquid?

@pdehaan
Copy link
Contributor

pdehaan commented Aug 14, 2022

"So in theory, if I return the array and it works, I do not need the custom collection at all, right?"

In my experience, you still need a custom collection in your .eleventy.js config file if you're setting the tags data dynamically. Collections are only auto-created if you set them via front matter. I don't know if that's a bug or gotcha or fact of life; I'm just in the workaround business(tm).

@pdehaan
Copy link
Contributor

pdehaan commented Aug 15, 2022

And just because I still had the tab open and I'm bored on a Sunday, here's my proposed solution:

// posts/posts.11tydata.js
module.exports = {
	eleventyComputed: {
		permalink: data => (data.draft) ? false : data.permalink,
		eleventyExcludeFromCollections: (data) => data.draft,
	},
	tags: ["posts"]
};

Since we're hardcoding the tags: ["posts"] in the directory data file (but not as eleventyComputed data), all the posts should be automatically added to the collections.posts collection; BUT, the eleventyExcludeFromCollections will make sure any truthy draft: true posts aren't included (either in collections.posts or collections.all).

Since that explanation was hot garbage, here's my modified output from your /_site/index.html file:

<h2>collections.posts (1)</h2>
<ol>
  <li>a post: /posts/alpha/, title: alpha, tags: posts</li>
</ol>

<h2>collections.all (2)</h2>
<ol>
  <li>page: /, title: , tags: </li>
  <li>page: /posts/alpha/, title: alpha, tags: posts</li>
</ol>

And that also means I don't need any sneaky .eleventy.js .addCollection() tricks, behold:

// .eleventy.js
module.exports = function(eleventyConfig) {
};

@cfjedimaster
Copy link
Author

All very cool and thank you - going to edit my blog post with a link to this discussion. Still curious to hear though if this would be considered a bug. It feels like a bug to me. :)

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

2 participants