The rdfpub Tutorial Site

Handlebars

Handlebars is a template system that rdfpub uses to generate HTML pages. Template files that you create are filled out with the results of your SPARQL queries and rendered as HTML. It is expected that you know how to write HTML and that you understand how Handlebars operates in order to create functioning templates.

About index files

A file named index.handlebars is known as an index file. A resource that has an index file will serve HTML rendered from its index file. For example, this web page is generated from an index.handlebars file in the /tutorial/lessons/handlebars-templates directory of this site's source.

About partial templates

It is often the case that many resources will have similar or identical layouts which can be tedious to repeat throughout many index files. To make repeated templates easier to read, write, and maintain, rdfpub makes use of a Handlebars feature known as partials.

Handlebars partials are templates that can be called from and included in other templates. They present a means of building templates that include common template components that would otherwise require that you copy/paste the parts that are shared between multiple templates. Duplicating common code is difficult to maintain and prone to error; partials solve this problem very gracefully.

In rdfpub, any .handlebars file that has a name other than index will get registered as a partial template that other templates can include. For example, the lesson.handlebars file for this tutorial site is registered as a partial named lesson. This partial then gets called in each of the individual lesson index templates as {{>lesson}}.

As you might have guessed, partial templates are inherited by subdirectories in the same was as SPARQL query files. That is, partial templates are recursively copied down from its own directory to all subdirectories during the site build process.

SPARQL query results in Handlebars

In the previous lesson, it was explained that SPARQL queries are executed against every resource they apply to, and that the name of each SPARQL query is used to reference its results in Handlebars templates. The resulting data object that gets passed into a Handlebars template is indexed by each query name. For example, like so:

{
  "person": { ... },
  "friends": { ... }
}

And the value mapped to each name is the JSON SPARQL query results returned from being executed for the current resource.

SPARQL query results shortcuts

The default format of a JSON SPARQL query results object is a little verbose and unwieldy. For example, if you wanted to reference a name variable from your person query, it might look like this:

<p>This person's name is {{person.results.bindings[0].name.value}}.</p>

This is terribly long-winded which makes it harder to understand as well as makes it more likely that you'll make some kind of human error while typing it all out. It also makes your templates less readable.

To ease this burden, rdfpub adds a few convenient shortcuts to your query results that make your templates easier to both read and write. Each shortcut is explained below.

Top-level variable values

In many cases, your queries will return exactly one result which contains information about a single resource. For instance, each lesson in this tutorial is rendered from a lesson query that extracts the name, content, chapter number, and next lesson from each lesson. In the lesson template, the results of this query are used to render each lesson as HTML.

So for this common case, it would be nice to be able to access the single result of the query without having to drill all the way down into the SPARQL results object. rdfpub enables this by mapping each variable to the top-level query result object to its value in the first query result binding.

For the lesson query, that means that instead of this:

<header>
  <h1>Chapter {{lesson.results.bindings[0].chapter.value}} - {{lesson.results.bindings[0].name.value}}</h1>
</header>

We can instead do this:

<header>
  <h1>Chapter {{lesson.chapter}} - {{lesson.name}}</h1>
</header>

This second example is much more concise.

Top-level results iteration

There are cases where your query might return a number of results that you need to render as a list or such. For example, the lessons query gets a list of all lessons in chapter order so that they can be rendered in a consistent order as links at the top of each page.

It would be nice to loop through a query's results without having to dive too deeply into the query results object. rdfpub enables this by making each query iterable by a Handlebars #each block helper at the top level. It also binds each value of a variable binding to the variable binding object itself.

So instead of a verbose iteration like this:

