Origami templates

Transform data to text

Web Origami templates let you convert turn data into HTML or other text documents through expressions embedded in text.

  • These templates work directly on a wide range of data types, including file system folders or network resources.
  • Templates have a very small number of fundamental features that can be combined to address a wide range of scenarios.
  • You can extend what’s possible in a template expression in JavaScript with essentially no configuration.

You can use Web Origami with other template systems, but the small degree of integration code required is currently beyond the scope of this documentation.

Template documents

An Origami template document is a text file with a .orit extension. A template contains placeholders marked with {{ and }} curly braces that contain Origami expressions.

$ cat greet.orit
Hello, {{ name }}.

You can evaluate a template in the context of data, such as an object defined in a data file.

$ cat alice.yaml
name: Alice Andrews
$ ori "greet.orit(alice.yaml)"
Hello, Alice Andrews.

Reference input

When you invoke a template as a function, you can refer to the template’s input using an underscore (_).

$ cat heading.orit
<h1>{{ _ }}</h1>
$ ori "heading.orit('About Us')"
<h1>About Us</h1>

Reference individual files

You can reference local files in Origami expressions. Depending on the situation, you may not have to pass any arguments to the template — it may be able obtain whatever it needs from its file system context.

$ cat fileRef.orit
This project is {{ copyright.txt }}.
$ cat copyright.txt
©2023 Alice Andrews
$ ori "fileRef.orit()"
This project is ©2023 Alice Andrews.

In cases like this, where the template does not require any argument, you can avoid the need to quote parentheses by invoking the template using slash syntax:

$ ori fileRef.orit/
This project is ©2023 Alice Andrews.

Reference trees

If a template expression results in a tree such as a folder or hierarchical data, Origami will collect the deep values of that tree, convert them to strings, then concatenate them.

$ cat greetings.yaml
Alice: Hello, Alice.
Bob: Hello, Bob.
Carol: Hello, Carol.

$ cat flatten.orit
Here are the text strings in the greetings tree:
{{ @tree/from greetings.yaml }}
$ ori flatten.orit/
Here are the text strings in the greetings tree:
Hello, Alice.Hello, Bob.Hello, Carol.

This feature forms the basis for more complex ones (like maps, below), but one basic use for it is to inline a set of files. For example, you might create a folder that contains a collection of HTML fragments as separate files:

$ ls fragments
a.html b.html c.html
$ cat fragments/a.html
<p>A</p>

You can then reference that fragments folder in a template to concatenate all those HTML fragments into the output:

$ cat concat.orit
{{ fragments }}
$ ori concat.orit/
<p>A</p>
<p>B</p>
<p>C</p>

Use template expressions in any file type

It may be useful to embed Origami expressions inside other kinds of files, such as .html files. You can evaluate such expressions with the built-in @inline function.

For example, you can use this to inline resources such as stylesheets.

$ cat inline.html
<html>
  <head>
    <style>
      {{ inline.css }}
    </style>
  </head>
  <body>
    This text will be red.
  </body>
</html>
$ cat inline.css
body { color: red }
$ ori @inline inline.html
<html>
  <head>
    <style>
      body { color: red }

    </style>
  </head>
  <body>
    This text will be red.
  </body>
</html>

Here, the inline.html file is acting as an Origami template, but keeps the .html extension so that it can be otherwise treated as an HTML file.

If the input document contains any front matter (see below), @inline preserves this in the output.

Traverse into data

Inside a template, you can use slash-separated paths to traverse into data.

$ cat teamData.yaml
- name: Alice
  image: van.jpg
  location: Honolulu
  bio: After working as a manager for numerous startups over the years, I
    decided to take the plunge and start a business of my own.
…
$ cat teamLead.orit
The leader of our team is {{ teamData.yaml/0/name }}.
$ ori teamLead.orit/
The leader of our team is Alice.

Reference network resources

Since https and http URLs are valid Origami expressions, you can incorporate network content into a template’s output.

