Trio Icon
Trio v6.1.0

The Trio Blog

  1. Latest
  2. Releases
  3. News
  4. Tutorials

Advanced Page Composition And Collections: Part 3) 249 Detail Pages

a web page composition abstract

Prerequisites

Before proceeding with this tutorial, please familiarize yourself with Trio's Core Concepts, Tag-Based Callbacks, Metadata, and Collections.

Intention Of This Tutorial

In this tutorial we will discuss the use case for Trio collections and use a collection to generate the 249 flag detail pages for our Flags Of The World Site.

Collections Overview

Collections are groups of pages that are dynamically generated by Trio that we would otherwise have to manually create ourselves.

Take our Flags Of The World website that we are developing as part of this tutorial series for example, it includes a landing page that lists all 249 flags of the word. Information about each flag is maintained in a file named root/source/data/world.json. Now we'd like to create a detail page for every flag of the world and render those pages when one clicks on one of the flags on the site's landing page.

Without collections, you would have to manually create one fragment for each flag detail page. If there were only a few flags then that wouldn't be too difficult. However, there are 249 flags and imagine how tedious that would be if we had to manually create and maintain all of them.

That's where collections come to the rescue; they do all the "heavy lifting" for you. You only need to create one fragment whose front matter declares one collection filter function that when called by Trio returns a collection dataset, which is an array that contains one collection dataset item for each flag. Trio will then create a "clone" of that fragment in memory for every collection dataset item and will expose the collection dataset item's properties for that cloned fragment to tag-based callbacks via the callback's asset argument, specifically as the asset.collection property.

Collection Datasets

Collection datasets are arrays of collection dataset items that collection filter functions return. Each collection dataset item in a collection dataset represents a single page of the collection. The total number of collection dataset items in a collection dataset determines the total number of pages that Trio will generate for the collection.

Collection Dataset Item Properties

Each collection dataset item in a collection dataset must include the following properties:

  • pageName

The name that will be used for the page, which may also include a path.

Trio uses the value that you assign to pageName for generating the path in the root/source/fragments/ folder that the "cloned" fragment would be located in if it were actually written to disk. This allows Trio to treat the in memory only "cloned" fragment as if it actually was a physical file.

Trio uses the following algorithm to generate the path: path to the fragment in which the collection is declared + collectionDatasetItem.pageName + the file name extension of the fragment (either .html or .md) in which the collection is declared.

  • data

The data that is intended to be exposed to the composite's tag-based callbacks, which can be accessed to add dynamic content to the page. Using the Flags Of The World website for an example, the data property would be assigned a single item from the root/source/data/world.json file.

You can add your own custom properties to collection dataset items and they will be accessible in the composite's tag-based callbacks also.

Create The Template Project Asset

In the project's root/source/templates folder, create a new template file named country.html and copy and paste the following markup into that file:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <link rel="stylesheet" href="/css/main.da9d6ce4b56f84613e14a22e3a92b5d4.css">
    <script src="/scripts/main.js"></script>
</head>
<body>
    <main data-trio-fragment data-trio-callback="country" class="flag-country">
        <h1 class="flag-country__page-title">Flags Of The World</h1>
    </main>
</body>
</html>

Please notice that the above template project asset contains a single tag (in this case, a "main" tag) that is decorated with the data-trio-fragment attribute. When Trio finds such a tag decorated with this attribute in a template it will target it with any content it finds in the associated fragment. This isn't required if the fragment isn't contributing any content of its own.

Also, please notice that the same "main" tag is decorated with the data-trio-callback attribute and that it is assigned the name of the tag-based callback (see below), "country", which is located in the root/source/callbacks folder.

Create The Fragment Project Asset

In the project's root/source/fragments/country folder, first delete the .gitkeep file, and then create a new fragment file named country.html and copy and paste the following into that:

<!--
template: country
title:  Map |
appendToTarget: true
collection:
    filterFn: countryFilter
-->
<h2 class="flag-country__name"></h2>
<div class="flag-country__flag-container"></div>
<div class="flag-country__alpha2-code"></div>
<div class="flag-country__alpha3-code"></div>
<a id="catalog-link" href="/"><p class="flag-country__catalog-link">Return To Catalog</p></a>

