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:
<%= 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:
<%= 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:
<%= 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:
<%= 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:
<%= 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:
- 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:
<%= 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:
<%= 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:
<% 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.
<% 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
<%= 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:
<%= 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:
<%= 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.