Eleventy: Possible to get page content in frontmatter?

Created on 5 Dec 2018  Â·  17Comments  Â·  Source: 11ty/eleventy

I set the page's meta description to {{description}}. This description can be found through my frontmatter most of the time. No problems here.

description: 'some-text'

But I have hundreds of blog posts without this description property in the frontmatter. It'll take too long to go through these posts one by one. Is it possible to access page data in the frontmatter (so I can use JavaScript or shortcodes to create the description?).

Here's what I did with my previous static site generator:

  1. Loop through articles
  2. If articles don't have a description tag, look at the contents of the file.
  3. Get everything before <!-- more --> and format them as a description.

Wondering if something like this is possible.

education

Most helpful comment

Adding a comment here for future googlers if you don't mind – I wanted to add an "og:description" meta tag based on the content of the current blog post, but I didn't want it to include the entire content of the page.

I did what @edwardhorsford suggested and looped like so in my layout:

{%- for post in collections.posts -%}
  {%- if post.url == page.url -%}
    <meta property="og:description" content="{{ post.templateContent | striptags | truncate(200) }}">
  {%- endif -%}
{%- endfor -%}

Do let me know if there's a better way. I've tried various things but didn't find anything else that worked.

Should mention that I have two levels of layout – an outer base layout and an inner blog post layout.

All 17 comments

Just saw #135, so I don't think what I mentioned above is possible :(

Curious if you have any solutions though!

Have a look at #179 (very similar tasks, i.e. excerpt creation). It sounds like you’re looking for shortcodes: https://www.11ty.io/docs/shortcodes/.

You loop through your articles like you used to and then you can do something along the lines of:

{% if post.description %}
  {% excerpt post %}
{% endif %}

@kleinfreund Unfortunately what I need is different from what you suggest. I need this for the <meta name="description"> tag. To write the shortcode, the article itself needs access to templateContent. But templateContent is only present when we loop through a collection.

Then you can try to implement the logic in the template that defines the meta tag. There, you could check if the current page has a description. If it has, you’re good to go; if it doesn’t, you call the shortcode with the page variable and try to extract the relevant content.

Does this mean I should get {{page.inputPath}} into the shortcode, parse the inputPath to retrieve said file, parse its content (which contains the frontmatter), and extract the relevant content?

I don't mind doing this. It feels a little hacky to parse through the frontmatter to get the content manually.

Oh, right, I understand. I believed/had hoped that the page object contained some more data. Reading in the file from the inputPath would certainly work, but it’s quite hacky and complicated.

Hm, in my opinion, Eleventy should have a more precise and consistent document model. When processing a file, I should have access to its front matter if present. I get that for documents inside a collection, but not for plain pages. That’s inconsistent. A post is really just a page which happens to be part of a collection. Maybe that can be streamlined in the future.

What I've done in similar situations is to use collections.all, loop through all content until I find one with page.url == collection.url, and then do something with it. It sounds like templateContent contains the information you need to derive a description - this sounds possible to do.

@zellwk you might have luck with using layouts and the content variable here?

Layout file:

`<meta name="description" content="{% if description %}{{description}}{% else %}{{ /* DO SOMETHING WITH THE content VARIABLE */ }}{% endif %}">`

More about layouts: https://www.11ty.io/docs/layouts/

@zachleat Unfortunately this doesn't work because I have nested layouts. The content variable contains too much unnecessary information. Feels cleaner to work directly with the page itself through inputPath.

I did the above (to read the file manually). It's a pretty decent solution afterall.

eleventyConfig.addShortcode('getDesc', (page) => {
    const { inputPath } = page

   // Only read posts
    const isPost = inputPath.includes('/posts/')
    if (!isPost) return ''

   // Get the post. Includes all frontmatter. 
   // Uses front-matter package to parse files. Separates into frontmatter and body. 
    const file = fs.readFileSync(inputPath)
    const contents = file.toString()
    const fm = frontMatter(contents)

   // Do what you need. 

Would it be useful to include the solution above in 11ty docs?

I'm okay with closing this issue, unless @zachleat you wanna hold this open.

@kleinfreund what do you mean by this?

When processing a file, I should have access to its front matter if present.

@kleinfreund what do you mean by this?

When processing a file, I should have access to its front matter if present.

@zachleat If I have this shortcode:

eleventyConfig.addShortcode('log_input', doc => { console.log(doc) });

and I use it inside a collection of posts for each post, I get heaps of data. All sorts of things. (Also, this object needs to lose some weight. It’s massive!). If I log it for a plain page, I get a tiny object with meta data.

I believe, there shouldn’t be a difference in what I get depending on where I access it. One document model for all documents.

@kleinfreund put another way, everything that's available about a page via a collection, should be available from the page itself, yes? Currently the page variable has very little.

I believe, there shouldn’t be a difference in what I get depending on where I access it. One document model for all documents.

I'm down with this

It'd be good to have both frontmatter and content in the page itself.

Going to continue this discussion over at new issue #338 please!

Adding a comment here for future googlers if you don't mind – I wanted to add an "og:description" meta tag based on the content of the current blog post, but I didn't want it to include the entire content of the page.

I did what @edwardhorsford suggested and looped like so in my layout:

{%- for post in collections.posts -%}
  {%- if post.url == page.url -%}
    <meta property="og:description" content="{{ post.templateContent | striptags | truncate(200) }}">
  {%- endif -%}
{%- endfor -%}

Do let me know if there's a better way. I've tried various things but didn't find anything else that worked.

Should mention that I have two levels of layout – an outer base layout and an inner blog post layout.

Was this page helpful?
0 / 5 - 0 ratings