【求助】Meta Bind 插件用法

meta-bind-js-view代码块创建的按钮可以变成行内使用吗?

花了几个小时,终于写出了代码,让在正文中也可以手动填写/直接选库中特定属性的所有值,但是生成的按钮是占领全行的,希望能添个id属性之类的,用类似 BUTTON[button-id] 语法在行内使用

```meta-bind-js-view
 ---
// 只需要改这里:你要提取哪个字段?
const FIELD_NAME = "科属"; // 各笔记的属性名

// ==============================================
// 1. 遍历全库,提取所有该字段的值(自动去重)
const files = app.vault.getMarkdownFiles();
const valueSet = new Set();

files.forEach(file => {
  const value = app.metadataCache.getFileCache(file)?.frontmatter?.[FIELD_NAME];
  if (value) valueSet.add(value);
});

// 2. 转成 option(xxx) 格式
const options = Array.from(valueSet)
  .filter(Boolean)
  .map(item => `option(${item})`)
  .join(", ");

// 3. 加上 allowOther
const finalOptions = `${options}, allowOther`;

// 4. 输出 Meta Bind 选择器
const str = `\`INPUT[suggester(${finalOptions}):${FIELD_NAME}]\``;
return engine.markdown.create(str);
```

scope可以嵌套2次吗?

2026-05-10_044924
最好让早餐名称和早餐价格还是在一个单元格内

```js-engine
const mb = engine.getPlugin('obsidian-meta-bind-plugin').api;

const tableOptions = {
	bindTarget: mb.createBindTarget('frontmatter', context.file.path, ['食物记录']),
	tableHead: ['消费日期', '记录日期', '早', '早餐价格', '中', '晚'],
	columns: [
		'INPUT[datePicker:scope^记录日期]',
		'INPUT[datePicker:scope^消费日期]',
		'INPUT[suggester(option(包子), option(粉), option(不吃), allowOther):scope^早餐]',
		`INPUT[number:scope^早餐价格]`,
		'INPUT[text:scope^中餐]',
		'INPUT[text:scope^晚餐]',
	],
};

const mountable = mb.createTableMountable(context.file.path, tableOptions);

mb.wrapInMDRC(mountable, container, component);
```

没用过这些, 这也太复杂了… 只尝试讨论第一个问题吧

首先, 最容易想的肯定是把楼主的 meta-bind-js-view 代码改造为只定义 suggester, 之后另行引用

经过一番折腾, 目前我觉得这插件只有 button 可以通过 id 引用 (即先声明按钮再渲染按钮, 两部分在笔记里分离), 其他的诸如 suggester 之类都不行

然后下一个方案是, 放弃 js-engine, 只借助 inline dataviewjs 去做出 suggester, 经实验这具备基本的可行性, 例如以下代码应该是能用的

(其中 $= 是 inline dvjs 的标识符)
`$= String.fromCharCode(96)+"INPUT[number:distance]"+String.fromCharCode(96)` 

所以打算放弃 js-engine 的代码块, 把楼主代码改造为用行内 dataviewjs 实现, 容易理解的版本是

  const FIELD_NAME = "科属";  
  const files = app.vault.getMarkdownFiles();  
  const valueSet = new Set();  
  
  files.forEach(file => {  
    const value = app.metadataCache.getFileCache(file)?.frontmatter?.[FIELD_NAME];  
    if (value) valueSet.add(value);  
  });  
  
  const options = Array.from(valueSet).filter(Boolean).map(item => `option(${item})`).join(", ");  
  const finalOptions = options ? `${options}, allowOther` : "allowOther";  
    
  // 使用 String.fromCharCode(96) 生成反引号,防止 Markdown 解析冲突  
  return String.fromCharCode(96) + `INPUT[suggester(${finalOptions}):${FIELD_NAME}]` + String.fromCharCode(96);  

但上面代码还是不能直接用, 得改成单行 $= xxx 形态且转义掉所有的碍事字符, 改完是:

`$= const FIELD_NAME = "科属"; const valueSet = new Set(); app.vault.getMarkdownFiles().forEach(file => { const v = app.metadataCache.getFileCache(file)?.frontmatter?.[FIELD_NAME]; if (v) valueSet.add(v); }); const options = Array.from(valueSet).filter(Boolean).map(item => "option(" + item + ")").join(", "); const finalOptions = options ? options + ", allowOther" : "allowOther"; const tick = String.fromCharCode(96); tick + "INPUT[suggester(" + finalOptions + "):" + FIELD_NAME + "]" + tick`

最后这段在我这是能用的, 楼主可以试试

1 个赞

谢谢!成功啦!
可以正式开始用Meta Bind参与我的笔记


之前为了方便base管理笔记,得让笔记的内容更多的记录在属性区 → 需要添加的属性名很多 → 如果不模板一次性添加属性名,就可能导致不同属性管理同一内容;事先添加了,属性一大堆列出来,有点眼花;直接通过base添加,得由卡片切至表格再切回来

现在准备使用Meta Bind+Callout折叠+Virtual Content,预计会方便多了

1 个赞

再次求助变为1行,找AI改不成功 :sob:
代码用了 await,说inline dvjs 不支持
这个是选填手动添加的列表子项值:

需要放在**根目录**的「test.md」文件中
```meta-bind-js-view
 ---
// ===================== 配置区 =====================
const TARGET_CATEGORY = "植物名";  // 要提取 test.md 里的哪个列表分类
const OUTPUT_FIELD = "易混淆植物";   // 要保存到哪个字段
const file = app.vault.getAbstractFileByPath('test.md'); // test.md文件路径
// ==================================================

const content = await app.vault.cachedRead(file);
const lines = content.split("\n").filter(line => line.trim() !== '');

// 解析缩进级别 + 清洗文字
const parsedLines = lines.map(line => {
  const indent = line.match(/^(\s*)/)[0].length;
  const text = line.trim()
    .replace(/^- /, '')
    .replace(/[::\s]*$/, '')  //去掉冒号、空格
    .trim(); 
  return { indent, text };
});

const values = [];
let targetIndent = -1;
let found = false;
let childIndent = null; // 记录直接子项的真实缩进

for (let i = 0; i < parsedLines.length; i++) {
  const pl = parsedLines[i];

  // 1. 找到目标分类
  if (!found && pl.text === TARGET_CATEGORY) {
    found = true;
    targetIndent = pl.indent;
    continue;
  }

  // 2. 收集子项
  if (found) {    
    if (pl.indent <= targetIndent) break;                   // 缩进回到 <= 目标 → 停止
    if (childIndent === null) { childIndent = pl.indent;}   // 第一个比目标大的缩进,就是子项层级
    if (pl.indent === childIndent) { values.push(pl.text);} // 只收集和 childIndent 相同缩进的项,孙子项(更深层级)不收集
  }
}

// 生成选择框
const options = values.map(v => `option(${v})`).join(", ");
const finalStr = `${OUTPUT_FIELD}:\`INPUT[suggester(${options}, allowOther):${OUTPUT_FIELD}]\``; //去前面属性名提示:const finalStr = `\`INPUT[suggester(${options}, allowOther):${OUTPUT_FIELD}]\``;

return engine.markdown.create(finalStr);
```

%%
- 备选值
	- 植物名:
		- [[香樟]]
		- 栾树
		- [[桂花]]
		- 银杏
	- 科属
		- 木樨科
		- 马鞭草科
%%

目前在用的代码是根据这个改来的:

试了在 inline dataviewjs 里使用 await , 能想到的办法全都失败了, 感觉搞不定…