eleventy
eleventy copied to clipboard
Dynamic collection generation based on dynamic tags
Hi there!
I tried several hours to get into this but I cannot get it working. I have global data file where I fetch some data from an api. In this api I receive posts and every single post has an array of tags already baked in.
I tried to include those already given tags into my collection, to generate new collections out of it.
But entering this into my front matter does not work?!
---
pagination:
data: posts
size: 1
alias: post
permalink: "posts/{{ post.slug }}/"
tags: {{ post.tags }}
---
Is there a way to dynamically chain the tags from my api to the tags from the collection?
I tried exactly the same things today. In fact I also tried to do it for the site title as well to push it to my template. Would love to understand how to get this done.
I tried exactly the same things today. In fact I also tried to do it for the site title as well to push it to my template. Would love to understand how to get this done.
It is a little bit frustrating that no one of the pros answers this simple question. This would really open a lot of doors for me right now...
Hi @designbuedchen, please keep in mind, that this community consits of volunteers and is a comparatively small one. Chances are, nobody ran into your use case yet or hasn't checked in this issues list for a while.
I think I found a work around for this problem. I was unable to figure out the tags part but I definitely could get around other parts of the content like the meta 'title' and 'description'.
On the generate.md page I worked with a block like this
{% extends '_includes/layouts/page.njk' %}
{% block ctitle %}{{sampledata.title}}{% endblock %}
and in the page.njk file I added a block to the header section like this.
<title>{% block ctitle %}{% endblock %}</title> . This worked beautifully I must say.
this ofcourse then completely ignore the {{title}} property that I would otherwise normally pass in the front matter. On adding a code like the following:
<title>{% block ctitle %}{% endblock %}{{title}}</title> I would be thrown a title like [object Object] which makes no sense to me but that is how eleventy processed it.
I hope this throws some light and hope we all could figure out a way for this.
[object Object] is the stringified version of a JavaScript object. (You can check with console.log(({}).toString())). I wonder, whether you could use the dump pipe as in <title>{% block ctitle %}{% endblock %}{{ title | dump }}</title> …
Step 1:
A) In the as-is state, I checked with this on the chrome browser.
> console.log(({}).toString())
VM414:1 [object Object]
Step 2:
B) I tried <title>{% block ctitle %}{% endblock %}{{ title | dump }}</title>. The [object Object] issue was sorted out with this. Interestingly, quotes surrounding the variable for pages which had the title in the front matter. Something like this
<title>"foo"</title>
So, I used the nunjuncks filter to replace filter to replace the quotes with nothing. Thus my final output is
<title>{% block ctitle %}{% endblock %}{{title | dump | replace('"','')}}</title>
Fortunately, my issue on the meta tags I sorted for now.
There are 2 problems which have risen from this situation:
- The
collectiondoes not know how to read the page title. A sample filecheck file I have done is attached. - The
layoutvariable that is used commonly for sitemap.xml and checking collections which have a layout, has missed on the front-matter that the page is infact is made from alayout.
Though whatever I have posted does not necessarily answer @designbuedchen
Attached is a sample code which I used to check if the collections took up the title and found missing. page-check.txt
My generate.md file header looks like this now:
---
pagination:
data: brands
size: 1
alias: brand
addAllPagesToCollections: true
permalink: 'brandss/{{prop.websiteLink}}/'
headerHeight: 70vh
tags:
- brand
carousel: true
---
{% extends 'layouts/page.njk' %}
Step 1: A) In the as-is state, I checked with this on the chrome browser.
> console.log(({}).toString())VM414:1 [object Object]
See, same output like you observed before
Step 2: B) I tried
<title>{% block ctitle %}{% endblock %}{{ title | dump }}</title>. The[object Object]issue was sorted out with this. Interestingly, quotes surrounding the variable for pages which had thetitlein the front matter. Something like this<title>"foo"</title>
Hm, then I'm surprised to find this output. I'd expected something like {"foo": "bar"} (that is, an object instead of a string).
Sample code is
<?xml version="1.0" encoding="UTF-8"?>
{%- for page in collections.all %}
<page>
<layout>{{page.data.layout}}</layout>
<url>{{ page.url }}</url>
<title>{{ page.data.title }}</title>
</page>
{%- endfor %}
Thanks. I followed the sitemap.xml code. This is one of them which was generated. Sample output here below which had some pages which had empty titles and layouts.
<page>
<layout></layout>
<url>/brand/dawson-bungalow/</url>
<title></title>
</page>
<page>
{"foo": "bar"} is what it did. You are right.
Thank you for the support anyway @Ryuno-Ki . The primary objective of having the rest of the website populate correctly has been achieved for me.
Okay, what happens with <title>{% block ctitle %}{% endblock %}{{ title.foo }}</title> (or whatever you have instead of „foo”?
@Ryuno-Ki So what I have done is:
A) Pages which are paginated use the block code for obtaining the title. The title.foo gives me nothing anyway as I have not added a title in the front matter of these pages like in post https://github.com/11ty/eleventy/issues/1059#issuecomment-608988030
B) Pages which are non-paginated (built from an MD file) use the title.foo part while block remains empty, something like this
---
title: All Brands for Sale
layout: page
eleventyNavigation:
key: brands
parent: main
---
It is like either or basis.
Hi @designbuedchen , I have figured out a way to handle your issue (and mine as well). To avoid limitations of any functionality in the front-matter or any other part, I created a node module that handles it for me. I am 15 days old to node.js so please do ignore if I have done anything that is considered inefficient. Mustache templates did not really work out for me as I am still too new to it. I have added a public repo for the same to demonstrate how I worked it out. I used a similar technique on converting some json files earlier to a Hugo blog site. The main code is the templateMaker folder.
https://github.com/sachinsancheti1/json-yaml-front-matter
The node module created here has helped me solve my issues of creating pages. I hope this helps you too.
At this point I can only wish there was an easier way to add titles and tags dynamically from a data source using the pagination feature.
I am 15 days old to node.js so please do ignore if I have done anything that is considered inefficient.
Welcome to the JavaScript community then :-) Would you be willing to accept PRs on your repo?
Yes sure. Please feel free. It's open to all.
I'd like to bring this back around to the original discussion about dynamic tags. If one wanted to pull in, say, posts from an API and bring in the tags for each post, is it possible to generate collections from those tags?
Also eager to hear if dynamic tags are possible. I tried to do this using a javascript template and code similar to what's possible with permalink:
function (data) {
return data.item.tags
}
Unfortunately this didn't work, and I was returned an error similar to entry.data.tags is not iterable
I guess I had a similar issue... ended with something like below
assuming that posts is a list of posts
[
{ title: 'first post', tags: ['tag1', 'tag2'] },
{ title: 'second post', tags: ['tag1', 'tag2'] },
{ title: 'third post', tags: ['tag1', 'tag3'] }
]
1. Creating page per post
---
pagination:
data: posts
size: 1
alias: page
addAllPagesToCollections: true
permalink: "blog/{{ page.title | slug }}/"
layout: layout.njk
tags: post
---
<h1>
{{ page.title }}
</h1>
will create
blog/first-page/index.htmlblog/second-page/index.htmlblog/third-page/index.html
2. Creating posts list
---
pagination:
data: posts
size: 2
permalink: blog/{% if pagination.pageNumber %}{{ pagination.pageNumber + 1 }}/{% endif %}
layout: layout.njk
---
<ol>
{% for page in pagination.items %}
<li>
{{ page.title }}
</li>
{% endfor %}
</ol>
will create
blog/index.htmlblog/2/index.html
3. Creating post list per tag
Basic on this issue https://github.com/11ty/eleventy/issues/332 I added new collection tags
eleventy.js
require('dotenv').config()
const lodashChunk = require('lodash.chunk')
module.exports = eleventyConfig => {
eleventyConfig.addCollection('tags', function (collection) {
const posts = collection.getFilteredByTag('post')
.map(item => item.data.page)
const tags = posts
.map(item => item.tags)
.flat()
.filter(Boolean)
const uniqueTags = [...new Set(tags)]
const pageSize = 2
return uniqueTags.map(tag => {
const postsWithTag = posts.filter(post => post.tags.includes(tag))
return lodashChunk(postsWithTag, pageSize)
.map((item, index) => ({
tagName: tag,
pageNumber: index,
pageData: item
}))
}).flat()
})
}
and
---
pagination:
data: collections.tags
size: 1
alias: tag
permalink: /blog/{{ tag.tagName | slug }}/{% if tag.pageNumber %}{{ tag.pageNumber + 1 }}/{% endif %}
---
<ol>
{% for page in tag.pageData %}
<li>
{{ page.title }}
</li>
{% endfor %}
</ol>
will create
blog/tag1/index.htmlblog/tag1/2/index.htmlblog/tag2/index.htmlblog/tag3/index.html