Meteor客户端调用服务端findOne方法返回undefined解决方案咨询
解决方案
首先先修正现有代码的两处明显错误:
- 客户端取值逻辑错误:
const value = $('#name')取到的是jQuery DOM对象,需要改为const value = $('#name').val()才能拿到输入的字符串值 - 服务端查询字段配置错误:
fieldsObj[fieldsObj] = 1是明显的笔误,需要改为fieldsObj[field] = 1,否则查询会返回全字段甚至出错
方案1:用async/await改造异步调用(最推荐,改动最小)
Meteor 1.3+ 版本的Meteor.call不传回调时会返回Promise,你可以直接把事件处理函数改为async函数,用await拿返回值,不需要嵌套回调,代码结构和你原来的写法几乎一致:
Template.createDoc.events({ // 给事件函数加async标识 'async click button[type=submit]'(e, template) { e.preventDefault(); // 记得阻止表单默认提交行为 const value = $('#name').val(); const fieldName = 'name'; // 用await拿方法返回值,后续代码不需要嵌套在回调里 const exists = await Meteor.call('checkIfFieldExistsOnDoc', fieldName, value); if (exists) { if (confirm(`Doc with ${value} as its ${fieldName} already exists. Are you sure you want to continue creating Doc?`)) { // 原有创建文档的逻辑直接放在这里即可,不需要嵌套 // db.Docs.insert.... } } } });
这个方案完全不需要改服务端代码,也不需要预加载全量数据,只改几行客户端代码就可以满足需求,可扩展性也强。
方案2:按输入值增量订阅数据
如果不想用async/await,也可以用Meteor自带的发布订阅能力,只订阅当前输入值对应的文档数据,不需要全量拉取所有name:
- 先新增服务端发布:
Meteor.publish('checkDocExistence', function(field, val) { if (!this.userId) return this.ready(); check(field, String); check(val, String); const query = {}; query[field] = val; return Docs.find(query, {fields: {_id: 1}}); });
- 客户端在输入框内容变化时触发订阅:
Template.createDoc.events({ // 监听输入框变化,动态订阅匹配的文档 'input #name'(e, template) { const value = e.target.value.trim(); if (value) { template.docSub = Meteor.subscribe('checkDocExistence', 'name', value); } }, 'click button[type=submit]'(e, template) { e.preventDefault(); const value = $('#name').val(); const fieldName = 'name'; // 直接查本地Mini Mongo,不需要异步调用 const exists = Docs.findOne({[fieldName]: value}); if (exists) { if (confirm(`Doc with ${value} as its ${fieldName} already exists. Are you sure you want to continue creating Doc?`)) { // 创建文档逻辑 } } // 用完可以停止订阅节省资源 if (template.docSub) template.docSub.stop(); } });
这个方案的好处是提交时完全没有异步等待,而且每次只拉取当前输入值对应的单条数据,资源消耗极低,也支持多字段校验的扩展。
内容的提问来源于stack exchange,提问作者user2402616
相关产品推荐
相关产品推荐

