Add metadata on page using scripting

What I’m trying to do

Trying to add metadata to a page using dataviewjs.

Things I have tried

const mods = dv.pages('"Worlds/Compendium/Modifiers"')
const hp_sum = mods.hp.array().reduce((acc, val) => acc + val, 0)
dv.paragraph("[HP::" + hp_sum + "]")

This way the paragraph renders as it should. But when I try using
= this.hp , then I get the following in reader mode:

image

Do you know a workaround to this. Or an overall alternative to add metadata on pages using js.

Its acting really weird and I don’t know why.

$= String("[HP:" + ":1]")

= this.hp

This code gives me this result:
image

The reason I’m doing this approach is because if I don’t separate the string there It would render the whole thing as plain text.

Any idea how to do this the right way.

Have you enabled inline queries within code blocks in your dataview settings? That could possibly explain some of this behavior…

Related to your original request, I’m thinking that you can’t return something from dataview and expect dataview to run again on that output (if that makes sense). It’s like a variant over the theme which came first; the chicken or the egg? I think you need to either use someone like app.fileManager.processFrontMatter(), or trigger a call to a plugin like the metadata which have functions for changing fields.

Hi, thanks for the answer! I have the settings enabled.

I understand your concern, but I’d still rather try. I looked up processFrontMatter and would request further assistance with it.

For the following dataviewjs code:

await app.fileManager.processFrontMatter(view.file, (fm) => {
fm["hp"] = 1;
});

I get the error:

Evaluation Error: TypeError: Cannot read properties of undefined (reading 'extension')
    at e.<anonymous> (app://obsidian.md/app.js:1:1491314)
    at app://obsidian.md/app.js:1:237027
    at Object.next (app://obsidian.md/app.js:1:237132)
    at app://obsidian.md/app.js:1:236048
    at new Promise (<anonymous>)
    at v (app://obsidian.md/app.js:1:235793)
    at e.processFrontMatter (app://obsidian.md/app.js:1:1491216)
    at eval (eval at <anonymous> (plugin:dataview), <anonymous>:1:76)
    at eval (eval at <anonymous> (plugin:dataview), <anonymous>:4:4)
    at DataviewInlineApi.eval (plugin:dataview:18638:16)
    at evalInContext (plugin:dataview:18639:7)
    at asyncEvalInContext (plugin:dataview:18646:16)
    at DataviewJSRenderer.render (plugin:dataview:18670:19)
    at DataviewRefreshableRenderer.maybeRefresh (plugin:dataview:18254:22)
    at HTMLDivElement.r (app://obsidian.md/enhance.js:1:11895)

I found the code snippet in the other post and the TFile argument I’ve given is wrong.

This works:

const currentFile = app.workspace.getActiveFile();
await app.fileManager.processFrontMatter(currentFile, (fm) => {
fm["hp"] = 1;
});

There are indeed multiple references with various context to be used slightly different depending on whether you’re using a DQL query or dataviewjs query:

  • a link – Which has the properties of path, display, etc
  • a page reference - Which is used to access the properties of a given page (and related file information)
  • a TFile reference – Which is more of an internal file reference

In DQL you normally use the link and page references, and they sometimes even coexists to make it easier for the end user. Within dataviewjs, they don’t coexists and you need to be careful whichever variant you’re using. You can however access both variants with proper code. Lastly the TFile can be found as a return option from various calls, and I’ve so far used the following method (given a file path to the page I want to access):

// Using Templater's tp.file.find_tfile
const tp = app.plugins.plugins['templater-obsidian'].templater.current_functions_object
const tFile = await tp.file.find_tfile(file.path)

// Using _Dataview_ functions
const tFile =
  app.metadataCache
     .getFirstLinkpathDest(dv.io.normalize(file.path, "")

See Open a file in callback - #2 by holroy for slightly more information on these two methods. In the code above file refers to some file link, like in a link context, so that the .path is available providing the file name (with path) of that link.

Hopefully this explains some more about the TFile thingy, and how to access is from dataviewjs (or similar). The app.workspace.getActiveFile is another way, with its caveats, to get to a relevant TFile in your context.

Thank you so much for your help! This is what I ended up with and I’m very happy with the results. This code adds together the values and lists of the given property names and adds them to the frontmatter of one file.

const curr = dv.current();
const currentFile = app.vault.getAbstractFileByPath(curr.file.path);
const config = dv.page("Worlds/Compendium/Rules/Config.md")

const mods = dv.pages(config.modifiers_path)
	.where(p => curr.modifiers
		.some(mod => dv.equal(mod, p.file.link)));

await app.fileManager.processFrontMatter(currentFile, (fm) => {
fm["hp"] = mods.hp.array().reduce((acc, e) => acc + e, 0);
fm["spells"] = new Set(mods.spells.path.map(p => "[[" + dv.page(p).file.name + "]]"))
});

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