Skip to content

07. Special Page Template Tags (e.g. for Sidenavs)

James231 edited this page Jun 9, 2020 · 3 revisions

In this section I present the special tags you can use in Documentation Templater files, allowing you to iterate through all pages of the documentation to construct things like a sidenav.

These tags include <ForAllFolders>, <ForeachSubfolder>, <ForeachPage>, <IsThisPage>, <IsNotThisPage>, <IsThisFolder>, and <IsNotThisFolder>.

Iterating through Folders and Files

Suppose you wanted to add a links to all the files in your Input folder, but you wanted the files to be grouped so that all files of the same directory appear together. To achieve this you need to recursively search through the tree and add HTML for each file you encounter. This can be achieved in the following code (this uses a Depth First Search):

<ForAllPages>
    <ForeachSubfolder>
        ... Repeated for each folder
        @SubFolder.Recursive;
    </ForeachSubfolder>
    <ForeachPage>
        ... Repeated for each page
    </ForeachPage>
</ForAllPages>

Note: This would usually be done in a template file as you would want this layout code to be shown on all pages.

It is best to think of this in terms of a side navigation bar as in the sample documentation. In the sidenav, you can expand a folder and within it you see a list of subfolders, and a list of pages in the folder. The code showing each subfolder in the list is defined within the <ForeachSubfolder> tags, and the code displaying each file in the list is defined within the <ForeachPage> tags in the example above. Now consider one of those subfolders. If you look at the raw HTML, you'll see it has hidden content just after it, which would be drawn if the folder dropdown was expanded. This dropdown content comes from the same <ForeachSubfolder> and <ForeachPage> tags, but they are now iterating through subsubfolders and files in the subfolder. This is where the recursion takes place. And you can define the exact position using the @SubFolder.Recursive; text.

Subfolder/Page Properties, Layers, Links and Ids

In the Sidenav you would want to display the name of each subfolder/file, and link to the files. First of all let's consider pages only.

Recall from the sections on HTML Pages and Markdown Pages that pages can have page properties which you can define on the top of each page, and inject the values elsewhere into the page template. The code being added to the page template displaying each name in the list is within the <ForeachPage> tag. Within this tag you can access the properties of the iterate page by using @SubPage.myproperty; where myproperty is the property of the page.

You can also add links to the different pages by using @SubPage.Link;. This will give a URL encoded link 'relative to the Output folder' with a leading slash. And @SubPage.Id; will give a unique id code for each page which may be useful if you need unique id's for each page link in the HTML.

Then for folders, you may want not want a link, but instead a dropdown to contain nested folders/pages. You can write the code for this within <ForeachSubFolder> tags which should be children of <ForAllPages> and siblings to <ForeachPage>. Note that to contain nested folders/pages, we need to insert the next level of folder/page menu items somewhere within the <ForeachSubFolder> tags. This is done with @SubFolder.Recursion;.

For folders within the <ForeachSubFolder> tags you can use @SubFolder.Name; to get the name of the folder and @SubFolder.Layer; to get the depth of the folder from the root Input folder (which has layer '0').

For example, suppose we have the following file structure in the Input folder:

├── page1.html
├── page2.md
└── Page Subdirectory
   └── page3.html

And within each page file we have a PageTitle property defined. E.g. for page1.html let's suppose the top of the file has:

<PProperty name="PageTitle" value="Page 1"/>

Now suppose our page template looks like this:

<html>
    <body>
        <div class="sidenav">
            <ForAllPages>
                <ForeachSubFolder>
                    <h4>@SubFolder.Name;</h4>
                    <div style="margin-left: 10px">
                        @SubFolder.Recursive;
                    </div>
                </ForeachSubFolder>
                <ForeachPage>
                    <a href="@SubPage.Link;">@SubPage.PageTitle;</a>
                </ForeachPage>
            </ForAllPages>
        </div>
        <div class="content">
            <h2>@Page.PageTitle;</h2>
            @ChildContent;
        </div>
    </body>
</html>

(Note that we could move the <ForAllPages> tag and its children into a Template Element if the file gets too long.

Then our output HTML for page1.html will be something like this:

<html>
    <body>
        <div class="sidenav">
            <h4>Page Subdirectory</h4>
            <div style="margin-left: 10px">
                <a href="/page+subdirectory/page3.html">Page 3</a>
            </div>
            <a href="/page1.html">Page 1</a>
            <a href="/page2.html">Page 2</a>
        </div>
        <div class="content">
            <h2>Page 1</h2>
            <!-- Main content of page1.html -->
        </div>
    </body>
</html>

Notice that the (layer 1) folder and its (layer 2) contents appear before the (layer 1) pages in the sidenav. This is not related to the order of <ForeachSubFolder> and <ForeachPage> but instead is the default behaviour for orders which can be changed. See the section on Ordering Pages for more info.

Conitionals

When drawing a sidenav or navigation bar you might want to highlight the currently selected page but not the other pages. But if you apply a styling change within <ForeachPage> then this will effect all pages. You could fix this by giving each page a unique id with @SubPage.Id; then setting the change in JavaScript to the element with the id of the current page but there is a nicer way ...

If you use tags <IsThisPage>...</IsThisPage> within the <ForeachPage> tags (doesn't have to be direct children, just descendants), then the content between the tags is only added to the output HTML once for the generated page. Tag <IsNotThisPage> does the opposite, and the content will be added to the output HTML for every page except the page which is being generated.

Applying this to the example code above we can use:

<ForeachPage>
    <IsThisPage>
        <a class="selected" href="@SubPage.Link;">@SubPage.PageTitle;</a>
    </IsThisPage>
    <IsNotThisPage>
        <a href="@SubPage.Link;">@SubPage.PageTitle;</a>
    </IsNotThisPage>
</ForeachPage>

Then for page1.html the sidenav contains:

<div style="margin-left: 10px">
    <a href="/page+subdirectory/page3.html">Page 3</a>
</div>
<a class="selected" href="/page1.html">Page 1</a>
<a href="/page2.html">Page 2</a>

There is an equivalent for folders within <ForeachSubFolder>, namely the <IsThisFolder> and <IsNotThisFolder> tags. The <IsThisFolder> tag is used if the currently folder being iterated through to draw the side nav is an anscestor (parent or indirect parent) of the page currently being viewed. The <IsNotThisFolder> tag is shown otherwise. This is useful for styling the folders in the sidenav which the user is reading content from, or if you are using dropdowns, to decide which folder dropdowns should be expanded when the page loads.

Note

These are the only conditions that the Documenation Templater currently supports when iterating through files. If you wand to add more the please consider opening a PR.