Get Dataview inline field from different file using Templater

Hello,

I am trying to accomplish the following in a template using Templater and Dataview:

What I’m trying to do

(1) Select a note (foo) using the system suggester in Templater (This part works fine):
let foo = (await tp.system.suggester((item) => item.basename, app.vault.getMarkdownFiles())).basename

(2) Retrieve the value from a Dataview inline field (foo_meta:: value) of note foo:
This is the part I'm stuck on.

Things I have tried

I’ve tried finding it by Tfile:
tp.file.find_tfile(foo).foo_meta).

I’ve also tried to access it with a dataviewjs query: dv.paragraph(dv.pages('"tp.file.find_tfile(foo).path"').foo_meta)

(3) Output the value from foo_meta in the new file by calling the template.

Summary

It seems like there’s got to be a better way to access this data. I assume it has to involve Dataview since Templater doesn’t natively recognize the :: as indicating metadata? Note, I don’t want to place the metadata in YAML because some of it uses [[links]] and they don’t update automatically when files are renamed when they are in YAML.

Thanks for the help!

I don’t know to much about Templater (I know it exists and not much more).
I’m also a dumb in JS… But if you’re using Templater with a js command, maybe you can call dataview API…
Maybe something like
DataviewAPI.page("foo").foo_meta
I’m not sure if with page() you need the full path of the page or only the title. If you don’t have another file in your vault with the same title, then I think the title is enough.

I’ve used something like the following function in the past where content is a string containing the entire content of a file and field is a string naming the inline field I was interested in

function fieldValue(content,field)
{
   const regex = new RegExp(field + ":: (.*)\n");
   let m = content.match(regex);
   if (m === null)
   {
      return '';
   }
   else
   {
      return m[1];
   }
}

Note, if you have a TFile in a variable called tf, you can get its contents using let content = await app.vault.read(tf);

Also note, if the content happens to have more than one instance of the inline field, the above function only grabs the first occurrence.

You were so close:

<%*
const value = DataviewAPI.page(tp.file.find_tfile(foo).path).foo_meta
tR += value // output the value in the template
%>

This worked! Thank you!

I basically used it to open access to all the pages meta, i.e.:

<%*
const fooMeta = DataviewAPI.page(tp.file.find_tfile(foo).path)
%>

some_metadata: <% fooMeta.some_metadata %>
other_metadata: <% fooMeta.other_metadata %>

Works like a charm!

tmykkanen - would you mind laying the information out to demonstrate exactly how you’re doing this? Seems super interesting but having trouble recreating…thx!

@Twita Gladly.

It’ll probably be easiest to explain by just demonstrating my use case. I’m a pastor and I use Templater to set up a template for a new sermon each week. I wanted the template to automate as much metadata as I could. My structure is essentially one main MOC for each book/sermon series and then individual documents for each week’s sermon.

So when I call the template, here’s what happens, along with the code that accomplishes it. (Note, it’s all wrapped in Templater’s tags <%* -%>

(1) I am prompted for the sermon series. I’m preaching in Matthew, right now, so I would select = Matthew Sermon Series MOC. This assigns the .basename of that file (i.e. “= Matthew Sermon Series MOC”) to the variable series_MOC.

let series_MOC = (await tp.system.suggester((item) => item.basename, app.vault.getMarkdownFiles())).basename

(2) I am prompted for the sermon text. I enter something like “Matthew 11:20–30”. It’s assigned to the variable text.
let text = await tp.system.prompt("Enter your sermon text.")

(3) I want access to the metadata of my = Matthew Sermon Series MOC file. This metadata is in a callout box at the front of the file that looks like this:

> [!meta]+ Extra Meta
> parent:: [[Sermon Preparation MOC]]
> series_book:: [[Matthew]]
> series_name:: "The King and His Kingdom"

I did it this way because the link titles update automatically if the linked filename changes (and they are visible as backlinks) vs. linked filenames in YAML which do not update nor show as backlinks.

To access this metadata, I assign the results of a Dataview Page query to a variable using this code:

let sermonMeta = DataviewAPI.page(tp.file.find_tfile(series_MOC).path)

(4) This enables me to call each metadata item in the body of the template. For instance, at the beginning of each sermon file, I have a callout box that looks like this in the template:

> [!meta]+ Extra Meta
> parent:: [[<% series_MOC %>]]
> series_order:: <% tp.user.getSeriesNumber(tp, "[[" + series_MOC + "]]") %>
> sermon_text:: "<% text %>"
> sermon_title:: 
> series_name:: "<% sermonMeta.series_name %>"
> series_book:: <% sermonMeta.series_book %>

You can see that I am calling the metadata using the variable sermonMeta and dot syntax, e.g. .series_name which reflects the Dataview inline keys from the sermon series MOC (series_name:: "The King and His Kingdom in this case).

This is an example of using Dataview for the query and then assigning the result as a static result using Templater, which is the heart of this system and what I was trying to do. I could just have a simple dataview query there, but I needed a static output, which is why I needed to access the inline field from a specific file. There might be a better way to do it, but this is working so far. :slight_smile:

(5) I do something similar with a javascript snippet for finding the series number. I call the snippet with tp.user.getSeriesNumber as seen above. The code in the snippet looks like this:

function getSeriesNumber (tp, seriesTag) {
  let result = "";
  
  const sermonList = app.plugins.plugins.dataview.api
    .pages(seriesTag + 'AND !"000 Home" AND !"Resources/Templates"')
    .sort(p => p.series_order, 'desc')
    .map(p =>[p.series_order]);

  result = sermonList[0];
  result++;
  return result;
}

module.exports = getSeriesNumber;

Essentially what’s happening is the same thing as above. I’m using a Dataview query to get a specific sermon (in this case the most recent), find it’s series_order value, increment it by 1, and return it for the new series order. I needed this both because it’s helpful information and also so I can sort my table in the Sermon MOC by series_order as my files are named Sermon - Matthew 11_20–30 and therefore don’t sort by filename (and cdate isn’t always preferable to me).

Hope that’s helpful in understanding how this is working for me. I only know a little javascript and I gain most of my understanding from trying to read documentation, so my code may be wonky at times. But everything’s working now thanks to those above who helped me figure out why it wasn’t (and some kind hearts on the Obsidian Discord!).

2 Likes

Holy cow…thanks so much for the detailed explanation. I’m going to have to study this one a bit !! Much, much appreciated.

@tmykkanen Thanks so much! This is very useful. I am inspired to create a similar setup.

Although, in your use case, it seems pretty likely that usually the metadata values used within the Sermon MOCs would stay the same, I think this technique could be expanded to provide a framework for building and adapting the MOC source metadata values such that they can be like living, expandable profiles to be tapped into by various adjustable “getter” templates (the template you use for to create each weekly sermon).

It helps separate the source data from the actual use of that data. I believe this would make managing and editing templates much easier for my workflow. I still have to actually try to implement what I am imagining, but I am feeling hopeful. At the very least, this has helped me better understand the power of Templater to access data stored using the dataview key:: value syntax via the dot operator. Many thanks to you and those you helped you put this together!

@I-d-as indeed. The sermon metadata is pretty constant for me from week to week (I’ll be in Matthew for another year and a half, lol), but I agree with you that the general flow of selecting the applicable metadata via the item selector and then accessing it with the dot syntax is more broadly applicable.

I’m glad it got the ideas flowing for you; that was my hope by providing a more comprehensive example. Obsidian and Templater and Dataview are such awesome tools for many varied applications!

1 Like

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