Heyo đź‘‹
I tried looking through the issue tracker and didn't turn up anything that quite matched what I'm trying to do. Apologies if this has been answered before...
I'm creating a table of contents page, and using the slug for each post as an identifier. I have my YAML organized like so:
---
layout: path
topics:
- title: Understand the diverse needs of users
guides:
- what-is-accessibility
- title: Make your site keyboard accessible
guides:
- keyboard-access
- use-semantic-html
- control-focus-with-tabindex
- style-focus
- title: Understand semantics and basic screen reader support
guides:
- semantics-and-screen-readers
- headings-and-landmarks
- labels-and-text-alternatives
---
I'd like to be able to use the slug of each guide to find its data object and pull out its title property. That would let me do something like this:
<!-- GUIDES FOR TOPIC -->
<ul class="guides">
{% for slug in topic.guides %}
<li>
<a href="./{{slug}}">
// somehow access the YAML for the guide that lives
// at `slug` so I can display guide.title
</a>
</li>
{% endfor %}
</ul>
Is my best bet to just write a shortcode to lookup the posts and use readFileSync to grab their contents and parse their YAML?
Another alternative I thought of was to have all of the posts be in a collection, and filter the collection by slug until I find the right object. But that felt a little weird to me because I have to remember to add each post to this collection, even though I already have a unique identifier that I could use to look them up.
Another alternative I thought of was to have all of the posts be in a collection, and filter the collection by slug until I find the right object. But that felt a little weird to me because I have to remember to add each post to this collection, even though I already have a unique identifier that I could use to look them up.
Could you create a collection of all posts (without adding them individually) like this:
module.exports = (function(eleventyConfig) {
// ...
eleventyConfig.addCollection("posts", function(collection) {
return collection.getAll().filter(function(item) {
return item.data.tags == "post";
// OR, this is my approach, because I give every
// piece of content a `content_type`:
// return item.data.content_type == "post";
});
});
// ...
});
and then filter the collection by slug, like you suggested? I'm sure my implementation is more verbose than it needs to be, but maybe something like this:
<!-- GUIDES FOR TOPIC -->
<ul class="guides">
{% for slug in topic.guides %}
<li>
<a href="./{{slug}}">
{% for post in collections.posts %}
{% if post.title == slug %}
{{ post.title }}
{% endif %}
{% endfor %}
</a>
</li>
{% endfor %}
</ul>
That seems like a good idea, I'll give it a shot and report back. Thank you
Paul!
On Wed, Feb 6, 2019, 6:41 AM Paul Shryock notifications@github.com wrote:
Another alternative I thought of was to have all of the posts be in a
collection, and filter the collection by slug until I find the right
object. But that felt a little weird to me because I have to remember to
add each post to this collection, even though I already have a unique
identifier that I could use to look them up.Could you create a collection of all posts (without adding them
individually) like this:module.exports = (function(eleventyConfig) {
// ...
eleventyConfig.addCollection("posts", function(collection) {
return collection.getAll().filter(function(item) {
return item.data.tags == "post";
// OR, this is my approach, because I give every
// piece of content acontent_type:
// return item.data.content_type == "post";
});
});// ...
});
and then filter the collection by slug, like you suggested? I'm sure my
implementation is more verbose than it needs to be, but maybe something
like this:{% for slug in topic.guides %}
- {% for post in collections.posts %} {% if post.title == slug %} {{ post.title }} {% endif %} {% endfor %}
{% endfor %}—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
https://github.com/11ty/eleventy/issues/399#issuecomment-461046992, or mute
the thread
https://github.com/notifications/unsubscribe-auth/ABBFDWWYVgGXP8qiot8cpS8ETFYNf9Qlks5vKumsgaJpZM4af1dC
.
Couple of things here:
return item.data.tags == "post"; I believe tags is always going to be an array here, we normalize them to be arrays (even if they are stored as a string in your front matter)collections.all special collection provided for free https://www.11ty.io/docs/collections/#the-special-all-collectionBigger picture, it kinda reminds me of this, a bit: https://github.com/11ty/eleventy/issues/189#issuecomment-411544737 though that is focused on using includes and this wouldn’t. It kinda makes me thing we should just have a shortcode that sugars a reference to an entry in collections.all by its inputPath. Though you wanted fileSlug, those wouldn’t be unique IDs? Maybe we could be smarter about this and search both unless there are conflicts—not sure. I think this feature might be a good path forward for a lot of things. Would that solve your use case @robdodson? cc @tylersticka (would work well with existing feature permalink: false to bypass writing a file for these)
(Also related: #250, #254)
Hmm, after trying to write out a bikeshed for this, nevermind @tylersticka this wouldn’t handle your issue, which needs customizable data to be passed into the component. Ignore your cc here sorry 🤡
@zachleat If it makes you feel better about cc'ing me, on a recent project we wrote a simple helper that allowed us to filter a collection of posts by a URL segment. We used this so we could show dynamic collections of a particular content type by parent without having to make collections for each one.
I'm sure this has like a zillion edge cases and reasons why it shouldn't work, and it doesn't solve the problem of finding a _specific_ post by slug, but it worked for our needs so I thought I'd share.
/**
* This is used to easily display groups of navigation in Eleventy templates
* without requiring new collections for every folder, by letting us filter
* larger groups by URL segments.
*
* It searches for any match in the URL (rather than an exact prefix) so that
* collections of patterns in `/patterns/example` and documentation in
* * `/docs/example` can be grouped together in navigation.
*
* @param {Array} collection An Eleventy collection.
* @param {String} segment A URL segment to search for.
* @returns {Array} The collection filtered by the URL segment.
*/
module.exports = function(collection, segment) {
return collection.filter(page => page.url.indexOf(`/${segment}/`) !== -1);
};
{% assign patterns = collections.pattern | items_with_url_segment: page.fileSlug %}
{% for pattern in patterns %}
{% include 'styleguide/pattern' %}
{% endfor %}
@zachleat sorry for the delay on this.
Having a shortcode to grab a post by inputPath seems like it could work. I'll probably write something similar in the meantime to see if it meets our needs.
@zachleat
My thinking was to call collection.getAll() and then create a dictionary of all items, keyed off of their inputPath. Then in my shortcode I could do something like this:
eleventyConfig.addShortcode('getItemByPath', function(inputPath) {
// Somehow dict would be in scope here.
// Maybe I could hang it off of a global object?
return dict[inputPath];
});
I'm not sure where in the eleventy lifecycle I could build dict or what would be the best way to put it in scope for my shortcodes.
I think I have a solution for this. It's a bit hacky but it seems to work so far.
https://gist.github.com/robdodson/c2d3c4a6bf6bf9962893760c5585a3eb
Basically I create a fake collection so I can get access to collection.getAll(). In the function that creates that collection I pass collection.getAll to a memoize() function that lives in the same module as my findBySlug() function.
Elsewhere in my shortcodes I can just do findBySlug('some-slug').
Since we want to have unique slugs for every post on our site, this setup works for me. I'm sure others could adapt it to their needs or memoize more than 1 collection type.
Note that is very similar to a Liquid example {% link %} tag I built in Issue 544: https://github.com/11ty/eleventy/issues/544#issuecomment-496752551
If we want this in core, is it okay to move this into the enhancement queue? Seems like we have some userspace options here as a short term solution. (Please complain if not, because I am going to queue it for now)
This repository is now using lodash style issue management for enhancements. This means enhancement issues will now be closed instead of leaving them open.
View the enhancement backlog here. Don’t forget to upvote the top comment with 👍!
I'm doing something similar to find specific items from a collection, tying in netlify CMS's relational fields.
In my site, pages have many work_streams, identified by fileSlug. With the relation field, an array of fileSlugs for the assigned work_streams are added to the page's data.
I've added a getCollectionItemsBySlug filter.
module.exports = function getCollectionItemsBySlug(collection, slugArray = []) {
function fileSlugInSlugArray(item) {
return slugArray.includes(item.fileSlug);
}
return collection.filter(fileSlugInSlugArray);
};
Then in my nunjucks page template, I call it on the sorted_work_streams collection to just get the one-or-more work_streams for the page:
{% set workstreamListItems = collections.sorted_work_streams | getCollectionItemsBySlug(work_streams) %}
{% include "partials/components/work-stream-list.njk" %}
Hope that helps someone :)
Most helpful comment
@zachleat If it makes you feel better about cc'ing me, on a recent project we wrote a simple helper that allowed us to filter a collection of posts by a URL segment. We used this so we could show dynamic collections of a particular content type by parent without having to make collections for each one.
I'm sure this has like a zillion edge cases and reasons why it shouldn't work, and it doesn't solve the problem of finding a _specific_ post by slug, but it worked for our needs so I thought I'd share.