Please notice how the above fragment project asset declares front matter at the very top of the file, in which it defines:

  1. The required front matter property template with the name of the template file it is associated with.
  2. The optional front matter property title with the title to be assigned to the generated document's title tag.
  3. The optional front matter property appendToTarget which Trio uses to determine if it should append to or replace the tag in the template that is decorated with the data-trio-fragment attribute with the fragment's content. By assigning true, Trio will append whatever content it finds in the associated fragment to this tag.
  4. The optional front matter property collection which is a hash with a key of "filterFn" which is assigned the name of the filter function, which in this case is countryFilter.js, that resides in the source/filters folder (the .js file type is assumed so it isn't necessary to include it).

The JSON File

In the starter project's root/source/data folder you will find the world.json file, which is an array of 249 items which contain the data for each flag of the world. The following is only a very small sample of the actual file:

[
{"id":4,"name":"Afghanistan","alpha2":"af","alpha3":"afg","img":"af.png"},
{"id":248,"name":"Åland Islands","alpha2":"ax","alpha3":"ala","img":"ax.png"},
{"id":8,"name":"Albania","alpha2":"al","alpha3":"alb","img":"al.png"},
{"id":12,"name":"Algeria","alpha2":"dz","alpha3":"dza","img":"dz.png"},
.
.
.
]

The following describes the key: value pairs of the items in the file:

  • "id" - a number, the unique id of the item
  • "name" - a string, the geographical location that this items flags pertains to, also used to sort the file in ascending "name" order
  • "alpha2" - a string, the 2 character code for the flag and its geographical location
  • "alpha3" - a string, the 3 character code for the flag and its geographical location
  • "img" - a string, the path to the flag's .png image residing in the site's media/flags/128x128/ folder

Create the Collection Filter Function

In the project's root/source/filters folder, first delete the .gitkeep file, and then create a new javascript file named countryFilter.js and copy and paste the following into that:

module.exports = ({ site }) =>
    site.dataCatalog.world.map(country =>
        ({ data: country, pageName: country.name }));

The above collection filter function maps each country record in the root/source/data/world.json file, which it accesses via site.dataCatalog.world, to a collectionDatasetItem by:

  1. Assigning the whole country record to the collectionDatasetItem's data property.
  2. Assigning the country name to the collectionDatasetItem's name property.

Create The Tag-Based Callback

In the project's root/source/callbacks folder, create a new javascript file named country.js and copy and paste the following into that:

/*
dataDependencies: world
*/
module.exports = ({ $tag, $page, asset }) => {
    $page("title").append(" " + asset.collection.data.name);
    $tag.find("h2.flag-country__name").append(asset.collection.data.name);
    $tag.find("div.flag-country__flag-container").append(`
        <img class="flag-country__flag"
        src="/media/flags/128x128/${asset.collection.data.alpha2}.png"
        alt=" flag of ${asset.collection.data.name}">
    `);
    $tag.find("div.flag-country__alpha2-code").append(`
        <span class="flag-country__label">ISO 3166-1 Alpha2 Code:</span> ${asset.collection.data.alpha2}
    `);
    $tag.find("div.flag-country__alpha3-code").append(`
        <span class="flag-country__label">ISO 3166-1 Alpha3 Code:</span> ${asset.collection.data.alpha3}
    `);
};

When fragments declare collections, Trio will pass all the properties in the collectionDatasetItem to each tag-based callback it finds in the composite via the tag-based callback's asset argument, specifically by way of the asset.collection property.

The tag-based callback above uses the asset.collection.data property, which references the record from the root/source/data/world.json file that the collection filter function assigned to the collectionDatasetItem's data property, to format the HTML and content required by the page.

Build And Run The Project

Now that we have composed our intended 249 detail pages using a template, a fragment, data from a JSON file, a collection filter function, and a tag-based callback, we are ready to build our site and render the site in the browser. In your terminal application, please run the following commands from the root folder of your project:

trio build; trio serve

If you prefer, you can use the abbreviated forms of these commands instead:

trio b; trio s

The build command instructs Trio to do a one-off build of your site for development and to place the site's generated output into the project's public/ folder. The serve command instructs Trio to serve the project's public/ folder's content in the browser.

Now that we have implemented the 249 detail flag pages using a collection, please click on any one of the flags listed in the landing page to render its detail page which, depending on the flag you clicked on, will look similar to the following:

image of Flags Of The World detail page

You can also view the content of any of the generated HTML documents for the detail pages, which reside in the public/country folder by simply opening one in your editor of choice. Let's take a look at the one for Virgin Islands (U.S.) now:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Map | Virgin Islands (U.S.)</title>
    <link rel="stylesheet" href="/css/main.da9d6ce4b56f84613e14a22e3a92b5d4.css">
    <script src="/scripts/main.js"></script>
</head>

<body>
    <main data-trio-fragment data-trio-callback="country" class="flag-country">
        <h1 class="flag-country__page-title">Flags Of The World</h1>
        <h2 class="flag-country__name">Virgin Islands (U.S.)</h2>
        <div class="flag-country__flag-container">
            <img class="flag-country__flag" src="/media/flags/128x128/vi.png" alt=" flag of Virgin Islands (U.S.)">
        </div>
        <div class="flag-country__alpha2-code">
            <span class="flag-country__label">ISO 3166-1 Alpha2 Code:</span> vi
        </div>
        <div class="flag-country__alpha3-code">
            <span class="flag-country__label">ISO 3166-1 Alpha3 Code:</span> vir
        </div>
        <a id="catalog-link" href="/">
            <p class="flag-country__catalog-link">Return To Catalog</p>
        </a>
    </main>
</body>

</html>

From the above we can see that the tag-based callback root/source/callbacks/country.js declared by the <main> tag that is decorated with the data-trio-callback attribute in the template

<main data-trio-fragment data-trio-callback="country" class="flag-country">

was called and that it did dynamically generate the flag details from the individual items in the root/source/data/world.json file and appended them as content to the <main> tag.

Conclusion

This tutorial concludes our examination of Trio's advanced page composition with collections and demonstrates how we can dynamically generate HTML documents from metadata, such as from the root/source/data/world.json file used in this tutorial.

We really hope that you have enjoyed all the tutorials in this series on Page Composition with Trio as much as we have had making them. Please consider a small donation to this project (see below) so that we can continue to bring you similar quality content.

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.

Show your ❤️, add your ★ to the Github repo.