Create a note and call a template in one step (no user function or 3rd party code editor)

Hi all
I just put together a 3-liner for creating a note and calling a template in one step rather than the 2-steps native to Obsidian. I thought i would share as it may be useful for some.
Most importantly, this does not require user functions (i.e. works on mobile as well) or any sort of 3rd party plugin/code editor to Templater.

In your template folder, create a note that will generate future notes for you (I called it ‘NewFile’).
Paste the below code:

<%*
const fileName = await tp.system.prompt("File name")
const templateName = await tp.system.suggester(["Template1", "Template2"], ["Template1", "Template2"])
tp.file.create_new(tp.file.find_tfile(templateName), fileName, tp.file.folder())
%>

Replace the sample table items “Template1” & “Template2” with the name of your templates in both tables on the second line of code above.

By calling this template with code (‘NewFile’), a free text box will appear to enter your note’s name (NOT title) and a drop-down will then appear to let you choose your template from the list you have entered (in the second row above). I personally use notes’ frontmatter with a field ‘ChildrenType’ to predefine which templates to pre-populate as i use that script for all my master notes…

For you to then create a note, either call the ‘NewFile’ file from the command palette (‘create a note from a template’) or create a button somewhere (with the Buttons plugin):

\```button
name Create Note
type append template
action NewFile
\```
11 Likes

This is super awesome and I have been using it. However, every time I create a new note it jumps me to a blank Untitled file and keeps it open. The new note I made with this method also gets created perfectly but I have to go and delete the Untitled note and then open the one I created with this method. Can you possibly help me? Thank you in advance!

Unfortunately this is deemed to happen if you launch this from the command palette.
To avoid this, create a button on a note that links to your macro or use templater user functions

1 Like

Yes! Thank you! I will try that now. I’m just glad it’s expected behavior and you gave me a solution! It’s been driving me crazy! Thank you so much!

Ok I got it working! Thank you so much!

This solved an issue I was having thanks so much!

I now have a way of using a button to create a new note that I can name on creation. Tried the rename method but it was having issues with the dataview blocks in my template. Having the templater factory solved this.

Hi,
I found a solution for the Untitled note problem if you only use one template.
We will use the Core plugin Zettelkasten prefixer for the creation of a file through a shortcut and then rename the filename.

In my case I also wanted to append the current date to the file name, so in the below example that’s included.

  1. Create a template file with code similar to:
<%*
const zettelkasten = tp.date.now()
const fileName = await tp.system.prompt("File name")
await tp.file.rename(zettelkasten + "_" + fileName)
%>---
date_creation: <% tp.file.creation_date() %>
date_modification: <% tp.file.last_modified_date() %>
---

# <% this.app.workspace.getActiveFile().basename %>

(don’t leave a space/enter after %> as it would become an empty new line.)

At present, <% tp.file.title %> is broken, in the sense that if you rename the file, it will not get this updated filename. Hence, the use of this.app.workspace.getActiveFile().basename()

  1. Activate Zettelkasten prefixer and in de plugin options, set “Template file location” to the template you made (e.g. the above).
  2. In the plugin settings for Templater you have to turn on “Trigger Templater on new file creation”
  3. Use the Zettelkasten icon / set a shortcut under Hotkeys for “Zettelkasten prefixer: Create new Zettelkasten note” to create a new Zettelkasten Note
  4. You can now create a new note with 1 click / shortcut!

For multiple scripts, I might have found a lead.
Under Hotkeys there is an action named “Templater: Replace templates in the active file”.
I don’t know how to trigger this from a script, if someone can find the command, you can replace the line tp.file.create_new() in OP’s code with that code line.

I assume in this way you can enter the filename, and then it deletes the whole script and replaces it with a template of your choice.

3 Likes

I’ve been having problems with the “Extract current selection (to new file)” with this template running. Basically, the file is created nicely, but the extracted content vanishes. Suggestions?

This template is a pretty nice starting point to work off of. I had some issues with “Untitled” file duplication and wanted to use it as the default, so here’s mine.

<%*
/* Prompt for note details */
let filename = await tp.system.prompt("Enter the note's filename");
const description = await tp.system.prompt("Enter the note's description");
let title = await tp.system.prompt("Enter the note's title");

/* Some input sanitation */
/* Filename RegEx sources:
 * https://stackoverflow.com/a/6555220
 * https://stackoverflow.com/a/28604520 */
filename = filename.replace(/\s+/g, '-').toLowerCase();
filename = filename.replace(/[^a-zA-Z0-9 -]/g, "");

/* Linter does't do input sanitation on titles when auto adding aliases to the
 * front matter, so this is here to avoid value duplication after linting. */
title = title.replace(/[,]/g, "");

/* Prompt for the note's classification */
const classification = await tp.system.suggester(
	["Public", "Private", "Work", "Journal", "None (Field remains blank)"],
	["public", "private", "work", "journal", ""]
);

/* Prompt for note placement */
let path;
switch (classification) {
	case "private":
		path = "/Private/"; break;
	case "work":
		path = "/Work/"; break;  
	case "journal":
	    path = "/Journal/"; break;
	case "public":
	case "":
	default:
	    path = await tp.system.suggester(
			["root", "assets", "private", "work", "journal"],
			["/", "/assets/", "/private/", "/work/", "/journal/"]
		);
}

/* Saves a prefix check into a boolean and save current creation date */
let prefix;
if (classification == "journal") {
		prefix = true;
} else {
	prefix = await tp.system.suggester(
		["(Y) Prefix date onto filename", "(N) Do not prefix date onto filename"],
		[true, false]
	);
}

let date;
let year;
if (classification == "journal") {
	date = await tp.file.creation_date("YYYY-MM-DD");
	year = await tp.file.creation_date("YYYY");
} else {
	date = await tp.file.creation_date("YYYY-MM-DDTHH-mm");
}

/* Prompt for the note's license */
let license;
switch (classification) {
	case "private":
	case "work": 
	case "journal":
	    license = "All rights reserved"; break;
	case "":
		license = ""; break;
	case "public":
	default:
		license = await tp.system.suggester(
			["CC0 (Text/images)", "Unlicense (Source code)", "All rights reserved"],
			["CC0", "Unlicense", "All rights reserved"]
		);
}

/* Prompt for note type */
const type = await tp.system.suggester(
	["Meta", "Information", "Guide"],
	["meta", "information", "guide"]
);

/* Save filename plus prefix to a let variable */
let filename_date = date + "_" + filename;
let uuid = tp.user.uuid();
let uuid_short = filename + "_" + uuid.slice(0, 8);

/* If-check the prefix boolean
 * NOTE: The file rename is required to avoid the creation of "Untitled" files */
if (prefix == true) {
	/* Rename using filename_date and move */
	await tp.file.rename(filename_date);
	if (classification == "journal") {
		await tp.file.move(path + year + "/" + filename_date)
	} else {
		await tp.file.move(path + filename_date);
	}
} else {
	/* Rename using filename and move */
	await tp.file.rename(filename);
	await tp.file.move(path + filename);
}

/* Print the multiline string as the template
 * NOTE: You can use filename_date in aliases if that's wanted. */
tR = `---
title: ${title}
aliases: [${title}, ${uuid_short}, ${uuid}]
author: 
description: ${description}
created: ${tp.file.creation_date("YYYY-MM-DDTHH:mm:ssZ")}
updated: ${tp.file.last_modified_date("YYYY-MM-DDTHH:mm:ssZ, [week] WW [of] MMMM [on] dddd")}
classification: ${classification}
license: ${license}
type: ${type}
tags: 
views: 1
---

# ${title}

${tp.file.cursor()}`;
%>

It uses a user script file for generating uuid’s, so you’ll need to add that as well for this to work.

/* Source: https://stackoverflow.com/a/2117523 */
function uuidv4() {
    return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c =>
        (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
    );
}

module.exports = uuidv4;

Note that you’ll need the Linter plugin for this and have enabled Trigger Templater on new file creationEnable Folder Templates → and set the template as the default for root. You’ll also need to enable Automatic jump to cursor.

Also note that you’ll likely have to change some of the path names and such to fit your own vaults if you want to use this.

As an aside, while this works perfectly via Ctrl + N once the correct settings have been applied, if you select the template via Alt + N instead. You’ll have to use the keybind to auto-remove tp.file.cursor() even though the cursor moves to the correct position. This is likely just a bug in Templater.

1 Like