Tag-Based Callbacks
Trio scans your composites for HTML tags that are decorated with the data-trio-callback attribute and asynchronously calls the JavaScript Node modules they name, passing them a single object whose properties can be used along with cheerio's selector API to augment the composites with dynamic content.
- file location: root/source/callbacks
- file type/content: .js/JavaScript Node Module
Trio treats your project's markup as plain text, meaning there is no DOM involved, and parses it using the jQuery-like selector API provided by the awesome cheerio OS library. jQuery's selector API documentation can be found at jquery.com.
Declaring Tag-Based Callbacks In Your Markup
To declare tag-based callbacks in your markup you decorate the tags you want to target in template, fragment and include assets with data-trio-callback attributes, and assign them the names of the callbacks located in the root/source/callbacks folder:
<ul data-trio-callback="blogtaglist"></ul>
The callbacks can then reference the decorated tags to augment them with dynamic content using the $tag property. See Implementing Tag Based Callbacks below for more information.
Implementing Tag-Based Callbacks
Each module must export a single function which, when called by Trio, is passed a single argument
{ $tag, $page, asset, site, cheerio }
which can be destructured
({ $tag, $page, asset, site, cheerio })
to access its properties. See Unpacking fields from objects passed as function parameter for more information.
Callbacks can be implemented to run synchronously
module.exports = ({ $tag, site }) => {
site.tagsCatalog.forEach(item => {
$tag.append(`
<li class="tag__list-item">
<a data-trio-link class="tag__list-item-link"
href="/blog/tag/${item.tag.toLowerCase()}">${item.tag}</a>
</li>
`);
});
};
or asynchronously, using async/await.
module.exports = async ({ $tag, site }) => {
const catalog = await getCatalogFromCloud(...);
catalog.forEach(item => {
$tag.append(`
<li class="catalog-item__list-item">
<a data-trio-link class="catalog-item__list-item-link"
href="/catalog/item/${item.name}">${item.price.toFixed(2)}</a>
</li>
`)
});
};
Argument Properties
$page
Please note that this property was originally named $ but was renamed to $page in v1.0.0-rc.5.
$page is a cheerio function. It is equivalent to jQuery's $ and jQuery() functions and can be used to return a collection of matched tags in the composite when you are targeting tags other than $tag.
module.exports = ({ $page, site }) => {
site.tagsCatalog.forEach(item => {
$page("ul.tag__list").append(`
<li class="tag__list-item">
<a data-trio-link class="tag__list-item-link"
href="/blog/tag/${item.tag.toLowerCase()}">${item.tag}</a>
</li>
`);
});
$page("div.page-modified-date").append(new Date().toDateString());
};
$tag
$tag is a cheerio wrapper for the tag which was decorated with the data-trio-callback attribute. It can be used to target the tag with dynamic content.
module.exports = ({ $tag }) => {
$tag.append(new Date().toDateString());
};
site
site exposes the organized collection of metadata that Trio creates from your project's assets. Its catalogs - frags, articlesCatalog, categoriesCatalog, tagsCatalog, dataCatalog - as well as its other properties can be used to augment your composites with dynamic content.
module.exports = ({ $tag, site }) => {
site.tagsCatalog.forEach(item => {
$tag.append(`
<li class="tag__list-item">
<a data-trio-link class="tag__list-item-link"
href="/blog/tag/${item.tag.toLowerCase()}">${item.tag}</a>
</li>
`);
});
};
asset
asset exposes the metadata specific to the fragment, including its front matter. Its catalogs - relatedArticlesByCategory, relatedArticlesByTag, relatedArticlesByTagFlattened - as well as its other properties can be used to augment your composites with dynamic content.
module.exports = ({ $tag, asset }) => {
const data = asset.matter.data;
$tag.find("h1.article__title").append(data.title);
$tag.find("span.article__category").append(`"${data.category}"`);
$tag.find("span.article__date").append(asset.articleDate);
$tag.find("img.article__img").attr("src", `/media/${data.image}`);
};
cheerio
cheerio exposes a constructor function that can be used to load and manipulate dynamic tag structures, such as:
const $ = cheerio.load('<h2 class="title">Hello world</h2>');
$('h2.title').text('Hello there!');
$('h2').addClass('welcome');
$.html();
//=> <html><head></head><body><h2 class="title welcome">Hello there!</h2></body></html>
};
Since both $page and $tag also provide methods for creating and manipulating dynamic tag structures the cheerio property is therefore basically redundant. It is currently being maintained for legacy sake but you are advised that the cheerio property is a strong candidate for deprecation in a future release of Trio.
Internal Module Dependencies And Caching
Tag-Based-callbacks can, of course, have their own internal module dependencies. When they do, though, you should import them using import-fresh (or some similar package) to guarantee that you are always importing uncached copies of them.
const importFresh = require('import-fresh');
const capitalize = importFresh("../lib/capitalize");
module.exports = ({ $tag, site }) => {
site.articlesCount && site.articlesCatalog.forEach(item => {
const data = item.matter.data;
$tag.append(`
<li>
<a data-trio-link href="${item.url}">
${data.title} - Posted to ${capitalize(data.category[0])} - ${item.articleDate}
</a>
</li>
`);
});
};
In the above example, if you had used require instead of import-fresh, and you were running any of Trio's build commands with the watch option, then any changes you make to the module ./lib/capitalize would be ignored because Node will import the module from its cache.
Trio uses import-fresh internally to import uncached tag-based callbacks.
Declaring Your Tag-Based Callback Module's Internal Dependencies To Trigger Incremental Builds
When building incrementally, Trio will detect that you have made modifications to your tag-based callbacks, and it will trigger a build to regenerate only those composites whose assets relate by way of their chains of dependencies to the modified callback. However, Trio will not automatically trigger a build when:
- You have made modifications to modules that your tag-based callbacks might require (internal module dependencies), such as to a library that you created in root/source/callbacks/lib.
- You have made modifications to JSON files in the root/source/data folder that your tag-based callbacks might require
To remedy these 2 situations, you can optionally add front matter to your tag-based callbacks with the properties discussed below that will inform Trio to trigger a build whenever these internal dependencies are modified.
Declaring Your Tag-Based Callback Module's Internal Module Dependencies
You can declare a single module dependency using the moduleDependencies property
/*
moduleDependencies: ../lib/getDate
*/
and you can declare multiple module dependencies.
/*
moduleDependencies:
- ../lib/getDate
- ../lib/getTime
*/
Declaring Your Tag-Based Callback Module's Internal JSON Data File Dependencies
You can declare a single JSON file dependency using the dataDependencies property.
/*
dataDependencies: contactInfo
*/
and you can declare multiple JSON file dependencies.
/*
dataDependencies:
- contactInfo
- portfolio
*/
Declaring External Dependencies On Metadata To Trigger Incremental Builds
When building incrementally, Trio will detect that you have made modifications to your fragment, include, template and tag-based callback assets, and it will trigger a build to regenerate only those composites whose assets relate by way of their chains of dependencies to those modified assets. However, Trio will not regenerate composites whose own assets haven't changed but which consume the metadata generated from unrelated modified assets, such as for pages that contain lists of other pages (e.g. blog index pages, tag pages, category pages, catalog pages, portfolio pages, etc.).
To remedy this situation, you can add the alwaysBuild property to your fragment's front matter which will cause Trio to always mark this fragment as stale.
Using collections, which were introduced in v2.0.0, can eliminate having to use alwaysBuild.
<!--
template: blogpage
title: Trio Blog | Official blog for Trio static site generator.
alwaysBuild: true
-->
See Also
Your Financial Support Of This Project Is Greatly Appreciated
Trio is an open source project and is therefore free of charge to use both for noncommercial and commercial use, but when you use Trio to create a new website, please consider donating a few bucks. It doesn't take very long, the process is secure, and it will allow us to continue to support the community and to maintain and enhance Trio going forward.