$ cat net.orit
This content came from graphorigami.org:
{{ https://graphorigami.org/samples/templates/net.txt }}
$ ori net.orit/
This content came from graphorigami.org:
Hello, Graph Origami!

This includes being able to traverse into data from the network. A teamData.yaml file posted on the network can be referenced as an expression and then further traversed:

$ cat netData.orit
Bob lives in {{ (https://graphorigami.org/samples/templates/teamData.yaml)/1/location }}.
$ ori netData.orit/
Bob lives in Los Angeles.

You can also obtain a data file from the network, treat it as a tree, and map the tree to text. This allows you to directly process network data into text in a template.

Conditions

Use the built-in @if function to include text based on some condition.

The first argument to @if is a condition that is evaluated. If the result is truthy (not false, null, or undefined), the second argument to @if is included in the template’s text output. If the result is falsy and a third argument is provided, that third argument will be included in the output.

$ cat condition.orit
{{ @if(_/rating, `Rating: {{ _/rating }}`, `Not yet rated`) }}

$ ori “condition.orit({ rating: 3 })”
Rating: 3

$ ori “condition.orit({})”
Not yet rated

Call your own JavaScript functions

Your template expressions can call any JavaScript in scope via the base of the JavaScript file name.

For example, if you have a file named uppercase.js in the same directory as the template, a template expression can reference that module’s default export as uppercase:

$ cat uppercase.js
export default (x) => x.toString().toUpperCase();

$ cat callJs.orit
Hello, {{ uppercase.js("world") }}!
$ ori callJs.orit/
Hello, WORLD!

If the function you invoke is asynchronous, its result will be awaited before being incorporated into the text output.

Call another template as a function

One template can invoke another as a function.

We can define a template stars.orit as a component that displays a star rating:

$ cat stars.orit
<span class="stars">{{ @repeat(_, "★") }}</span>

This template repeats a ★ star character for the number of times defined in in the input value. For example, you can directly invoke and test this template, passing in a value of 3:

$ ori "stars.orit(3)"
<span class="stars">★★★</span>

This stars.orit template defines a function that you can invoke inside expressions in other templates:

$ cat review.orit
{{ stars.orit(5) }}

$ ori review.orit/
<span class="stars">★★★★★</span>

This technique can let you define components in plain HTML and CSS.

Wrap one template with another

Another application of invoking a template as a function is to wrap the output of one template inside another. For example, you can create an overall page template for a site called page.orit:

$ cat page.orit
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
  </head>
  <body>
{{ _ }}
  </body>
</html>

A template for a specific type of page, like a contact.orit template for a Contact Us page, can invoke page.orit as a function:

$ cat contact.orit
{{ page.orit(`
    <h1>Contact Us</h1>
    <p>We'd love to hear from you!</p>
`) }}

Evaluating this embeds the contact page template, then passes its content to the overall site page template:

$ ori contact.orit/
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
  </head>
  <body>
    <h1>Contact Us</h1>
    <p>We'd love to hear from you!</p>

  </body>
</html>

Front matter

Both a template’s input document and the template itself can contain front matter in YAML or JSON format. The front matter is delineated with both a leading and trailing line of three hyphens (---), like so:

$ cat front.orit
---
title: My First Page
---
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>{{ title }}</title>
  </head>
  <body>
    <h1>{{ title }}</title>
  </body>
</html>

This template defines a title value as front matter and then references that value in multiple expressions. This makes it easy to update the title in a single place and have that change reflected everywhere. The front matter values are added to the scope used to evaluate the template’s expressions, so the {{title}} references in the template body will find the title value.

Invoking this template performs the title substitutions:

$ ori front.orit/
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>My First Page</title>
  </head>
  <body>
    <h1>My First Page</title>
  </body>
</html>

Front matter expressions

Front matter can include Origami expressions via the !ori YAML tag, as discussed in Origami expressions in YAML. You can use this to calculate a value you want to reference multiple times in the template.

$ cat banner.orit
---
banner: !ori (`<p>{{ title }}</p>`)
title: My Site
---
<html>
  <head>
    <title>{{ title }}</title>
  </head>
  <body>
    {{ banner }}
    <p>Welcome to my site.</p>
    {{ banner }}
  </body>
</html>

$ ori banner.orit/
<html>
  <head>
    <title>My Site</title>
  </head>
  <body>
    <p>My Site</p>
    <p>Welcome to my site.</p>
    <p>My Site</p>
  </body>
</html>

Template and input front matter

Both a template and an input document can define front matter.

Among other things, you can use this to have a template define a default, fallback value for a given key. A blog post template can define a default title in its own front matter. It can then use an @or statement to prefer the input’s title property if one is defined, but if not falling back to the default title.

$ ori blogPost.orit
---
defaultTitle: A blog post
---
<html>
  <head>
    <title>{{ @or(_/title, defaultTitle) }}</title>
  </head>
  <body>
    {{ _/@text }}
  </body>
</html>

If a blog post defines a title, that title is preferred:

$ cat posts/post1.html
---
title: My First Post
---

Here's the text of my first post.

$ ori blogPost.orit posts/post1.html
<html>
  <head>
    <title>My First Post</title>
  </head>
  <body>
    
Here's the text of my first post.

  </body>
</html>

But if a post fails to define a title, the template’s default title is used:

$ cat posts/post2.html
Here's the text of my second post.

$ ori blogPost.orit posts/post2.html
<html>
  <head>
    <title>A blog post</title>
  </head>
  <body>
    Here's the text of my second post.

  </body>
</html>

Map trees to text

It’s common to have a template generate some fragment of text for each value in a tree: an array, a set, a folder, etc. You can handle such cases in Web Origami templates by calling the built-in @map function to map a tree’s values to text.

$ cat teamData.yaml
- name: Alice
  image: van.jpg
  location: Honolulu
  bio: After working as a manager for numerous startups over the years, I
    decided to take the plunge and start a business of my own.
- name: Bob
  image: kingfisher.jpg
  location: Los Angeles
  bio: Having been an art student for 11 years, I constantly explore various
    disciplines to incorporate artistic pursuits into product design studies.
- name: Carol
  image: venice.jpg
  location: Venice
  bio: I open the line of communication between clients, customers, and
    businesses to get projects done.

$ cat teamList.orit
<ul>
{{ @map(teamData.yaml, =`
  <li>{{ _/name }}</li>
`) }}
</ul>

$ ori teamList.orit/
<ul>
  <li>Alice</li>
  <li>Bob</li>
  <li>Carol</li>
</ul>

The teamList.orit file defines an outer template that includes an <ul> tag. Inside that, a substitution calling @map appears, which maps the array of people in teamData.yaml to a set of HTML fragments using a nested template with an <li> tag.

How maps work

Web Origami templates don’t treat such maps specially. Rather, the @map function is returning a tree of HTML fragments that are concatenated into the text output.

In the above example, the @map function maps an array of people to HTML fragments. The transformation can be visualized like this:

g 0 ->0 0 1 ->1 1 2 ->2 2 0/name Alice 0->0/name name 1/name Bob 1->1/name name 2/name Carol 2->2/name name
g 0 <li></li> ->0 0 1 <li></li> ->1 1 2 <li></li> ->2 2
Source tree of people objects
Result tree of HTML fragments

Per the discussion in Reference trees, the template concatenates the HTML fragments into the text output.

Reference the key for a value

When mapping a tree (like a folder) to text, you can obtain the key (like a file name) via the ambient @key property.

Suppose you have a folder holding some files:

$ ls posts
post1.html post2.html

You can create an index page that links to these files using the ambient @key property. This lets a link reference a file’s specific file name in the href attribute.

$ cat blogIndex.orit
---
defaultTitle: A blog post
---
<ul>
{{ @map(posts, =`
  <li>
    <a href="posts/{{ @key }}">
      {{ @or(_/title, defaultTitle) }}
    </a>
  </li>
`) }}
</ul>

This index page template defines a default title property to use if a page omits a title; see template and input front matter above.

Evaluating this template produces a list of links to each post, with each href attribute referencing the appropriate file:

$ ori blogIndex.orit/
<ul>
  <li>
    <a href="posts/post1.html">
      My First Post
    </a>
  </li>
  <li>
    <a href="posts/post2.html">
      A blog post
    </a>
  </li>
</ul>