[BUG]? Using RequestInput overrides params.Variables changes in QuickAdd Macro Script
I am creating some notes to store locations and filling the values in dynamically if they're left blank on an existing location name. It seems these are being blanked by QuickAdd's frontmatter processing and reset.
To Reproduce
This script uses the directory Personal/Locations but should be easily modifiable
Setup: Create an existing note manually (title does not matter)
---
location: Home
address: 123 Main St, Toronto, Ontario
latitude: 43.7182115
longitude: -79.460629
creation date: 2025-11-17T21:34:11.022Z
current: true
---
QuickAdd Macro JS Script
module.exports = {
entry: start,
settings: {
name: "Add Location Script",
author: "Taylor Dickson"
}
};
async function start(params, settings) {
/** @type {import("./Quickadd").quickAddApi} */
const _quickAddApi = params.quickAddApi;
/** @type {import("./obsidian").App} */
const app = params.app;
const variables = params.variables
const dataView = app.plugins.plugins.dataview?.api
const entryDate = moment();
const entry_date_iso = entryDate.toISOString(); // For frontmatter
if (!dataView) {
new Notice("Required plugin Dataview is not available.", 5000);
return;
}
try {
/*
File Structure:
FileName: UUID
---
location: "Location Name"
latitude: 00.0000
longitude: 00.0000
creation date: 2024-01-01T00:00:00.000Z
address: "123 Main St..."
current: true
---
*/
// Get Location Names from DataView
const dvResults = await dataView.pages('"Personal/Locations"')
.where(p => p.current === true)
.groupBy(p => p.location)
.array();
let locationOptions = dvResults.map(g => g.key).sort();
const formInputs = [
{
id: "location",
label: "Select Location",
type: "suggester",
options: locationOptions,
placeholder: "Start typing location name..."
},
{
id: "address",
label: "Address",
type: "textarea",
placeholder: "123 Main St., Toronto, Ontario, Canada"
},
{
id: "latitude",
label: "Latitude",
type: "text"
},
{
id: "longitude",
label: "Longitude",
type: "text"
}
];
const numericFields = ["latitude", "longitude"];
const values = await _quickAddApi.requestInputs(formInputs);
// Validate Input - fill in missing values BEFORE merging into variables
if (locationOptions.includes(values.location)) {
// Existing Location - pull lat/long from dataview
console.log("Updating Existing Location", values.location);
const dvLocationGroup = dvResults.find(g => g.key === values.location);
// Fill in any missing values from existing record in the VALUES object
const inputIds = formInputs.map(i => i.id);
for (let input of inputIds) {
if (input !== "location" && (values[input] === "" || values[input] === undefined)){
values[input] = dvLocationGroup.rows.values[0][input] || "";
}
}
}
// Now merge the filled values into variables
Object.assign(variables, values);
// Handle location updates after merge
if (locationOptions.includes(variables.location)) {
// Update Records to set current = false
const dvLocationGroup = dvResults.find(g => g.key === variables.location);
for (let page of dvLocationGroup.rows.values){
console.log("Setting current = false for:", page.file.path);
app.fileManager.processFrontMatter(
app.vault.getFileByPath(page.file.path),
(frontmatter) => {
frontmatter["current"] = false;
});
}
} else {
// New Location - convert numeric fields
console.log("Adding New Location", variables.location);
for (let field of numericFields){
if (variables[field] === "") {
variables[field] = 0.0;
} else {
// Ensure numeric fields are numbers
variables[field] = parseFloat(variables[field]);
}
}
}
// Join Address Lines if needed - frontmatter can't handle multiline strings
if (variables.address && variables.address.includes('\n')) {
variables.address = variables.address.replace(/\n/g, ', ');
}
// Set variables for use in QuickAdd templates
variables.fileName = crypto.randomUUID();
variables['creation_date'] = entry_date_iso;
variables.current = true; // Set current to true for new record
return;
} catch (error) {
// Check Abort status as abort() triggers an exception which we don't want to catch
if (error.name === 'MacroAbortError') {
params.abort(error.message);
return;
}
console.error("Add Location Script:", error);
new Notice(`Error: ${error.message}`, 5000);
}
}
Run QuickAdd Macro to Add Location. Choose existing location name from dropdown (note created manually above). Do not enter more details into other fields. These will be pulled from the existing entry.
Note: It may be helpful to include this in the last line of the script:
console.log("FINAL params.variables:", JSON.stringify(params.variables, null, 2));
Expected Behavior
- Old Entry updated with
current = false - New Note Created in
Personal/Locationswith frontmatter valuesaddress, long, and latfilled from previous note (failing)
Actual
Console Output from Debug:
FINAL params.variables: {
"location": "Home",
"address": "123 Main St, Toronto, Ontario",
"latitude": 43.7182115,
"longitude": -79.460629,
"fileName": "e311f2ec-daa2-4898-a658-1de8966ce7ed",
"creation_date": "2025-11-18T21:40:03.953Z",
"current": true
}
New note is being created but with empty frontmatter fields:
---
location: Home
address:
latitude:
longitude:
creation date: 2025-11-18T21:39:04.136Z
current: true
---
Obsidian Debug Info (required for bug reports)
SYSTEM INFO:
Obsidian version: v1.10.3
Installer version: v1.10.3
Operating system: Windows 11 Enterprise 10.0.26100
Login status: not logged in
Language: en
Insider build toggle: off
Live preview: on
Base theme: adapt to system
Community theme: Underwater v1.6.53
Snippets enabled: 3
Restricted mode: off
Plugins installed: 54
Plugins enabled: 54
1: Lazy Plugin Loader v1.0.21
2: Remotely Save v0.5.25
3: Actions URI v1.8.3
4: Code Editor Shortcuts v1.14.0
5: Footnote Shortcut v0.1.3
6: Hotkey Helper v0.3.20
7: Paste URL into selection v1.11.4
8: Tab Switcher v1.4.0
9: Tag Wrangler v0.6.4
10: About Blank v1.2.0
11: Better Command Palette v0.17.1
12: Calendar v1.5.10
13: Commander v0.5.4
14: JS Engine v0.3.3
15: Keyshots v2.8.0
16: Attachment Management v0.10.0
17: Automatic Table Of Contents v1.7.3
18: Better Word Count v0.10.1
19: Copy as HTML v1.1.3
20: Dataview v0.5.68
21: Editor Syntax Highlight v0.1.3
22: Excalidraw v2.17.1
23: Excel to Markdown Table v0.4.0
24: floating toc v2.7.0
25: Meta Bind v1.4.6
26: Line Arrange v1.5.0
27: Linter v1.30.0
28: List Callouts v1.2.9
29: Local REST API v3.2.0
30: Markdown Formatting Assistant v0.4.1
31: Mermaid Tools v1.3.0
32: Modal forms v1.61.0
33: Natural Language Dates v0.6.2
34: Ninja Cursor v0.0.13
35: Note Refactor v1.8.2
36: Outliner v4.9.0
37: Pandoc Plugin v0.4.1
38: Periodic Notes v0.0.17
39: Pomodoro Timer v1.2.3
40: Advanced Tables v0.22.1
41: Better Export PDF v1.11.0
42: Differential ZIP Backup v0.0.20
43: MCP Tools v0.2.27
44: Solve v1.1.0
45: PDF++ v0.40.31
46: Smart Connections v3.0.80
47: Style Settings v1.0.9
48: Text Finder v0.2.2
49: Typewriter Scroll v0.2.2
50: Iconic v1.1.4
51: Settings Management v1.0.6
52: Templater v2.16.2
53: QuickAdd v2.8.0
54: Advanced Canvas v5.6.4
QuickAdd Macro Package:
{
"schemaVersion": 1,
"quickAddVersion": "2.8.0",
"createdAt": "2025-11-18T21:44:27.582Z",
"rootChoiceIds": [
"c3c2ed13-5eae-47c1-8306-5f7db6b7a65f"
],
"choices": [
{
"choice": {
"id": "c3c2ed13-5eae-47c1-8306-5f7db6b7a65f",
"name": "Add Location",
"type": "Macro",
"command": true,
"runOnStartup": false,
"macro": {
"name": "Add Location",
"id": "994aaf61-1296-4721-abea-111bb916e0d7",
"commands": [
{
"name": "AddLocation",
"type": "UserScript",
"id": "0fc0331d-206d-4fe6-bc58-fdbdffca3373",
"path": "Scripts/AddLocation.js",
"settings": {}
},
{
"name": "New Location Capture",
"type": "NestedChoice",
"id": "c5f109ad-60d0-47c0-bacd-bf8ba9a9377e",
"choice": {
"id": "936230d6-e2c9-4037-9d64-23b132030d14",
"name": "New Location Capture",
"type": "Capture",
"command": false,
"onePageInput": "never",
"appendLink": false,
"captureTo": "Personal/Locations/{{VALUE:fileName}}",
"captureToActiveFile": false,
"activeFileWritePosition": "cursor",
"createFileIfItDoesntExist": {
"enabled": true,
"createWithTemplate": false,
"template": ""
},
"format": {
"enabled": true,
"format": "---\nlocation: {{VALUE:location}}\naddress: {{VALUE:address}}\nlatitude: {{VALUE:latitude}}\nlongitude: {{VALUE:longitude}}\ncreation date: {{VALUE:creation_date}}\ncurrent: {{VALUE:current}}\n---\n"
},
"insertAfter": {
"enabled": false,
"after": "",
"insertAtEnd": false,
"considerSubsections": false,
"createIfNotFound": false,
"createIfNotFoundLocation": "top"
},
"newLineCapture": {
"enabled": false,
"direction": "below"
},
"prepend": false,
"task": false,
"openFile": true,
"fileOpening": {
"location": "tab",
"direction": "vertical",
"mode": "source",
"focus": true
}
}
}
]
}
},
"pathHint": [
"Add Location"
],
"parentChoiceId": null
}
],
"assets": [
{
"kind": "user-script",
"originalPath": "Scripts/AddLocation.js",
"contentEncoding": "base64",
"content": "bW9kdWxlLmV4cG9ydHMgPSB7DQogICAgZW50cnk6IHN0YXJ0LA0KICAgIHNldHRpbmdzOiB7DQogICAgICAgIG5hbWU6ICJBZGQgTG9jYXRpb24gU2NyaXB0IiwNCiAgICAgICAgYXV0aG9yOiAiVGF5bG9yIERpY2tzb24iDQogICAgfQ0KfTsNCg0KYXN5bmMgZnVuY3Rpb24gc3RhcnQocGFyYW1zLCBzZXR0aW5ncykgew0KICAgIC8qKiBAdHlwZSB7aW1wb3J0KCIuL1F1aWNrYWRkIikucXVpY2tBZGRBcGl9ICovDQogICAgY29uc3QgX3F1aWNrQWRkQXBpID0gcGFyYW1zLnF1aWNrQWRkQXBpOw0KICAgIC8qKiBAdHlwZSB7aW1wb3J0KCIuL29ic2lkaWFuIikuQXBwfSAqLw0KICAgIGNvbnN0IGFwcCA9IHBhcmFtcy5hcHA7DQogICAgY29uc3QgdmFyaWFibGVzID0gcGFyYW1zLnZhcmlhYmxlcw0KDQogICAgY29uc3QgZGF0YVZpZXcgPSBhcHAucGx1Z2lucy5wbHVnaW5zLmRhdGF2aWV3Py5hcGkNCg0KICAgIGNvbnN0IGVudHJ5RGF0ZSA9IG1vbWVudCgpOw0KICAgIGNvbnN0IGVudHJ5X2RhdGVfaXNvID0gZW50cnlEYXRlLnRvSVNPU3RyaW5nKCk7IC8vIEZvciBmcm9udG1hdHRlcg0KDQogICAgaWYgKCFkYXRhVmlldykgew0KICAgICAgICBuZXcgTm90aWNlKCJSZXF1aXJlZCBwbHVnaW4gRGF0YXZpZXcgaXMgbm90IGF2YWlsYWJsZS4iLCA1MDAwKTsNCiAgICAgICAgcmV0dXJuOw0KICAgIH0NCg0KICAgIHRyeSB7DQogICAgICAgIC8qDQogICAgICAgIEZpbGUgU3RydWN0dXJlOg0KICAgICAgICBGaWxlTmFtZTogVVVJRA0KICAgICAgICAtLS0NCiAgICAgICAgbG9jYXRpb246ICJMb2NhdGlvbiBOYW1lIg0KICAgICAgICBsYXRpdHVkZTogMDAuMDAwMA0KICAgICAgICBsb25naXR1ZGU6IDAwLjAwMDANCiAgICAgICAgY3JlYXRpb24gZGF0ZTogMjAyNC0wMS0wMVQwMDowMDowMC4wMDBaDQogICAgICAgIGFkZHJlc3M6ICIxMjMgTWFpbiBTdC4uLiINCiAgICAgICAgY3VycmVudDogdHJ1ZQ0KICAgICAgICAtLS0NCiAgICAgICAgKi8NCg0KICAgICAgICAvLyBHZXQgTG9jYXRpb24gTmFtZXMgZnJvbSBEYXRhVmlldw0KICAgICAgICBjb25zdCBkdlJlc3VsdHMgPSBhd2FpdCBkYXRhVmlldy5wYWdlcygnIlBlcnNvbmFsL0xvY2F0aW9ucyInKQ0KICAgICAgICAgICAgLndoZXJlKHAgPT4gcC5jdXJyZW50ID09PSB0cnVlKQ0KICAgICAgICAgICAgLmdyb3VwQnkocCA9PiBwLmxvY2F0aW9uKQ0KICAgICAgICAgICAgLmFycmF5KCk7DQogICAgICAgIGxldCBsb2NhdGlvbk9wdGlvbnMgPSBkdlJlc3VsdHMubWFwKGcgPT4gZy5rZXkpLnNvcnQoKTsNCg0KICAgICAgICBjb25zdCBmb3JtSW5wdXRzID0gWw0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgIGlkOiAibG9jYXRpb24iLA0KICAgICAgICAgICAgICAgIGxhYmVsOiAiU2VsZWN0IExvY2F0aW9uIiwNCiAgICAgICAgICAgICAgICB0eXBlOiAic3VnZ2VzdGVyIiwNCiAgICAgICAgICAgICAgICBvcHRpb25zOiBsb2NhdGlvbk9wdGlvbnMsDQogICAgICAgICAgICAgICAgcGxhY2Vob2xkZXI6ICJTdGFydCB0eXBpbmcgbG9jYXRpb24gbmFtZS4uLiINCiAgICAgICAgICAgIH0sDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgaWQ6ICJhZGRyZXNzIiwNCiAgICAgICAgICAgICAgICBsYWJlbDogIkFkZHJlc3MiLA0KICAgICAgICAgICAgICAgIHR5cGU6ICJ0ZXh0YXJlYSIsDQogICAgICAgICAgICAgICAgcGxhY2Vob2xkZXI6ICIxMjMgTWFpbiBTdC4sIFRvcm9udG8sIE9udGFyaW8sIENhbmFkYSINCiAgICAgICAgICAgIH0sDQogICAgICAgICAgICB7DQogICAgICAgICAgICAgICAgaWQ6ICJsYXRpdHVkZSIsDQogICAgICAgICAgICAgICAgbGFiZWw6ICJMYXRpdHVkZSIsDQogICAgICAgICAgICAgICAgdHlwZTogInRleHQiDQogICAgICAgICAgICB9LA0KICAgICAgICAgICAgew0KICAgICAgICAgICAgICAgIGlkOiAibG9uZ2l0dWRlIiwNCiAgICAgICAgICAgICAgICBsYWJlbDogIkxvbmdpdHVkZSIsDQogICAgICAgICAgICAgICAgdHlwZTogInRleHQiDQogICAgICAgICAgICB9DQogICAgICAgIF07DQogICAgICAgIGNvbnN0IG51bWVyaWNGaWVsZHMgPSBbImxhdGl0dWRlIiwgImxvbmdpdHVkZSJdOw0KICAgICAgICBjb25zdCB2YWx1ZXMgPSBhd2FpdCBfcXVpY2tBZGRBcGkucmVxdWVzdElucHV0cyhmb3JtSW5wdXRzKTsNCg0KICAgICAgICAvLyBWYWxpZGF0ZSBJbnB1dCAtIGZpbGwgaW4gbWlzc2luZyB2YWx1ZXMgQkVGT1JFIG1lcmdpbmcgaW50byB2YXJpYWJsZXMNCiAgICAgICAgaWYgKGxvY2F0aW9uT3B0aW9ucy5pbmNsdWRlcyh2YWx1ZXMubG9jYXRpb24pKSB7DQogICAgICAgICAgICAvLyBFeGlzdGluZyBMb2NhdGlvbiAtIHB1bGwgbGF0L2xvbmcgZnJvbSBkYXRhdmlldw0KICAgICAgICAgICAgY29uc29sZS5sb2coIlVwZGF0aW5nIEV4aXN0aW5nIExvY2F0aW9uIiwgdmFsdWVzLmxvY2F0aW9uKTsNCiAgICAgICAgICAgIGNvbnN0IGR2TG9jYXRpb25Hcm91cCA9IGR2UmVzdWx0cy5maW5kKGcgPT4gZy5rZXkgPT09IHZhbHVlcy5sb2NhdGlvbik7DQoNCiAgICAgICAgICAgIC8vIEZpbGwgaW4gYW55IG1pc3NpbmcgdmFsdWVzIGZyb20gZXhpc3RpbmcgcmVjb3JkIGluIHRoZSBWQUxVRVMgb2JqZWN0DQogICAgICAgICAgICBjb25zdCBpbnB1dElkcyA9IGZvcm1JbnB1dHMubWFwKGkgPT4gaS5pZCk7DQogICAgICAgICAgICBmb3IgKGxldCBpbnB1dCBvZiBpbnB1dElkcykgew0KICAgICAgICAgICAgICAgIGlmIChpbnB1dCAhPT0gImxvY2F0aW9uIiAmJiAodmFsdWVzW2lucHV0XSA9PT0gIiIgfHwgdmFsdWVzW2lucHV0XSA9PT0gdW5kZWZpbmVkKSl7DQogICAgICAgICAgICAgICAgICAgIHZhbHVlc1tpbnB1dF0gPSBkdkxvY2F0aW9uR3JvdXAucm93cy52YWx1ZXNbMF1baW5wdXRdIHx8ICIiOw0KICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIH0NCiAgICAgICAgfQ0KDQogICAgICAgIC8vIE5vdyBtZXJnZSB0aGUgZmlsbGVkIHZhbHVlcyBpbnRvIHZhcmlhYmxlcw0KICAgICAgICBPYmplY3QuYXNzaWduKHZhcmlhYmxlcywgdmFsdWVzKTsNCg0KICAgICAgICAvLyBIYW5kbGUgbG9jYXRpb24gdXBkYXRlcyBhZnRlciBtZXJnZQ0KICAgICAgICBpZiAobG9jYXRpb25PcHRpb25zLmluY2x1ZGVzKHZhcmlhYmxlcy5sb2NhdGlvbikpIHsNCiAgICAgICAgICAgIC8vIFVwZGF0ZSBSZWNvcmRzIHRvIHNldCBjdXJyZW50ID0gZmFsc2UNCiAgICAgICAgICAgIGNvbnN0IGR2TG9jYXRpb25Hcm91cCA9IGR2UmVzdWx0cy5maW5kKGcgPT4gZy5rZXkgPT09IHZhcmlhYmxlcy5sb2NhdGlvbik7DQogICAgICAgICAgICBmb3IgKGxldCBwYWdlIG9mIGR2TG9jYXRpb25Hcm91cC5yb3dzLnZhbHVlcyl7DQogICAgICAgICAgICAgICAgY29uc29sZS5sb2coIlNldHRpbmcgY3VycmVudCA9IGZhbHNlIGZvcjoiLCBwYWdlLmZpbGUucGF0aCk7DQogICAgICAgICAgICAgICAgYXBwLmZpbGVNYW5hZ2VyLnByb2Nlc3NGcm9udE1hdHRlcigNCiAgICAgICAgICAgICAgICAgICAgYXBwLnZhdWx0LmdldEZpbGVCeVBhdGgocGFnZS5maWxlLnBhdGgpLA0KICAgICAgICAgICAgICAgICAgICAoZnJvbnRtYXR0ZXIpID0+IHsNCiAgICAgICAgICAgICAgICAgICAgICAgIGZyb250bWF0dGVyWyJjdXJyZW50Il0gPSBmYWxzZTsNCiAgICAgICAgICAgICAgICB9KTsNCiAgICAgICAgICAgIH0NCiAgICAgICAgfSBlbHNlIHsNCiAgICAgICAgICAgIC8vIE5ldyBMb2NhdGlvbiAtIGNvbnZlcnQgbnVtZXJpYyBmaWVsZHMNCiAgICAgICAgICAgIGNvbnNvbGUubG9nKCJBZGRpbmcgTmV3IExvY2F0aW9uIiwgdmFyaWFibGVzLmxvY2F0aW9uKTsNCiAgICAgICAgICAgIGZvciAobGV0IGZpZWxkIG9mIG51bWVyaWNGaWVsZHMpew0KICAgICAgICAgICAgICAgIGlmICh2YXJpYWJsZXNbZmllbGRdID09PSAiIikgew0KICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZXNbZmllbGRdID0gMC4wOw0KICAgICAgICAgICAgICAgIH0gZWxzZSB7DQogICAgICAgICAgICAgICAgICAgIC8vIEVuc3VyZSBudW1lcmljIGZpZWxkcyBhcmUgbnVtYmVycw0KICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZXNbZmllbGRdID0gcGFyc2VGbG9hdCh2YXJpYWJsZXNbZmllbGRdKTsNCiAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9DQogICAgICAgIH0NCg0KICAgICAgICAvLyBKb2luIEFkZHJlc3MgTGluZXMgaWYgbmVlZGVkIC0gZnJvbnRtYXR0ZXIgY2FuJ3QgaGFuZGxlIG11bHRpbGluZSBzdHJpbmdzDQogICAgICAgIGlmICh2YXJpYWJsZXMuYWRkcmVzcyAmJiB2YXJpYWJsZXMuYWRkcmVzcy5pbmNsdWRlcygnXG4nKSkgew0KICAgICAgICAgICAgdmFyaWFibGVzLmFkZHJlc3MgPSB2YXJpYWJsZXMuYWRkcmVzcy5yZXBsYWNlKC9cbi9nLCAnLCAnKTsNCiAgICAgICAgfQ0KDQogICAgICAgIC8vIFNldCB2YXJpYWJsZXMgZm9yIHVzZSBpbiBRdWlja0FkZCB0ZW1wbGF0ZXMNCiAgICAgICAgdmFyaWFibGVzLmZpbGVOYW1lID0gY3J5cHRvLnJhbmRvbVVVSUQoKTsNCiAgICAgICAgdmFyaWFibGVzWydjcmVhdGlvbl9kYXRlJ10gPSBlbnRyeV9kYXRlX2lzbzsNCiAgICAgICAgdmFyaWFibGVzLmN1cnJlbnQgPSB0cnVlOyAvLyBTZXQgY3VycmVudCB0byB0cnVlIGZvciBuZXcgcmVjb3JkDQoNCiAgICAgICAgY29uc29sZS5sb2coIkZJTkFMIHBhcmFtcy52YXJpYWJsZXM6IiwgSlNPTi5zdHJpbmdpZnkocGFyYW1zLnZhcmlhYmxlcywgbnVsbCwgMikpOw0KICAgICAgICByZXR1cm47DQogICAgfSBjYXRjaCAoZXJyb3IpIHsNCiAgICAgICAgLy8gQ2hlY2sgQWJvcnQgc3RhdHVzIGFzIGFib3J0KCkgdHJpZ2dlcnMgYW4gZXhjZXB0aW9uIHdoaWNoIHdlIGRvbid0IHdhbnQgdG8gY2F0Y2gNCiAgICAgICAgaWYgKGVycm9yLm5hbWUgPT09ICdNYWNyb0Fib3J0RXJyb3InKSB7DQogICAgICAgICAgICBwYXJhbXMuYWJvcnQoZXJyb3IubWVzc2FnZSk7DQogICAgICAgICAgICByZXR1cm47DQogICAgICAgIH0NCiAgICAgICAgY29uc29sZS5lcnJvcigiQWRkIExvY2F0aW9uIFNjcmlwdDoiLCBlcnJvcik7DQogICAgICAgIG5ldyBOb3RpY2UoYEVycm9yOiAke2Vycm9yLm1lc3NhZ2V9YCwgNTAwMCk7DQogICAgfQ0KfQ=="
}
]
}
Just adding my own quick observations here, but this issue might have something to do with some missing 'await' statements in your code. A lot of obsidian methods are async, and require an await to function properly. I don't know them all personally, but I do know that processFrontMatter() is asynchronous. A lot of file related methods are also async, so maybe adding some awaits to your code can fix your problem?
Edit: you can see in the obsidian docs if a method returns a promise, which makes it async.
Thanks for the catch on the ProcessFrontMatter, I did end up adding an await there, getFileByPath is syncronous.
for (let page of dvLocationGroup.rows.values){
console.log("Setting current = false for:", page.file.path);
await app.fileManager.processFrontMatter( // New! await
app.vault.getFileByPath(page.file.path),
(frontmatter) => {
frontmatter["current"] = false;
});
}
Unfortunately I'm still seeing the same result as mentioned above. I think there's a clue in the Console:
VM483:78 Updating Existing Location Home
VM483:98 Setting current = false for: Personal/Locations/7283b245-2303-492f-9918-e3880fde10ad.md
VM483:128 FINAL params.variables: {
"location": "Home",
"address": "123 Main St, Toronto, Ontario",
"latitude": 43.7182115,
"longitude": -79.460629,
"fileName": "36a5d658-6a49-4050-a33a-d45fe54c31de",
"creation_date": "2025-11-18T21:40:03.953Z",
"current": true
}
plugin:quickadd:73 QuickAdd: (LOG) CaptureChoiceEngine: Accumulated 1 structured capture variables
plugin:quickadd:73 QuickAdd: (LOG) could not get frontmatter. Maybe there isn't any.
plugin:quickadd:73 QuickAdd: (LOG) CaptureChoiceEngine: Post-processing front matter with 1 capture variables
plugin:quickadd:73 QuickAdd: (LOG) Post-processing front matter for Personal/Locations/36a5d658-6a49-4050-a33a-d45fe54c31de.md with 1 structured variables
plugin:quickadd:73 QuickAdd: (LOG) Variable types: current:boolean
plugin:quickadd:73 QuickAdd: (LOG) Successfully post-processed front matter for Personal/Locations/36a5d658-6a49-4050-a33a-d45fe54c31de.md
plugin:quickadd:73 QuickAdd: (LOG) Post-processing front matter ... with 1 structured variables plugin:quickadd:73 QuickAdd: (LOG) Variable types: current:boolean
Despite it being logged correctly just before the return it seems that it's still not getting the actual details in the final md file:
---
location: Home
address:
latitude:
longitude:
creation date: 2025-11-19T14:31:32.102Z
current: true
---
For posterity here is the updated js:
module.exports = {
entry: start,
settings: {
name: "Add Location Script",
author: "Taylor Dickson"
}
};
async function start(params, settings) {
/** @type {import("./Quickadd").quickAddApi} */
const _quickAddApi = params.quickAddApi;
/** @type {import("./obsidian").App} */
const app = params.app;
const variables = params.variables
const dataView = app.plugins.plugins.dataview?.api
const entryDate = moment();
const entry_date_iso = entryDate.toISOString(); // For frontmatter
if (!dataView) {
new Notice("Required plugin Dataview is not available.", 5000);
return;
}
try {
/*
File Structure:
FileName: UUID
---
location: "Location Name"
latitude: 00.0000
longitude: 00.0000
creation date: 2024-01-01T00:00:00.000Z
address: "123 Main St..."
current: true
---
*/
// Get Location Names from DataView
const dvResults = await dataView.pages('"Personal/Locations"')
.where(p => p.current === true)
.groupBy(p => p.location)
.array();
let locationOptions = dvResults.map(g => g.key).sort();
const formInputs = [
{
id: "location",
label: "Select Location",
type: "suggester",
options: locationOptions,
placeholder: "Start typing location name..."
},
{
id: "address",
label: "Address",
type: "textarea",
placeholder: "123 Main St., Toronto, Ontario, Canada"
},
{
id: "latitude",
label: "Latitude",
type: "text"
},
{
id: "longitude",
label: "Longitude",
type: "text"
}
];
const numericFields = ["latitude", "longitude"];
const values = await _quickAddApi.requestInputs(formInputs);
// Validate Input - fill in missing values BEFORE merging into variables
if (locationOptions.includes(values.location)) {
// Existing Location - pull lat/long from dataview
console.log("Updating Existing Location", values.location);
const dvLocationGroup = dvResults.find(g => g.key === values.location);
// Fill in any missing values from existing record in the VALUES object
const inputIds = formInputs.map(i => i.id);
for (let input of inputIds) {
if (input !== "location" && (values[input] === "" || values[input] === undefined)){
values[input] = dvLocationGroup.rows.values[0][input] || "";
}
}
}
// Now merge the filled values into variables
Object.assign(variables, values);
// Handle location updates after merge
if (locationOptions.includes(variables.location)) {
// Update Records to set current = false
const dvLocationGroup = dvResults.find(g => g.key === variables.location);
for (let page of dvLocationGroup.rows.values){
console.log("Setting current = false for:", page.file.path);
await app.fileManager.processFrontMatter(
app.vault.getFileByPath(page.file.path),
(frontmatter) => {
frontmatter["current"] = false;
});
}
} else {
// New Location - convert numeric fields
console.log("Adding New Location", variables.location);
for (let field of numericFields){
if (variables[field] === "") {
variables[field] = 0.0;
} else {
// Ensure numeric fields are numbers
variables[field] = parseFloat(variables[field]);
}
}
}
// Join Address Lines if needed - frontmatter can't handle multiline strings
if (variables.address && variables.address.includes('\n')) {
variables.address = variables.address.replace(/\n/g, ', ');
}
// Set variables for use in QuickAdd templates
variables.fileName = crypto.randomUUID();
variables['creation_date'] = entry_date_iso;
variables.current = true; // Set current to true for new record
console.log("FINAL params.variables:", JSON.stringify(params.variables, null, 2));
return;
} catch (error) {
// Check Abort status as abort() triggers an exception which we don't want to catch
if (error.name === 'MacroAbortError') {
params.abort(error.message);
return;
}
console.error("Add Location Script:", error);
new Notice(`Error: ${error.message}`, 5000);
}
}
I have retested this on my system, and it seems to work perfectly fine for some reason? I personally do run a lot less plugins atm, so that might be doing something maybe.
SYSTEM INFO: Obsidian version: v1.10.3 Installer version: v1.9.10 Operating system: Windows 11 Home 10.0.26200 Login status: not logged in Language: en Insider build toggle: off Live preview: on Base theme: dark Community theme: Minimal v8.0.4 Snippets enabled: 5 Restricted mode: off Plugins installed: 13 Plugins enabled: 13 1: Minimal Theme Settings v8.1.1 2: Templater v2.16.2 3: Style Settings v1.0.9 4: Notebook Navigator v1.8.4 5: Meta Bind v1.4.6 6: Set View Mode per Note v1.0.3 7: Editing Toolbar v3.1.18 8: Commander v0.5.4 9: QuickAdd v2.8.0 10: Linter v1.30.0 11: CustomJS v1.0.21 12: Dataview v0.5.68 13: Iconize v2.14.7
https://github.com/user-attachments/assets/a2fc3e3d-a0e1-4ed1-b1d1-7e918ad42085
Interesting, I did watch your video and it should work on first time entries, but it should be using dataview to pull that information from the previous 'Home' entry in your location. So:
- Create First File
- Run Add Location Script
- Select previously added location from 1
- Do not fill out additional information. The script should fill this information from note created in 1
I think I might know what's going on. When you print params.variables by using console.log() in another script right after the AddLocation.js within the same macro, it shows all empty variables except for the location and creation_date, as shown before. As luck would have it, I actually just today looked into the API on this github to look into the requestInputs() method as I was having my own problems with this method. I learned that requestInputs() actually already applies the values it gathered to params.variables itself. I think due to the order in which the methods are executed, requestInputs() would sort of overwrite your inputs with the ones it found in the modal form. That's why is does load 'Home', but nothing else.
requestInputs: async (
inputs: Array<{
id: string;
label?: string;
type: "text" | "textarea" | "dropdown" | "date" | "field-suggest" | "suggester";
placeholder?: string;
defaultValue?: string;
options?: string[];
dateFormat?: string;
description?: string;
suggesterConfig?: {
allowCustomInput?: boolean;
caseSensitive?: boolean;
multiSelect?: boolean;
};
}>,
): Promise<Record<string, string>> => {
// If all inputs already have values, return them immediately
const existing: Record<string, string> = {};
const missing: FieldRequirement[] = [];
for (const spec of inputs) {
const val = choiceExecutor.variables.get(spec.id) as
| string
| undefined;
// Empty string is considered intentional and should not be re-asked
if (val !== undefined && val !== null) {
existing[spec.id] = String(val);
continue;
}
missing.push({
id: spec.id,
label: spec.label ?? spec.id,
type: spec.type,
placeholder: spec.placeholder,
defaultValue: spec.defaultValue,
options: spec.options,
dateFormat: spec.dateFormat,
description: spec.description,
suggesterConfig: spec.suggesterConfig,
source: "script",
});
}
let collected: Record<string, string> = {};
if (missing.length > 0) {
const modal = new OnePageInputModal(
app,
missing,
choiceExecutor.variables,
);
try {
collected = await modal.waitForClose;
} catch (error) {
throwIfPromptCancelled(error);
throw error;
}
}
const result = { ...existing, ...collected };
Object.entries(result).forEach(([k, v]) =>
choiceExecutor.variables.set(k, v),
);
return result;
},
There are multiple ways to solve this, but one of the easiest might be to add a quickAddApi.yesNoPrompt to determine if you want to use an existing location or not, and then adjust the available options for requestInputs(). So you will first be asked if you want to update an existing location, then you have a prompt that will only select the existing location (Home in this case), and then you can still fetch the address and stuff with dataview and assign it to params.variables safely, since requestInputs() should not overwrite it. At least I pretty sure this would work :)
I hope this made sense, English isn't my first languague
EDIT: I just realized the solution I proposed doesn't make any sense, since you of course want to have the option to actually edit stuff like the address and such.
I hope the information was useful anyway, maybe you can find a solution with it! If not, I might be able to look further into it tomorrow, I already have some ideas :)
Thanks @JurreOsinga, I've been able to partially solve this. I'm not sure if I would label this a bug - I think it might be up to @chhoumann to decide. Maybe a potential future enhancement.
What's happening:
- Macro Runs
- Request Input gets called:
- Writes collected values (including blanks) to
choiceExecutor.variables - Returns those to
values
- Writes collected values (including blanks) to
- Script writes to
params.variables - Script returns
- MacroEngine Sync pulls
choiceExecutor.variablesand overrwrites any changes with it's blanks fromRequestInputs - Capture writes the now overwritten and blank variables to RequestInputs
In essence if you use RequestInputs within a macro you cannot change any variables it sets after the call.
Workaround
As a workaround I've decided to use a _quickAddApi.suggester() first, then fill those values as the defaultValue for the RequestInputs fields allowing them to persist in choiceExecutor.variables.
Here's the js I've landed on:
module.exports = {
entry: start,
settings: {
name: "Add Location Script",
author: "Taylor Dickson"
}
};
async function start(params, settings) {
/** @type {import("./Quickadd").quickAddApi} */
const _quickAddApi = params.quickAddApi;
/** @type {import("./obsidian").App} */
const app = params.app;
const variables = params.variables
const dataView = app.plugins.plugins.dataview?.api
const entryDate = moment();
// Format with local timezone offset (e.g., 2025-11-20T10:26:40.630-05:00)
const entry_date_iso = entryDate.format(); // Uses ISO 8601 with local offset
if (!dataView) {
new Notice("Required plugin Dataview is not available.", 5000);
return;
}
try {
/*
File Structure:
FileName: UUID
---
location: "Location Name"
latitude: 00.0000
longitude: 00.0000
creation date: 2024-01-01T00:00:00.000Z
address: "123 Main St..."
current: true
---
*/
// Get Location Names from DataView
const dvResults = await dataView.pages('"Personal/Locations"')
.where(p => p.current === true)
.groupBy(p => p.location)
.array();
let locationOptions = dvResults.map(g => g.key).sort();
// First, get location selection to determine if we can pre-fill
const location = await _quickAddApi.suggester(
/* displayItems */ locationOptions,
/* actualItems */ locationOptions,
/* placeholder */ "Select Location",
/* allowCustomInput */ true
);
if (!location) {
new Notice("Location selection cancelled");
return;
}
// Set location variable immediately
variables.location = location;
// Check if this is an existing location and get pre-fill data
const isExistingLocation = locationOptions.includes(location);
let prefillData = {};
if (isExistingLocation) {
console.log("Updating Existing Location:", location);
const dvLocationGroup = dvResults.find(g => g.key === location);
const existingData = dvLocationGroup.rows.values[0];
prefillData = {
address: existingData.address || "",
// Convert numbers to strings for form inputs
latitude: String(existingData.latitude || ""),
longitude: String(existingData.longitude || "")
};
console.log("Pre-fill data from dataview:", JSON.stringify(prefillData));
}
// Build form inputs with pre-filled defaults
const formInputs = [
{
id: "address",
label: "Address",
type: "text", // Use text instead of textarea to prevent multiline input
defaultValue: prefillData.address || "",
placeholder: "123 Main St., Toronto, Ontario, Canada"
},
{
id: "latitude",
label: "Latitude",
type: "text",
defaultValue: prefillData.latitude || ""
},
{
id: "longitude",
label: "Longitude",
type: "text",
defaultValue: prefillData.longitude || ""
}
];
const numericFields = ["latitude", "longitude"];
// requestInputs() writes to choiceExecutor.variables and those values persist through sync
await _quickAddApi.requestInputs(formInputs);
// No variables can be modified here as they persist in choiceExecutor.variables
// Convert numeric fields to floats (requestInputs returns strings)
// for (let field of numericFields) {
// if (variables[field] === "" || variables[field] === undefined) {
// variables[field] = 0.0;
// } else {
// variables[field] = parseFloat(variables[field]);
// }
// }
// console.log("Converted numeric fields - latitude:", variables.latitude, "longitude:", variables.longitude);
// Update existing records to set current = false if updating
if (isExistingLocation) {
// Update Records to set current = false
const dvLocationGroup = dvResults.find(g => g.key === variables.location);
for (let page of dvLocationGroup.rows.values){
console.log("Setting current = false for:", page.file.path);
await app.fileManager.processFrontMatter(
app.vault.getFileByPath(page.file.path),
(frontmatter) => {
frontmatter["current"] = false;
});
}
} else {
console.log("Adding New Location", variables.location);
}
// Again variables cannot be modified here as they persist in choiceExecutor.variables
// Join Address Lines if needed - frontmatter can't handle multiline strings
// if (variables.address && variables.address.includes('\n')) {
// variables.address = variables.address.replace(/\n/g, ', ');
// }
// Set variables for use in QuickAdd templates - New variables that don't exist in choiceExecutor.variables can be added
variables.fileName = crypto.randomUUID();
variables['creation_date'] = entry_date_iso;
variables.current = true; // Set current to true for new record
console.log("FINAL params.variables:", JSON.stringify(params.variables, null, 2));
return;
} catch (error) {
// Check Abort status as abort() triggers an exception which we don't want to catch
if (error.name === 'MacroAbortError') {
params.abort(error.message);
return;
}
console.error("Add Location Script:", error);
new Notice(`Error: ${error.message}`, 5000);
}
}
Great solution!