My workaround for the date placeholder conflict (Templater, Periodic Notes)

I believe this has not been reported before, at least I couldn’t find anything on this forum. After some experimenting, I finally found a solution to my particular problem, and I thought that maybe someone might be interested. Also, I found some posts (eg., here, or here) also looking for a solution to a similar problem, at least if I understood the authors correctly.

TL;DR

I found a way to “unify” the {{date}} and tp.date.now placeholders. See below for the solution.

The setup

  • In my vault, I store all my daily journal entries, which I create and navigate using the Periodic Notes and Calendar plugins.
  • I also use the Templater plugin instead of the regular Templates Core plugin, because I make use of the Folder Templates feature (the vault also stores other, irregular notes in different folders).
  • Finally, I use the Homepage plugin to show a welcome page at startup, which contains a link to today’s journal entry (using dataviewjs).

There are two ways for me to create today’s journal entry, if not already existing:

  1. via the Periodic Notes plugin (this includes opening today’s entry from the Calendar plugin’s widget in the right sidebar).
    In this case, the Periodic Notes plugin inserts the current date everywhere the template contains a {{date}} placeholder, which does also work as expected if I create, say, yesterday’s entry using this method; or
  2. by clicking the link on the welcome page.
    In this case, the note will be created in the exact same location and with the exact same filename format that are also set in the Periodic Notes settings. The Templater plugin, especially it’s Folder Templates feature, ensures that the correct template is used, inserting today’s date everywhere the template contains something like <% tp.date.now() %>.

The problem

In principle, I would need 2 seperate templates, as there are 2 different date placeholders {{date}} and tp.date.now, used by the Periodic Notes or Templater plugin, respectively, depending on how today’s note is created; but I dislike the idea of having to manage 2 templates that are supposed to do the exact same thing, especially since the template contains large blocks of dataviewjs code that I happen to modify quite frequently.

The solution

The solution I came up with:

<%* 
const date_from_pnp = "{{date:YYYY-MM-DD}}";
let datestring;
if (date_from_pnp === "{" + "{date:YYYY-MM-DD}}") {
	datestring = tp.date.now("YYYY-MM-DD");
} else {
	datestring = date_from_pnp;
}
-%><% datestring %>

If this is triggered via Periodic Notes (and I suppose, also the Daily Notes plugin, but have not tested), the first {{date}} placeholder will be replaced with the date corresponding to the created note, before Templater is triggered. Therefore, date_from_pnp holds the actual date string, not the placeholder, which is then compared to the not expanded placeholder (the second placeholder is “masked”, ie. cannot be expanded by Periodic Notes, by constructing it from substrings). In case the comparison is true, the note has in fact been created without Periodic Notes, and we use the Templater tp.date.now to insert the current date, assuming the note was created by clicking the link on the welcome page. Otherwise, if the comparison is false, the first {{date}} placeholder must have been expanded before being assigned to date_from_pnp, and we can use Templater to just insert the contents of date_from_pnp.

I am aware that this could possibly be solved/worked around in other ways, eg., by extracting the date from the note title, or via templates “calling” other templates. However, I find this solution quite elegant and hope that it might help someone at some point.

:beers:

3 Likes

Nice solution!

You can shorten the code as follows and avoid the let declaration:

<%*
const date_from_pnp = "{{date:YYYY-MM-DD}}"
const datestring = date_from_pnp === ("{" + "{date:YYYY-MM-DD}}") ? tp.date.now("YYYY-MM-DD") : date_from_pnp
-%><% datestring %>

Thanks for the suggestion!

Although I would always prefer a longer but more “folded” chunk of code over a one-liner from hell, your suggestion does indeed look more elegant. It is only a few days ago that I wrote my very first line of javascript.

Given the occasion, what’s the preferred practice of accepting answers in this category? Since I didn’t really ask a question…

1 Like

You’re welcome, and hats off, for your first steps with JavaScript!

You should be able to mark the answer as the “solution” if you accept it as such. I have not initiated a discussion myself, but I have seen it.

I just started using Obsidian a few days ago and am encountering this exact issue. Sorry to ask how to do this, but where do I implement this code in my vault?

There are some bits and pieces missing from the original post, and I’m partially guessing, but I somewhat sure that I’ve got it correct.

  • You need to have the Templater plugin installed, and possibly Periodic notes (if you’re not satisfied with what the core Daily notes plugin delivers)
  • In the settings section for Templater, you need to turn on the Trigger Templater on new file creation, which ensures that whatever way a new file is created Templater is also triggered to evaluate any templater stuff
  • The OP has also defined multiple Folder templates in their setup. This makes it possible for you to create a folder in any of those given folders (or sub-folders) and Templater is triggered. And herein lies part of the issue…
  • The issue now becomes what created the file in the first place? Was it the Daily Notes/Periodic Notes plugin which gives value to the {{date}} stuff? Or is just the Templater since it was created as a new file in some folder (and Templater would evaluate the <% ... %> stuff)?
  • One common solution is to split the template files, and have one set of template files for each of the plugins. But then you get a synchronisation issue when updating either version, so it’s better to just use one
  • With this setup, and the solution provided, another key fact is that Templater is always evaluating the newly created file, but in some cases the other template engine has already been run.

The reason the solution therefore works, is that it tries to detect whether the core template engine has been run through some trick settings of variables within a Templater block, and behave accordingly.

I do believe a simpler test would be:

<%* const coreEngineTriggered = "{{date:---}}" == "---"
const date_from_pnp = coreEngineTriggered ? "{{date:YYYY-MM-DD}}" : tp.date.now("YYYY-MM-DD")
%>
...
My date now is <% date_from_pnp %> 

This utilises a slight hack related to setting the date format to be only dashes (which also can be used by the core engine to workaround an issue with a leading property code fence ), and it sets the coreEngineTriggered to be a boolean depending on whether it has been run or not. This can then also be used later on to determine whether one would need to move the file, or verify the file location or similar stuff. Just encapsulate it in an if block with coreEngineTriggered