11ty / eleventy

A simpler site generator. Transforms a directory of templates (of varying types) into HTML.
https://www.11ty.dev/
MIT License
17.2k stars 493 forks source link

How to access "data" object inside non-11ty.js shortcodes? #1154

Closed sullyD64 closed 4 years ago

sullyD64 commented 4 years ago

Hello! I'm a novice, so I apologize in advance if this sounds like a silly question.

My project uses 11ty.js templates, like @reubenlillie does in both his site and his all-11ty.js starter project. Because of this, I use Javascript Template Functions as a mechanism to include partials in my template. Inside that partial, I want to access a global data file, so i write a function which can be called from a 11ty.js template using ${this.myPartial(data)}, and inside myPartial I access the global data file using data.myDataFile.

This question arises because I'm experimenting using multiple template engines.

I used addShortcode instead of addJavascriptFunction so that I get an universal shortcode I can use in other template languages, like nunjucks. The problem is that I think I have no means to reference the data object inside said templates.

I can explain better with an example:

Say I have this global data file _data/site.json:

{
  "author": "John"
}

My config:

// .eleventy.js
eleventyConfig.addShortcode('authorInfo', function(data) {
  return `Hello! My name is ${data.site.author}`
}

My template:

//_includes/layouts/myJsTemplate.11ty.js
exports.data = {
  layout: 'layouts/base'
}

exports.render = (data) => {
  return `<div class="author-info">${this.authorInfo(data)}</div>`
}

Now, since authorInfo is an Universal Shortcode, I want to use it in a nunjucks template.

<!--_includes/layouts/myNjkTemplate.njk-->
---
layout: 'layouts/base'
--- 
<div class="author-info">{% authorInfo ??? %}</div>

But as expected, I can't access the data object inside this template. I could write {% authorInfo site %} because site is visibile since it's a global data file, but this means I would have to:

This might be OK in this little example, but if I'm using more than one global data file or I want to access data anywhere else in the cascade (like template or directory data), I would have to explicitly declare all the keys in every Javascript Template Function that I intend to use and refactor all my existing 11ty.js templates just to fumble up with Nunjucks.

I know that in nunjucks i can use {% include 'myPartial' %} to include partials, but this only works between nunjucks templates and in my case I'm using javascript to define partials.

So this brings to the final question: is there a way - even hacky or dirty - to access the data file inside a nunjucks template? Is data similar to the nunjucks context?

Thank you in advance.

sullyD64 commented 4 years ago

I think I've found a solution that suits my case using the new eleventyComputed feature.

The trick is to add an eleventyComputed data property in a Javascript Data File which is populated with the data itself. I put this data file in the root directory of my content:

//content/content.11tydata.js
module.exports = {
  eleventyComputed: {
     data: (data) => data,
  }
}

By doing this I can use the root data object in my non-11ty.js shortcodes the same way I would do with my existing Javascript Template Functions. Any existing 11ty.js template works the same, since they keep accessing the "original" data object.

<!-- _includes/layouts/myNjkTemplate.njk -->
---
layout: 'layouts/base'
--- 
<div class="author-info">{% authorInfo data %}</div>

Note that I could have used any other label instead of data, e.g. rootData or context. I choose data to uniform my .11ty.js and my .njk templates.

zachleat commented 4 years ago

Glad you got it working!