Multi-select dropdown, insert multiple attributes

Obsidian is fun. Amazing to what extend the app can be customized. I hope the following script might be useful to somebody. Credit for the original idea for the multi-select dropdown goes to Christian.

Description:
A multi-select drop down box is prefilled with notes that have a given tag assigned (tag can be set in the script). User selects one or more entries in the drop-down. These items will be inserted as links into the current note. If the current Note contains a header “## Tags” the links will be inserted in the line following this header, otherwise at the current cursor position.

Use case: i.e. to quickly insert a collection of attributes into a daily note.

Screenshots:
The drop-down is automatically populated with notes that have a given tag assigned:

After checking the desired attributes and selecting “Done” the values get inserted after the “## Tag” header:

The links have the format: [[Information/9 Meta/Demo/Yoga|Yoga]]

Script code (Templater and DataView plugins are required):

<%*
// Configuration start
const question = "Your tags for today?";
const checkMark = "✅ ";
const searchTerm = "## Tags";
// Configuration end

// Check if there is a line with the searchTerm, if so put the cursor in the following line.
const cm = this.app.workspace.activeLeaf.view.editor;
const count = cm.lineCount();
let line;
for (let i = 0; i < count; i++) {
	line = cm.getLine(i + 1);
	if (line.includes(searchTerm)) {		
		cm.setCursor({line: i+2, ch: 0});		
		break;
	}		
}

// Collect all notes with the tag "Meta". Using DataViewJs for this.
const dv = this.app.plugins.plugins["dataview"].api;
let notes = dv.pages("#meta1").file.sort(n => n.name);
let suggestions = notes.map(n => n.name);
suggestions = ["Done", ...suggestions];
let values = notes.map(n => "[[" + n.folder + "/" + n.name + "|" + n.name + "]]");
values = ["Done", ...values];

// Create object with suggestions and values arrays
const config = {
  "suggestions": suggestions,
  "values": values,
  "responses": []
};

let response;
while (response !== "Done") {
    response = await tp.system.suggester(config.suggestions, config.values, true, question);
    if (response !== "Done") {	
		let rIndex = config.responses.indexOf(response);
		if (rIndex > -1) {
				config.responses.splice(rIndex, 1);					
		} else {				
			config.responses.push(response);
		}
		let vIndex = config.values.indexOf(response);
		let suggestion = config.suggestions[vIndex];
		if (suggestion.startsWith(checkMark)) {
			config.suggestions[vIndex] = suggestion.replace(checkMark,"");
		} else {
			config.suggestions[vIndex] = checkMark + suggestion;
		}
	}
}

// console.log(config);
let result = config.responses.join(", ");
return result
_%>
11 Likes

Very useful, thanks a lot! :clap:

This is exactly what I was looking for in order to quickly populate meeting notes with correctly named attendees. But when I tried adding it to my meeting template, rather than finding the correct heading, it replaced the entire document with the choices I’ve selected. Any idea why that would happen?

I imagine the described scenario would occur if

  • the script doesn’t find the configured searchTerm and
  • the whole content of the note had been selected (ctrl + a) before executing the template

Great script! Thanks!
However, it seems like the pages will only be placed under “## Tags” if the cursor is placed above “## Tags” in the document when the template is run. If I for instance has the cursor further down in the document the pages will be inserted where the cursor was.

Another question: Any idea how this approach would be used to fill out frontmatter? For instance tags where you get a similar looking dropdown list of previously used tags, and meeting attendees where you get a list of previously used attendees? But also with the possibility to add new tags/attendees not previously used?

Thank you so much! This is a game changer for my quick capture workflow.

I’ve modified the script to query for tasks rather than notes. It searches the for them using regex.

<%*
let regex = await tp.system.prompt("Enter a Regex search term for primary filter.", ".*?", true, false)

// Original Source https://forum.obsidian.md/t/multi-select-dropdown-insert-multiple-attributes/32315
// Configuration start
const question = "Your results";
const checkMark = ">>>>>>> ";
const searchTerm = "## Tasks Queried";
// Configuration end

// Check if there is a line with the searchTerm, if so put the cursor in the following line.
const cm = this.app.workspace.activeLeaf.view.editor;
const count = cm.lineCount();
let line;
for (let i = 0; i < count; i++) {
	line = cm.getLine(i + 1);
	if (line.includes(searchTerm)) {		
		cm.setCursor({line: i+2, ch: 0});		
		break;
	}		
}

// Collect all tasks with the regex primary filter. Using DataViewJs for this.
const dv = this.app.plugins.plugins["dataview"].api;
let notes = dv.pages().file.tasks.where(n => (new RegExp(regex)).test(n.text));
let suggestions = notes.map(n => n.text);
suggestions = ["Done", ...suggestions];
let values = notes.map(n => "[[🍄 " + n.text + "]]");
values = ["Done", ...values];


// Create object with suggestions and values arrays
const config = {
  "suggestions": suggestions,
  "values": values,
  "responses": []
};

let response;
while (response !== "Done") {
    response = await tp.system.suggester(config.suggestions, config.values, true, question);
    if (response !== "Done") {	
		let rIndex = config.responses.indexOf(response);
		if (rIndex > -1) {
				config.responses.splice(rIndex, 1);					
		} else {				
			config.responses.push(response);
		}
		let vIndex = config.values.indexOf(response);
		let suggestion = config.suggestions[vIndex];
		if (suggestion.startsWith(checkMark)) {
			config.suggestions[vIndex] = suggestion.replace(checkMark,"");
		} else {
			config.suggestions[vIndex] = checkMark + suggestion;
		}
	}
}

// console.log(config);
let result = config.responses.join("\n");
return result
_%>
1 Like