Templater is executing two templates for a single file creation

This is an issue that has been already reported at github: Racecondition between "Create new note from template" and "Folder Template Configuration" · Issue #710 · SilentVoid13/Templater · GitHub
But it is not getting any responses, so I decided to leverage the knowledge of the people in this forum.
The issue is that, when creating a new file from template, templater calls the choosen template but also the default template (the catch all in the configuration for folder templates).

I have a default template containing this:

<%*
  let title = tp.file.title
  if (title.startsWith("Untitled")) {
    title = await tp.system.prompt("Title");
    await tp.file.rename(title);
  } 
-%>
---
tags: <% tags %>
aliases: 
title:  <%* tR += title %>
created: <% tp.date.now() %>
---

<%tp.file.cursor(1)%>

Then, if I run “Create new note from template” I don’t expect the default template being triggered, but in practice it is being triggered, and because the default template has a prompt for a title, I get the prompt.

Now let’s say I have a second template with this content:

<%* let title = await tp.user.ensure_title(tp) -%>
# <%* tR += title %>

The user script looks like this:

module.exports = async function (tp) {
    let title = tp.file.title
    if (title.startsWith("Untitled")) {
        title = await tp.system.prompt("Title");
        await tp.file.rename(title);
    }
    return title;
}

If I now call ‘create file from template’ using the second template as argument, I get a prompt twice, one coming from the default template, and the final one coming from my second template. The result is a correctly generated page, but I’m wondering why is the default template being triggered. If I input two different values, only the second value gets used, what makes me thing that the order of execution is:

  1. Default template
  2. Selected template

Default template means that I have it setup to run when a note in the folder / is created.

Have you tried to use a lockTemplateCreation (or whatever you want to name it) field attached to the tp object in the script?

You could then use it to check for it in every template as a first action and skip anything if true or equals a certain value: conditional templating is your friend.

Is that a common thing to do? I mean, attach stuff to the global template object. I don’t want to start polluting stuff and build my own mess.

No. But it is a workaround until the official fix is out.

There is an alternative if you are willing to install another plugin.

With CustomJS you can define a class which can be loaded at startup. The plugin creates an instance that can be used anywhere where JavaScript can be used, e.g., within a Templater script.

This solution is “clean” and future-proof.

CustomJS class:

// user.script.custom.template-lock.js - version 0.0.1+20230811
// license: MIT - Copyright (c) 2023 Stefano F. Rausch <[email protected]>

class Template {
    static #lock = false

    static get lock() {
        return Template.#lock
    }

    static set lock(boolean = true) {
        return Template.#lock = boolean === true
    }
}

Templater script start:

<%_*

const {Template: template} = customJS

if (!template.lock) {
    template.lock = true

_%>

Your template goes here:

TEMPLATE

Templater script end:

<%-*
}

template.lock = false

-%>

I had no Templater race conditions – and in some cases, duplicate files in different folders – after using this approach.

Thanks, that looks like a neat aolution. Still wondering what are the consequences of ruining two templates.

Another question…if templater allows to run some script a startup, why not use that to attach something to the global namespace?

1 Like

According to Templater’s documentation: " Scripts should follow the CommonJS module specification, and export a single function.". Hence, you can return a class object from that function and have the same functionality.

I intensively use Templater for “templating”. :wink: What I miss is the flexibility to save my JavaScript scripts wherever I want to – you have to save them in one folder. The exception of the rule are startup scripts.

CustomJS is more supportive in that respect. It is a matter of preference.

My experience, especially when one template moves the current note: duplicate files in different folders with different content, i.e., one with no content and the other with the template applied.

There might be other results, but that is guesswork: mixed-up content etc.

Or… you can abuse the module system (this is a common thing, not edgy) and just make that function the only method to handle the lock.

let locked = false
module.exports = (setTo) => {
    if (setTo !== underined) {
        locked = setTo 
    }
    return locked
}

Then you can call without args to get the value, or with a value to set it. Its a bit brittle, but everything is in JS :joy:

1 Like

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.