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
Help requested: How to create separate collections using other frontmatter #259
Comments
|
I've found the internal functions that produce the current tagged collections, but it's beyond my abilities to stitch them together. My knowledge of Non working code: var frontMatterKey = 'photoTags'
getFilteredByTag(tagName) {
return this.getAllSorted().filter(function(item) {
let match = false;
if (!tagName) {
return true;
} else if (Array.isArray(item.data.tags)) {
item.data[frontMatterKey].forEach(tag => {
if (tag === tagName) {
match = true;
}
});
// This branch should no longer be necessary per TemplateContent.cleanupFrontMatterData
} else if (typeof item.data[frontMatterKey] === "string") {
match = item.data[frontMatterKey] === tagName;
}
return match;
});
}
getAllTags() {
let allTags = {};
for (let map of this.map) {
let tags = map.data[frontMatterKey];
if (Array.isArray(tags)) {
for (let tag of tags) {
allTags[tag] = true;
}
// This branch should no longer be necessary per TemplateContent.cleanupFrontMatterData
} else if (tags) {
allTags[tags] = true;
}
}
return Object.keys(allTags);
}
createTemplateMapCopy(filteredMap) {
let copy = [];
for (let map of filteredMap) {
// let mapCopy = lodashClone(map);
// TODO try this instead of lodash.clone
let mapCopy = Object.assign({}, map);
copy.push(mapCopy);
}
return copy;
}
getTaggedCollectionsData() {
let collections = {};
collections.all = this.createTemplateMapCopy(
this.collection.getAllSorted()
);
// debug(`Collection: collections.all size: ${collections.all.length}`);
let tags = this.getAllTags();
for (let tag of tags) {
collections[tag] = this.createTemplateMapCopy(
this.collection.getFilteredByTag(tag)
);
// debug(`Collection: collections.${tag} size: ${collections[tag].length}`);
}
return collections;
}
module.exports = function(collection) {
var newTags = getTaggedCollectionsData();
return newTags;
}; |
|
Whoa you're way overcomplicating this. You can make a collection yourself based on any data value or even a file path using the custom collections API. Look at the second example in this section https://www.11ty.io/docs/collections/#getall() |
|
Thanks for the pointer - I'm still struggling though. I'm am trying to create a custom collection (see end I'm not sure getAll does quite what I want - the way I read it is that it lets me get a set of items that share a common Essentially this is recreating the existing functionality applied to tags (which generate collections) but for another item. I'd like frontmatter like this: And then to end up with collections: Then I would then paginate on |
|
@zachleat or others: anyone able to help with this? I can see how I can make a collection of items based on a key. Eg all items with the key 'photoTag'. I can also make collections that are derived from a key. Eg a list of all I'm struggling to combine the two. For each item in the array I've almost got some code working, but I think my object structure is slightly wonky, and it won't render. |
|
I think I'm having a similar issue, I have two collections Posts and Projects, I'd like to add tags to both which I can do but if I filter the projects collection by a tag I get results form the |
|
I figured this out in the end and made some generic functions so I can create collections sets based on arbitrary frontmatter. Might be a nice feature to add to Eleventy. Eg it does it for tags by default, but can do it to other keys too on request. I think you've got a few options:
FWIW I'm starting to think that the existing collections would be better nested under Below is the code I wrote to create my own 'auto-collections'. As I'm not a developer, I'm guessing it's a bit awkward in places. In particular I'd rather not return it wrapped in an array (which requires me to access the data at const resolvePath = require('object-resolve-path');
const sortAphabetical = (x, y) => {
if(x.toLowerCase() !== y.toLowerCase()) {
x = x.toLowerCase();
y = y.toLowerCase();
}
return x > y ? 1 : (x < y ? -1 : 0);
}
const getKeys = (dataSource, key) => {
let keySet = new Set();
dataSource.forEach( item => {
if (!item.hidden){
var keys = resolvePath(item, String(key));
if( keys ) {
if( typeof keys === "string" ) {
keys = [keys];
}
if( typeof keys === "number" ) {
keys = [String(keys)];
}
for (const value of keys) {
if (!value.startsWith("_")){
keySet.add(value);
}
}
}
}
});
return [...keySet].sort(sortAphabetical);
}
const getItemsByKey = (dataSource, key, value) => {
var result = dataSource.filter( item => {
if (item.hidden){
return false;
}
var keys = resolvePath(item, String(key));
if(keys) {
var match = false;
if( typeof keys === "string" ) {
keys = [keys];
}
if( typeof keys === "number" ) {
keys = [String(keys)];
}
keys.forEach( keyValue => {
if (keyValue == value){
match = true;
};
});
return match;
}
return false;
});
result = result.sort( (a, b) => {
return b.date - a.date;
});
return result;
}
const itemsByKeys = (collection, key) => {
var keySet = {};
var newSet = new Set();
var dataSource = collection.getAll();
// Collections store useful data in collections.data
key = 'data.' + String(key);
keySet = getKeys(dataSource, key);
keySet.forEach( value => {
newSet[value] = getItemsByKey(dataSource, key, value);
});
return [{...newSet}];
}
// -----------
exports.byTrip = collection => {
return itemsByKeys(collection, "trip");
}
exports.bySeries = collection => {
return itemsByKeys(collection, "series");
}Some other things I do: I ignore keys beginning with underscore. I ignore content with I then add these as collections with: eleventyConfig.addCollection("contentByTrip", require("./utils/collections/contentByKey").byTrip);
eleventyConfig.addCollection("contentBySeries", require("./utils/collections/contentByKey").bySeries);I access the data like this: <ul class='horizontal-list'>
{% for tag, tagItems in collections.contentByTrip[0] %}
{% set tagUrl %}{{slug}}{{ tag | slugify }}/{% endset %}
<li><a href="{{ tagUrl }}" class="tag">{{ tag }}</a></li>
{% endfor %}
</ul>Edit I should also say that I'm using For example, I'm creating collection sets based on exif attributes in my images - where the exif data is stored in a very nested way. |
|
Also, hi @cathydutton! 👋 |
|
Hi @edwardhorsford Thanks for your help, I've gone with a new frontmatter data key for the projects collection using the code here https://www.11ty.io/docs/collections/#getall() It creates the filtered projectTags folders as expected, I just cant work out how to display the posts now |
|
Just as a note for me, I did implement something kinda like this for the “Notes” section on my website here https://github.com/zachleat/zachleat.com/tree/master/web/notes It’s managed by a parent collection that holds all the notes under the |
|
See also |
|
Coming here from https://fuzzylogic.me/thoughts/flexible-tag-style-collections-and-pages-for-non-tag-key-in-eleventy/ It looks like providing a generic way to create as much collections as we want, in addition to tags, could be useful quite often. I would need this in multiple projects too. |
|
I have a similar issue I need to resolve. I need to create paginated tag pages for individual collections without any contamination from other collections. My site includes blog posts, case studies and press releases. I'm currently using the zero maintenance tag pages but the problem I have is blog posts, case studies and press releases are all appearing on the same tag listing pages if they share a tag. E.g.
I've looked at the other solutions mentioned in this ticket and while I have a basic understanding of their concepts I'm still really struggling to resolve the issue. I have created separate collections for I have been looking into the pagination before function to see if I could filter one of the collections before the pagination kicks in but I'm not sure this is the right way to do it. Even if I could filter the Does it make sense to create a unique taxonomy in the frontmatter for each collection, e.g. Any advice would be greatly appreciated. |
|
Alright so for me, I needed two isolated collections "posts" and "projects", and it seems that the best way to handle it is to create three collections for each content type: First, you want a collection that will get all items of a single content type. This is the easiest part and I chose to do this by file structure to avoid relying on functional frontmatter or junk tags. eleventyConfig.addCollection("posts", function (collectionAPI) {
return collectionAPI.getFilteredByGlob("./src/posts/*.md");
});Next, you'll want a list of tags used by each content type. This can be used for Zero Maintenance Tag Pages and creating tag lists. I made this as a function, because I'll be using it several times // Return a list of tags in a given collection
function getTagList(collection) {
let tagSet = new Set();
collection.forEach((item) => {
(item.data.tags || []).forEach((tag) => tagSet.add(tag));
});
return [...tagSet];
}Then create the collection with: eleventyConfig.addCollection("postTags", function (collectionAPI) {
let collection = collectionAPI.getFilteredByGlob("./src/posts/*.md");
return getTagList(collection);
});Lastly you'll want a way to list all of the items in a content type with a given tag. This is a bit more complicated and huge credit to Laurence Hughes who figured out the base of my solution. // Return an object with arrays of posts by tag from the provided collection
function createCollectionsByTag(collection) {
// set the result as an object
let resultArrays = {};
// loop through each item in the provided collection
collection.forEach((item) => {
// loop through the tags of each item
item.data.tags.forEach((tag) => {
// If the tag has not already been added to the object, add it as an empty array
if (!resultArrays[tag]) { resultArrays[tag] = []; }
// Add the item to the tag's array
resultArrays[tag].push(item);
});
});
// Return the object containing tags and their arrays of posts
// { tag-name: [post-object, post-object], tag-name: [post-object, post-object] }
return resultArrays;
}Now we can create the actual collection with this: eleventyConfig.addCollection("postsTagged", function (collectionAPI) {
let collection = collectionAPI.getFilteredByGlob("./src/posts/*.md");
return createCollectionsByTag(collection);
});With all the above added, I can now:
Hopefully this helps someone out. I also wrote a blog post going over this for reference |
I'd like to be able to create separate sets of tags - so that I can do two sets of independent tag pages. For example, I plan to have blog posts with tags (and tag pages), and a separate photography section with tags.
I'd also like to use tags for internal IA / logic, but not expose these on tag pages. I know I can filter values, but being able to use other frontmatter properties would be useful.
I think I can achieve this with a custom collection, but it's a little beyond my ability. I wonder if anyone can help?
Something like
collections.photoTagsthat would contain all the tags from the frontmatterphotoTagsand within them each item with that tag. Recreate what already happens with tags for another field.My thinking is that I need to loop over all items, look for a designated data (
photoTags), then create a new collection for each tag found, containing all items that have that tag. Does that sound correct?Alternately, is there a simpler way to do what I want?
The text was updated successfully, but these errors were encountered: