How to include a dynamic section in many pages, that can be changed later

What I’m trying to do

I’m trying to re-build in Obsidian a simple Wine cellar management system. I create entries for wines, for wine producers, and I use properties to document where they’re from, which colour, how many bottles in stock, etc.

I’d like to have a templated section in some pages, that I can modify later for all entities of their kind. For example, if I open a wine producer page, I want to see some dataview queries showing the wines of this producer that I have in stock. I’ve come this far, and by including the dataview query in a template, I can create new producers and they benefit from the query.

But imagine I now have 100 wine producers stored, and I would like to add another query in the wine producer pages, say to also show the wines I don’t have in stock. I would now need to edit each wine producer page individually, as well as the template for future ones.

What I’d like to do is maintain a wine producer “dynamic section”, that I can refer to once and for all (e.g. in the wine producer template), and then I can make changes to it and it will work for all the existing pages referring to that dynamic section template.

Note that the dataview query itself uses things like WHERE Producer = this.file.link, so things like embedding sections from another file won’t work.

Things I have tried

I thought Templater’s dynamic content feature might help, but:

  1. it’s mentioned that it will be decomissioned
  2. I couldn’t even make it work - I tried this:
<%+ tp.file.include("[[Templates/Wine Producer dynamic page]]") %>

But it does nothing (it renders the command rather than anything else)

Outside of that, I’m not sure what to try…

Has anyone implemented something like what I’m trying to achieve? Does anyone have clues? Templater docs say dataview itself should allow replacing Templater dynamic content, but I don’t know how this would work in this case…

Thanks in advance!

this is where you want to use dataviewjs and dv.view()

https://blacksmithgu.github.io/obsidian-dataview/api/code-reference/#dvviewpath-input

dataviewjs does allow you with some of the functions to use the DQL queries you’re already using, and it also allows you a slightly less convenient/slightly more powerful js approach to doing essentially the same queries. All my notes have one of these at the top:

```dataviewjs
await dv.view("scripts/views/header")
```

and the header.js that I’ve written uses dataviewjs and the note’s metadata to render a different set of dynamic views depending on what is needed.

2 Likes

The answer to all of your problems is dv.view() ! :smiley: See Codeblock Reference > dv.view()

In my present journal entries they all start with:

```dataviewjs
await dv.view("js/header")
```

And then I can adapt the js/header.js file to my liking. It’s javascript, yes, but you can easily adapt into doing pure DQL queries from within that without knowing too much coding.

So one very simple such file could be:

dv.header(2, "TABLE example")

let result = await dv.query(`
  TABLE file.size, file.folder, length(file.lists) as "List element count"
  LIMIT 10 
`) 

if (result.successful) {
  dv.table(result.value.headers, result.value.values)
} else 
  dv.paragraph("~~~~\n" + result.error + "\n~~~~")


dv.header(2, "LIST example")

result = await dv.query("LIST LIMIT 10")

if (result.successful) {
  dv.list(result.value.values)
} else 
  dv.paragraph("~~~~\n" + result.error + "\n~~~~")

dv.header(2, "TASK example")

result = await dv.query(`
  TASK 
  WHERE !completed
  LIMIT 10
`)

if (result.successful) {
  dv.taskList(result.value.values, false)
} else 
  dv.paragraph("~~~~\n" + result.error + "\n~~~~")

Put the code above in the file js/header.js (which possibly needs to be done outside of Obsidian if you’ve not installed some plugins to edit .js files within your vault, and you’ll get the output of a table with the 10 first files with some random information, the list of the 10 first files, and the 10 first tasks in your vault.

Hopefully, this can be used as boilerplate to whatever query you’d like to present in your vault. The dv.header() is optional, and you can add other elements like shown on the web page in the first link.

3 Likes

Thank you @holroy and @cheezopath , this is golden!

Seems powerful and it ought to do exactly what I need!

Strangely, from the docs I thought I could just use successions of dv.header and dv.execute to place my tables under their corresponding headings, but doing this, even with await, gets only the first table to be inserted, and the headers added after the table even though in the code I put them in order. However using holroy’s approach, and running dv.query and then outputting the result with dv.table, things seem to work!

I’ll keep cracking! Thanks again for unblocking me.

1 Like

yeah that’s one of the reasons I usually end up switching entirely to js rather than DQL once i enter dataviewjs, I find it’s tricky to get the queries to render in a predictable order

I’ve never had any issues using the method above, but I’ve not used the dv.execute() and dv.executejs() variants. That could be relevant to this, as maybe the others are better suited for asynchronous building of rendered view.

If either you have a simple case, which often (or even better always) fails I would be interested in that (for potential bug fixes, or just looking into the matter).

I’ve more or less written them out of my vault but if I re-encounter the issue I’ll drop you a line and maybe we can puzzle it out.

1 Like

For a slightly different approach but the same end effect, I use:

```dataviewjs
 dv.span(await dv.io.load("_vault/_insert/filename.md"))
```

This inserts a straight Markdown document with any/all Dataview evaluated. Makes the editing easy (with the inserting file in source view and the embedding file in preview), and means I can mix both markdown and Dataview into the inserted content. For example, I’ve placed multiple Dataview queries each inside a callout and all of that is the header for my daily notes.

1 Like

The downside to that variant, @ShaneNZ , is that it kind of violates the html structure by doing block elements within spans, and so on. If that is not a concern of yours, it’s doable for some stuff.

Another downside, is that you don’t get to enjoy the local properties or metadata when you load the entire document like that. The dv.view() function has full access as if you had the query written out locally.

well now hold on, would this allow me to use dv.view()-like functionality inside a dataview publish block?? (i know this is going out of scope a bit but it was a block i ran into the other day)

True, and I hadn’t thought of that. But I also haven’t struck any problems from it so for now anyway I’m just going to close my eyes and move on :grin:

As for the local properties/metadata thing, the majority of my queries operate off the tags or the linked files of the file they’re embedded in, and it works just fine?? Example:

```dataview
LIST rows.file.link
FLATTEN array(filter(file.etags, (t)=>
	econtains(t, this.file.etags[0])) ) as Tags
WHERE length(Tags) > 0

FLATTEN Tags as tag
FLATTEN join(slice(split(tag, "/"), 1), "/") as TopicAndSubtopic
WHERE TopicAndSubtopic
GROUP BY TopicAndSubtopic
```

Key point being where it uses this.file.etags[0] to grab the first tag from the metadata of the file the query is inserted into, to then find all the notes with the same tag (and split by subtags for an index ie the second bit, which isn’t relevant to the point).

Now that I think about it, I haven’t tried accessing any properties with this, but if the metadata is available then properties should be as well? Or more likely, what am I misunderstanding here about how this works?

(You also might recognize the above, I think you were in the thread that I took it from :grin:)


EDIT (because I can’t post three consecutive replies, learnt something new about the forum today):

Had a bit of a vague memory and went back and checked the Dataview docs. Rather than dv.span() I could wrap it in dv.paragraph(). It would then be inside a block element (that apparently would add some extra padding, but which could then be stripped via some CSS).

Will try that once I’m back at my laptop, much much easier to do it there than on my phone.

I’m not sure what a Dataview publish block is, or in general what you’re asking :joy: So, maybe? What I’m doing does work (see further my next post here)

Just tested trying to access properties, and it does work @holroy (at least I think it does…)

This is my test query in an MD file (just this, no frontmatter):

```dataview
TABLE WITHOUT ID testprop, file.link, this.file.name, file.tags
WHERE file.name = this.file.name
```

In another MD file, I’ve used my usual method (above) to insert that file, and I’ve also put the query itself directly into the text of the file to give a comparison, this is what I get:

(Sorry for the awkward screenshot, doing this on my phone. It’s awkward.)

Both see the testprop property and the other metadata from the file. Updating the property in that file also updates on both the local and embedded query - it’s definitely getting it from there. So, I think it is handing over the local file context in the same way dv.view() does?

I’ll have a poke around with what you did and if it helps me with my thing I’ll explain more fully for the sake of posterity

1 Like