Getting Started
Template Basics

Template Basics

As you dive deeper into using obsidian-zotero, you'll find that having the ability to customize templates according to your specific needs can be incredibly useful. This feature allows you to tailor the output of your literature notes, offering additional flexibility and control over your data.

For our templates, we use the eta (opens in a new tab) template engine. We'll discuss the basics of template language in this section. If you're already familiar with eta, you can skip this section.

Basics

For the sake of simplicity, let's start with a simple template. As an example, if you want to include only the title in the note filename, you can use the following template:

Note Filename
<%= it.title %>.md

Every eta tag starts with <% and ends with %>. The symbol next to % signifies the tag type. Here, <%= ... %> is an output tag, meaning the evaluated value inside will be rendered as plain text.

A global it variable, similar to tp variable in Templater, is available in the template. This variable contains the data of the current literature, annotation, etc, depend on the context.

The beauty of eta is that the string inside the tags is evaluated as JavaScript code, which means you can utilize JavaScript for more complex operations. For example, to add suffix to the filename, you can do:

Note Filename
<%= it.title + "_suffix" %>.md

Processing Lists in Templates

In our data, there may be instances where we have a list of items such as authors, tags, or categories. This list is often presented as an array in JavaScript. To effectively utilize these arrays in our templates, we need to understand a variety of operations and methods that can be used with them.

Suppose that you want to include authors to the note content, you can use this basic structure:

Note Content Template
<%= it.authors %>

You'll get a list of authors split by comma:

Shira Yanovsky-Dagan,Michal Avitzour,Gheona Altarescu

However, there may be times when you only want to include the first author or the last author from the array. To do this, you can use the following:

Note Content Template
<%= it.authors[0] /** or it.authors.at(0) */ %>
<%= it.authors.at(-1) %>
💡

Although it's not in ECMAScript standard, you can use it.authors.first() and it.authors.last() to get the first and last element of the array, which are helper functions provided by Obsidian.

There are a handful of method bulti-in to the Array, in which you can find the full list here (opens in a new tab). You can find example templates in the cheatsheet, here we only discuss the most useful ones.

Join

To join the array into a string, you can use the join method (opens in a new tab), for example:

Note Content Template
<%= it.authors.join(", ") %>

This way the result will have a space after each comma between each author:

Shira Yanovsky-Dagan, Michal Avitzour, Gheona Altarescu

forEach

You can use the forEach method (opens in a new tab) to iterate through the array and render each element, for example:

Note Content Template
- authors:
<%- it.authors.forEach(author => { %>
  - <%= author -%>
<% })  %>

This will render the following:

- authors:
  - Shira Yanovsky-Dagan
  - Michal Avitzour
  - Gheona Altarescu
💡

Note here we used a tag that has no symbol next to <%, called "Scriptlet" tag, which is used for control-flow specifically, and produce no output. You'll find more usage of this tag in the following sections.

💡

Note here we use <% ... -%> in favor of <%= ... %>, which will remove the extra newline after the tag. To learn more about this, you can read the FAQ.

filter

You can use the filter method (opens in a new tab) to filter out elements that don't meet certain criteria. For example, if you only want to include authors whose last name starts with "A", you can do:

Note Content Template
<%= it.authors.filter(author => author.lastName.startsWith("A")) %>

Managing Logic - Control Flow in Templates

While writing templates, you may come across scenarios where you want to have different outputs based on different conditions. This is where control flow comes into play.

To handle these logical scenarios, we can use different JavaScript constructs like if, else, else if, switch, and ternary operators. Let's discuss each of them with the help of examples.

If...else Statement

An if statement (opens in a new tab) in JavaScript is written with the if keyword, followed by a condition in parentheses, and the code to be executed if the condition is true, inside curly brackets {}.

Here is an example to illustrate its usage:

<% if (it.date) { %>
  The date is <%= it.date %>
<% } %>

In the above code, the text "The date is ..." will only be rendered if it.date is defined.

The else statement is used to specify a block of code to be executed if the condition in the if statement is false.

Here's an example:

<% if (it.date) { %>
  The date is <%= it.date %>
<% } else { %>
  Date is not available
<% } %>

