小程序开发过程中,在使用小程序的模板下发功能时遇到这样一个问题:
核心错误在这句:"Cannot read property 'templateId' of undefined" 无法读取未定义的templateId。
我是将官方的小程序示例中模板消息下发模块的代码移植过来的,使用的是云调用中的模板消息下发,如图
将代码移植过来,自己创建目录文件,其中只用到js文件和html文件
修剪代码后的js代码
Page({
sendTemplateMessageViaCloudFunction(e) {
wx.cloud.callFunction({
name: 'openapi',
data: {
formId: e.detail.formId,
},
}).then((res) => {
console.log('[云调用] [发送模板消息] 成功: ', res)
}).catch(err => {
console.error('[云调用] [发送模板消息] 失败: ', err)
})
}
})
html代码
云函数目录
修剪后的index.js代码
const cloud = require('wx-server-sdk')
cloud.init()
async function sendTemplateMessage(event) {
const {
OPENID
} = cloud.getWXContext()
// 接下来将新增模板、发送模板消息、然后删除模板
// 注意:新增模板然后再删除并不是建议的做法,此处只是为了演示,模板 ID 应在添加后保存起来后续使用
const addResult = await cloud.openapi.templateMessage.addTemplate({
id: 'AT0002',
keywordIdList: [3, 4, 5]
})
console.log(addResult)
const templateId = addResult.result.templateId
const sendResult = await cloud.openapi.templateMessage.send({
touser: OPENID,
templateId,
formId: event.formId,
page: 'source/pages/index/index',
data: {
keyword1: {
value: '未名咖啡屋',
},
keyword2: {
value: '2019 年 1 月 1 日',
},
keyword3: {
value: '拿铁',
}
}
})
await cloud.openapi.templateMessage.deleteTemplate({
templateId,
})
return sendResult
}
// 云函数入口函数
// eslint-disable-next-line
exports.main = async(event) => {
return sendTemplateMessage(event)
}
代码环境如上给出,接下来分析下产生这个问题的原因。
在云调用中首次出现templateId的地方是这一句:const templateId = addResult.result.templateId
这句的作用是将addResult的对象result的templateId值赋给templateId
我们很好奇明明有赋值,但是为什么提示未定义?
我们这样做:只简单的添加模板,不发送模板消息,修改云调用的代码如下:
const cloud = require('wx-server-sdk')
cloud.init()
async function sendTemplateMessage(event) {
const {
OPENID
} = cloud.getWXContext()
// 接下来将新增模板、发送模板消息、然后删除模板
// 注意:新增模板然后再删除并不是建议的做法,此处只是为了演示,模板 ID 应在添加后保存起来后续使用
const addResult = await cloud.openapi.templateMessage.addTemplate({
id: 'AT0002',
keywordIdList: [3, 4, 5]
})
return addResult
}
// 云函数入口函数
// eslint-disable-next-line
exports.main = async(event) => {
return sendTemplateMessage(event)
}
运行后,控制台打印结果如下:
注意看result的值,result为一个对象,它有三个属性,分别为errCode,errMsg,templateId。我们在只添加模板时成功返回了templateId,按理说 "const templateId = addResult.result.templateId" 这行代码应该不会有错的。
难道说result里没有templateId?(怎么诡异?!)
于是乎,我在云调用中了添加一句:console.log(addResult) 想查看一下这个值在云开发打印的日志中是个什么情况
云调用代码如下:
const cloud = require('wx-server-sdk')
cloud.init()
async function sendTemplateMessage(event) {
const {
OPENID
} = cloud.getWXContext()
// 接下来将新增模板、发送模板消息、然后删除模板
// 注意:新增模板然后再删除并不是建议的做法,此处只是为了演示,模板 ID 应在添加后保存起来后续使用
const addResult = await cloud.openapi.templateMessage.addTemplate({
id: 'AT0002',
keywordIdList: [3, 4, 5]
})
console.log(addResult)
return addResult
}
// 云函数入口函数
// eslint-disable-next-line
exports.main = async(event) => {
return sendTemplateMessage(event)
}
云开发后台日志打印结果:
本地控制台打印结果:
我们发现云开发和本地的结果中templateId的所在位置不一样,云开发中templateId本身在"{}"中,本地的结果是templateId属于result的值。云开发中的打印结果是由console.log(addResult)打印的,本地的是由console.error('[云调用] [发送模板消息] 失败: ', err)打印的,而在云调用函数中templateId 是这样赋值的:const templateId = addResult.result.templateId
分析以上,就大概知道为什么了。
根据错误的提示 Cannot read property 'templateId' of undefined 无法读取templateId属性,以及云开发的打印结果
错误的原因很明显:
在本地打印的结果中templateId存在于result中,但是在云函数中templateId是在addResult中的
所以以下这个获取templateId的方式会报错:
const templateId = addResult.result.templateId
addResult根本没有result值,它的值是errCode,errMsg,templateId
知道错误原因后,解决方法很简单。
只需修改这句代码:
const templateId = addResult.templateId
修改后通过云调用模板消息成功下发:
问题解决后,有一个疑问是为什么在只添加模板时,云开发日志的打印结果和本地的打印结果templateId所在位置不一样?
原因是:
本地的 console.error('[云调用] [发送模板消息] 失败: ', err) 中的err携带的值不仅有云函数sendTemplateMessage的返回值addResult还有wx.cloud.callFunction函数调用后返回的信息,信息中说明了wx.cloud.callFunction函数是否调用成功。
还有一个疑问是,在小程序示例中云调用模板下发没有问题,移植代码却不行,这是为什么?修改那句却可以了。那官方的为什么就可以?只能说这个github下的小程序示例的源码比较诡异了...