Autofill properties based on folder

Use case or problem

I manually write the folder the file is in in the properties
Because I use graph view to give me anything related to this property.
It would be great to autofill property based on folder structure logic.

Specific use case

I use Obsidian for TTRPG world-building/playing. Therefore I get a lot of sub-locations or even sub-sub locations (sometimes I make sub-sub-sub-sub locations), and also other notes which live in that location without needing to backlink it.

When running a game I have a graph view (filtered for property [match] “location_X”),

Proposed solution

make a new property type for autofilling based on folder structure.
So that I can have a template with

location: folder.above

Ideally, I would like to have not just the folder the note is in, but the logic to autofill multiple

Current workaround (optional)

  1. I fill in properties manually for the folder (or folders) it´s in
  2. I use multiple filters in the graph view
    Example filter [“Location”:Bazzoxan] OR path:2-World/Exandria/Gazetter/Wildemount/Xhorhas/Bazzoxan

The issue with solution 1. is tedious, but with solution 2 its a little overcomplicated.

In general, I think logical statement for properties would be useful for many usecases.

Related feature requests (optional)

[somewhat similar but #tag related](https://forum.obsidian.md/t/folder-properties-add-tag-on-note-creation-based-on-the-notes-folder/74610)

How do I make it so that when I move a file to a certain folder (for example a People folder), a certain set of properties are “loaded” into that note?

I might want these five fields for each person I add, for example:

Role
Organisation
Phone
Email
Interests

Alternatively, if I add a “supertag” (like in Tana), those properties show up in the file.

/ Kristian

This is what templates are for.

Most people use the Templater community plugin.

A similar scenario was mentioned here:

  • There is a guide there as well. I think it’s better to learn a little bit about it (learn how to fish) than expect to be served a ready-made answer (the fish) each time.

Thank you! Feels like I need to become a programmer to make this work… Don’t know how many hours I want to invest. :slight_smile:

A couple of tips:
In the description of the video, there is a link to a GitHub account, where you can copy and paste code from or even download the full demo vault, so you don’t need to type stuff, only change some details.

Oh, that’s great!! Thanks.

I am no programmer, either.

But you can easily understand and tailor the logic of the meta template to your needs.

E.g. the important thing is that the last one under <%-* } else { -%> should be the one that doesn’t qualify for any criteria (trigger with a dash) set above, so it will be a normal or default template for everyday needs. And if you need extra templates, you add them in the middle with the else if bits.
E.g.:

<%-* /* Extracting the Trigger if there is one */ -%>
<%-*
if (tp.file.title.includes("-")){
	var trigger = (tp.file.title.split("-")[0]).trim()
} else {
	var trigger = ""
} -%>
<%-* /* In case it is not a meta-template picker note */ -%>
<%-* if (tp.file.title.startsWith("Weekly")) { -%>
<%-tp.file.include("[[Weekly Review Template]]")-%>
<%-* } else if (tp.file.title.startsWith("Monthly")) { -%>
<%-tp.file.include("[[Monthly Review Template]]")-%>
<%-* } else if (tp.file.title.includes("template")) { -%>
<%-tp.file.include("[[Template for Template]]")-%>
<%-* /* If it is a trigger note then check which note */ -%>
<%-* } else if (trigger === "webclip") { -%>
<%-tp.file.include("[[WebClipper Template]]")-%>
<%-* } else if (trigger === "z") { -%>
<%-tp.file.include("[[Simple Zettel Template]]")-%>
<%-* } else if (trigger === "comp") { -%>
<%-tp.file.include("[[Computerese Template]]")-%>
<%-* } else if (trigger === "Comp") { -%>
<%-tp.file.include("[[Computerese Template]]")-%>
<%-* } else if (trigger === "Google") { -%>
<%-tp.file.include("[[Google iFrame Template]]")-%>
<%-* } else if (trigger === "google") { -%>
<%-tp.file.include("[[Google iFrame Template]]")-%>
<%-* } else if (trigger === "babla") { -%>
<%-tp.file.include("[[Babla iFrame]]")-%>
<%-* } else if (trigger === "Babla") { -%>
<%-tp.file.include("[[Babla iFrame]]")-%>
<%-* } else if (trigger === "Wikipedia") { -%>
<%-tp.file.include("[[Wikipedia iFrame Template]]")-%>
<%-* } else if (trigger === "Wiki") { -%>
<%-tp.file.include("[[Wikipedia iFrame Template]]")-%>
<%-* } else if (trigger === "wiki") { -%>
<%-tp.file.include("[[Wikipedia iFrame Template]]")-%>
<%-* } else if (trigger === "Wiktionary") { -%>
<%-tp.file.include("[[Wiktionary iFrame Template]]")-%>
<%-* } else if (trigger === "Wikt") { -%>
<%-tp.file.include("[[Wiktionary iFrame Template]]")-%>
<%-* } else if (trigger === "wikt") { -%>
<%-tp.file.include("[[Wiktionary iFrame Template]]")-%>
<%-* } else if (trigger === "word") { -%>
<%-tp.file.include("[[WordSense iFrame Template]]")-%>
<%-* } else if (trigger === "Word") { -%>
<%-tp.file.include("[[WordSense iFrame Template]]")-%>
<%-* } else if (trigger === "Wordsense") { -%>
<%-tp.file.include("[[WordSense iFrame Template]]")-%>
<%-* } else if (trigger === "pallas") { -%>
<%-tp.file.include("[[Pallas iFrame Template]]")-%>
<%-* } else if (trigger === "Pallas") { -%>
<%-tp.file.include("[[Pallas iFrame Template]]")-%>
<%-* } else if (trigger === "search") { -%>
<%-tp.file.include("[[Arcanum Template]]")-%>
<%-* } else if (trigger === "Search") { -%>
<%-tp.file.include("[[Arcanum Template]]")-%>
<%-* } else if (trigger === "lsj") { -%>
<%-tp.file.include("[[LSJ iFrame Template]]")-%>
<%-* } else if (trigger === "LSJ") { -%>
<%-tp.file.include("[[LSJ iFrame Template]]")-%>
<%-* } else if (trigger === "greek") { -%>
<%-tp.file.include("[[LSJ iFrame Template]]")-%>
<%-* } else if (trigger === "Greek") { -%>
<%-tp.file.include("[[LSJ iFrame Template]]")-%>
<%-* } else if (trigger === "s") { -%>
<%-tp.file.include("[[Source Prompt Template]]")-%>
<%-* } else if (trigger === "source") { -%>
<%-tp.file.include("[[Source Prompt Template]]")-%>
<%-* } else if (trigger === "sbook") { -%>
<%-tp.file.include("[[Book Source Template]]")-%>
<%-* } else if (trigger === "svid") { -%>
<%-tp.file.include("[[Video Source Template]]")-%>
<%-* } else if (trigger === "j") { -%>
<%-tp.file.include("[[Journal Template]]")-%>
<%-* } else if (trigger === "journal") { -%>
<%-tp.file.include("[[Journal Template]]")-%>
<%-* } else { -%>
<%-* /* Default Note if it's not a trigger note */ -%>
<%-tp.file.include("[[Default Note Template]]")-%>
<%-* } -%>

In the individual templates you are calling, you populate those with custom information:
E.g.

---
obsidianUIMode: preview
obsidianEditingMode: live
tags:
  - greeketymology
  - LSJ
  - iframeforbrowsing
publish: false
date created: <% tp.date.now("YYYY-MM-DD")%>
date modified: <% tp.date.now("YYYY-MM-DD")%>
---

# <%-* tR += tp.user.getTitleSnippet(tp.file.title) -%>

<iframe width="700" height="900" src="https://lsj.gr/index.php?search=<%-* tR += tp.user.getTitleSnippet(tp.file.title) -%>"></iframe>
  • In this one, e.g. I embed the search term so I get the relevant results in the iframe.

In my default template, I move the files into designated subfolders based on first character (I don’t want over 10k files in a single folder):

<%-* 
if (tp.file.title.charAt(0) === "A") {
    await tp.file.move("A" + "/" + tp.file.title)
}
else if (tp.file.title.charAt(0) === "Á") {
    await tp.file.move("A" + "/" + tp.file.title)
}
else if (tp.file.title.charAt(0) === "B") {
    await tp.file.move("B" + "/" + tp.file.title)
}
else if (tp.file.title.charAt(0) === "C") {
    await tp.file.move("C" + "/" + tp.file.title)
}
else if (tp.file.title.charAt(0) === "D") {
    await tp.file.move("D" + "/" + tp.file.title)
}
else if (tp.file.title.charAt(0) === "E") {
    await tp.file.move("E" + "/" + tp.file.title)
}
else if (tp.file.title.charAt(0) === "É") {
    await tp.file.move("E" + "/" + tp.file.title)
}
else if (tp.file.title.charAt(0) === "F") {
    await tp.file.move("F" + "/" + tp.file.title)
}
else if (tp.file.title.charAt(0) === "G") {
    await tp.file.move("G" + "/" + tp.file.title)
}
else if (tp.file.title.charAt(0) === "H") {
    await tp.file.move("H" + "/" + tp.file.title)
}
else if (tp.file.title.charAt(0) === "I") {
    await tp.file.move("I" + "/" + tp.file.title)
}
else if (tp.file.title.charAt(0) === "Í") {
    await tp.file.move("I" + "/" + tp.file.title)
}
else if (tp.file.title.charAt(0) === "J") {
    await tp.file.move("J" + "/" + tp.file.title)
}
else if (tp.file.title.charAt(0) === "K") {
    await tp.file.move("K" + "/" + tp.file.title)
}
else if (tp.file.title.charAt(0) === "L") {
    await tp.file.move("L" + "/" + tp.file.title)
}
else if (tp.file.title.charAt(0) === "M") {
    await tp.file.move("M" + "/" + tp.file.title)
}
else if (tp.file.title.charAt(0) === "N") {
    await tp.file.move("N" + "/" + tp.file.title)
}
else if (tp.file.title.charAt(0) === "O") {
    await tp.file.move("O" + "/" + tp.file.title)
}
else if (tp.file.title.charAt(0) === "Ó") {
    await tp.file.move("O" + "/" + tp.file.title)
}
else if (tp.file.title.charAt(0) === "Ö") {
    await tp.file.move("O" + "/" + tp.file.title)
}
else if (tp.file.title.charAt(0) === "Ő") {
    await tp.file.move("O" + "/" + tp.file.title)
}
else if (tp.file.title.charAt(0) === "P") {
    await tp.file.move("P" + "/" + tp.file.title)
}
else if (tp.file.title.charAt(0) === "Q") {
    await tp.file.move("Q" + "/" + tp.file.title)
}
else if (tp.file.title.charAt(0) === "R") {
    await tp.file.move("R" + "/" + tp.file.title)
}
else if (tp.file.title.charAt(0) === "S") {
    await tp.file.move("S" + "/" + tp.file.title)
}
else if (tp.file.title.charAt(0) === "T") {
    await tp.file.move("T" + "/" + tp.file.title)
}
else if (tp.file.title.charAt(0) === "U") {
    await tp.file.move("U" + "/" + tp.file.title)
}
else if (tp.file.title.charAt(0) === "Ú") {
    await tp.file.move("U" + "/" + tp.file.title)
}
else if (tp.file.title.charAt(0) === "Ü") {
    await tp.file.move("U" + "/" + tp.file.title)
}
else if (tp.file.title.charAt(0) === "Ű") {
    await tp.file.move("U" + "/" + tp.file.title)
}
else if (tp.file.title.charAt(0) === "V") {
    await tp.file.move("V" + "/" + tp.file.title)
}
else if (tp.file.title.charAt(0) === "W") {
    await tp.file.move("W" + "/" + tp.file.title)
}
else if (tp.file.title.charAt(0) === "X") {
    await tp.file.move("X" + "/" + tp.file.title)
}
else if (tp.file.title.charAt(0) === "Y") {
    await tp.file.move("Y" + "/" + tp.file.title)
}
else if (tp.file.title.charAt(0) === "Z") {
    await tp.file.move("Z" + "/" + tp.file.title)
} 
-%>
---
title: <%tp.file.title%>
aliases:
  - <%tp.file.title.toLowerCase()%>
tags:
  - formatted🟢
publish: true
parent_topic:
  - <% tp.system.prompt("Care to add more parent topic?") %>
  - <% tp.system.prompt("Want to define parent topic?") %>
date created: <%tp.date.now("YYYY-MM-DD")%>
date modified: <%tp.date.now("YYYY-MM-DD")%>
---

# <%tp.file.title%>

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

One more:

<%-* 
var cleanTitle = tp.user.getTitleSnippet(tp.file.title)
var title = `${cleanTitle}`
await tp.file.rename(`${title}`)
await tp.file.move("999 Computerese" + "/" + `${cleanTitle}`)
_%>
---
title: <%tp.user.getTitleSnippet(tp.file.title)%>
aliases:
tags:
  - computer
publish: false
topic:
date created: <% tp.date.now("YYYY-MM-DD")%>
date modified: <% tp.date.now("YYYY-MM-DD")%>
---

# <%-* tR += tp.user.getTitleSnippet(tp.file.title) -%>


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

As others have suggested, you can use templater plugin with autotrigger.
You could also probably use a Base. With the right properties in view and filters set up, the new button should create a new note that satisfy your needs.

Alternatively: Super FR: Enhance Obsidian with a type system for notes and database-like views (metadata, object oriented model)