<ol>
  {{#each lessons.results.bindings}}
    <li><a href="{{url.value}}">{{name.value}}</a></li>
  {{/each}}
</ol>

We can instead do this:

<ol>
  {{#each lessons}}
    <li><a href="{{url}}">{{name}}</a></li>
  {{/each}}
</ol>

This makes iteration much more simple and sensible.

Checking if a query has results

Sometimes you may want to render a certain part of the page only if a particular query actually has results. With a raw JSON query results object, you could check the bindings array like so:

{{#if friends.results.bindings}}
  <h2>Friends</h2>
  <ul>
    {{#each friends}}
      <li>{{name}}</li>
    {{/each}}
  </ul>
{{/if}}

rdfpub makes this kind of check more concise at the top level which allows you to check for query results like this instead:

{{#if friends}}
  <h2>Friends</h2>
  <ul>
    {{#each friends}}
      <li>{{name}}</li>
    {{/each}}
  </ul>
{{/if}}

In this example, if the friends query has no results, then the #if check will evaluate to false and skip rendering the "Friends" section.

Helpers

The functionality of Handlebars can be extended via helpers which can perform processing on information passed into a Handlebars template. rdfpub defines a small handful of useful helpers that you can leverage for your site. In the future, it will be possible to register your own helpers, but only the built-in helpers described below are currently available.

any

The any helper is used in an #if condition to check if any single condition of multiple conditions are true. It is effectively a logical OR between multiple conditions.

You can use the any helper like so:

{{#if (any friends family)}}
<p>{{!-- render any available friend or family relationships}}</p>
{{/if}}

all

The all helper is used in an #if condition to check that all provided conditions are true. It is effectively a logical AND between multiple conditions.

You can use the all helper like so:

{{#if (all person.firstname person.lastname)}}
<p>Person has both a first name and a last name</p>
{{/if}}

equals

The equals helper tests a number of values (usually two) for equality. This can be useful for conditionally rendering parts of a page based on comparisons between values.

For example, you could have a template that has different sections based on a person's primary language returned in a query result, like so:

{{#if (equals person.language "en")}}
  {{!-- English text --}}
{{/if}}

{{#if (equals person.language "es")}}
  {{!-- Spanish text --}}
{{/if}}

markdown

The markdown helper renders its Markdown input as HTML. This can be very useful for embedding rich descriptions of resources in your RDF data that can be converted to HTML for display on your web pages.

This tutorial site uses markdown embedded in RDF for most of its content. For example, the lesson template renders its main content like so:

<main>
  {{{markdown lesson.content}}}
</main>

Note the use of triple curly braces, a Handlebars syntax that preserves characters that are used in HTML. Without them, the Handlebars processor would escape the resulting HTML which would not display nicely in a web browser.

Markdown rendering is provided by markdown-it.

relative

The use of relative URL's in HTML link/a elements can be very useful for site portability because a relative link will resolve against the current base URL which can change based on where a site is being served from. For instance, if your data's base URL is http://www.foo.example but your site is actually served from http://www.bar.example, then all of the URL's from your data that are linked to on your HTML pages will be wrong. The links could even break with a simple difference between http:// and https://.

The relative helper solves this problem by taking a URL and outputting its path, query, and fragment/hash. This URL segment can be used in HTML as a relative link to another page on the site without regards to where the site is actually hosted.

Use of this helper can be seen in the header.handlebars file of the rdfpub tutorial site itself. Note that the base URL of the site is https://rdf.pub, so if the results of the lessons query were to simply output URL's like so...

{{#each lessons}}
  <a href="{{url}}">{{name}}</a>
{{/each}}

...then the links in the header would always point to the live site at https://rdf.pub/tutorial. If you run the site on your local machine and access it via http://localhost/tutorial, such as for testing or experimenting with changes, then the links will take you away from your local site.

So the template uses the relative helper to render relative links that will always link to the site itself no matter where it's deployed to:

{{#each lessons}}
  <a href="{{relative url}}">{{name}}</a>
{{/each}}

It is generally recommended to use the relative helper for all URL's returned by queries that are expected to link exclusively to other pages on the same site.

Special variables

A couple of special variables are made available as part of the data object passed to your Handlebars templates. These variables are each explained below.

$resource

The $resource variable is the absolute URL of the current resource, resolved against the BaseURI specified in your .rdfpub configuration file. It can be useful for appending links in order to build links to other resources, especially language variants of the same resource.

For instance, if you have a resource http://example.com/foo/bar that you want to link to language variants of, you could do so in a generic/reusable fashion like so:

<a href="{{relative $resource}}@es">Visit this page in Spanish</a>
<a href="{{relative $resource}}@de">Visit this page in German</a>

$language

The $language variable contains the language code of the page that is currently being processed during site rendering. As explained in the i18n lesson, each of your index templates can have an associated language, such as en for English or es for Spanish. Your site will have a separate page for each supported language, and you can access the language for each page using the $language variable.

An example use case would be selecting different sections of a page based on the current language, like so:

{{#if (equals $language "en")}}
  <h1>Hello!</h1>
{{/if}}

{{#if (equals $language "es")}}
  <h1>Hola!</h1>
{{/if}}