In the above code, if it.date is not defined, the text "Date is not available" will be rendered.

The else if statement can be used to specify a new condition if the first condition is false.

Example:

<% if (it.date) { %>
  The date is <%= it.date %>
<% } else if (it.year) { %>
  The year is <%= it.year %>
<% } else { %>
  Neither date nor year is available
<% } %>

In the above code, if it.date is not defined, it checks for it.year. If neither is defined, it renders "Neither date nor year is available".

Switch Statement

The switch statement (opens in a new tab) is used to perform different actions based on different conditions.

Here's an example:

<% switch (it.rating) {
  case 5:
    %>Excellent<%
    break;
  case 4:
    %>Good<%
    break;
  case 3:
    %>Average<%
    break;
  case 2:
    %>Below Average<%
    break;
  default:
    %>No rating available<%
} %>

In this code, it.rating is compared with different cases, and when a match is found, it renders the corresponding value. If no match is found, it renders "No rating available".

Ternary Operator

The Conditional operator, or ternary operator (opens in a new tab) is a shorthand for an if statement and is used when you want to decide which value to assign to a variable based on a Boolean value. It's a one-liner replacement for an if-else statement and is also known as the conditional operator.

Here is an example:

The author is <%= it.author ? it.author : 'Unknown' %>

In the above code, if it.author is defined, it will render the author's name, otherwise, it will render 'Unknown'.

For this specific usage of handling undefined values, we will discuss in details in the next section.

Graceful Handling of Undefined Values

In the world of data, not everything is always defined or filled out. This could be due to a variety of reasons, such as missing data, improper formatting, or skipped entries. When you are creating templates, these undefined values could cause your program to halt or result in incomplete outputs. Therefore, learning how to properly handle undefined values becomes an integral part of working with templates. In this part, we will delve into how to manage and maneuver around undefined values, allowing your templates to operate smoothly and gracefully even when some data points are missing.

Suppose you wan't to add authors and year to the note content, you can add the following to the template:

Note Content Template
<%= it.date %>; <%= it.authors %>

As expected, the note content will look like this:

2015; Shira Yanovsky-Dagan,Michal Avitzour,Gheona Altarescu

Seems good, right? But what if the date or authors are not defined? for example, for a web page the date may not be available. In this case, you'll get something like this:

; Shira Yanovsky-Dagan,Michal Avitzour,Gheona Altarescu

This is not what we want. So how to make sure that the note content is not rendered with a ; by itself? Here we can use conditional rendering:

Note Content Template
<% if ( it.date && it.authors.first() ) { %>
  <%= it.date %>; <%= it.authors.first() %>
<% } %>

Here we use if statement (opens in a new tab) to check if the literature has both date and first author. In this case, it will render nothing if the item doesn't have any tag. You can also use if...else statement to render different content based on the condition.

Note Content Template
<% if ( it.date && it.authors[0] ) { %>
  <%= it.date %>; <%= it.authors[0] %>
<% } else { %>
  <%= it.date %><%= it.authors[0] %>
<% } %>

Here we use the else statement to render the date and author without ; if the condition is not met. by default the undefined value will be rendered as empty string, so we can put them directly together without worrying about the extra space.

Another use case where undefined values can get tricky is access the property of something that might be undefined, for example, you may want to include the first author's last name in the note content like so

Note Content Template
<%= it.authors.first().lastName %>

but what if the first author is not defined? Template engine will throw an error and stop rendering. To avoid this, you can use the ?. (opens in a new tab) to access the property:

Note Content Template
<%= it.authors.first()?.lastName %>

In addition, there is a handy ?? (opens in a new tab) that can be used to provide a default value if the value is not available. In the default template of note filename, you can see the following:

Note Filename
<%= it.citekey ?? it.DOI ?? it.title ?? it.key %>.md

We use this template to get the first defined value from citekey, DOI, title, and key.

There are many other useful methods and operators from JavaScript that can be used in the template, We won't cover them all here, but you can find more examples in the cheatsheet, and you can also find more information about JavaScript from MDN (opens in a new tab).

In the next section, we'll discuss how to customize the specific template.