Discord.js 14.22.1含选择菜单的模态组件提交报错排查
问题原因与解决方案
错误根源
你遇到的cannot read properties of undefined (reading 'forEach')错误,核心原因是手动构建的Modal组件结构完全不符合Discord API规范,导致Discord.js解析提交后的交互数据时出现异常。具体问题包括:
- 错误使用了不存在的组件类型
type:18,模态中的行组件类型应为1(即ActionRow); - 错误用
component字段包裹子组件,正确结构是将子组件放在ActionRow的components数组中; - 未遵循模态组件规则:每个ActionRow只能包含一个组件(文本输入或选择菜单)。
修正后的代码
使用Discord.js官方推荐的Builder类构建模态组件,避免手动拼写字段出错:
const { SlashCommandBuilder, ModalBuilder, ActionRowBuilder, TextInputBuilder, TextInputStyle, StringSelectMenuBuilder, UserSelectMenuBuilder, ChannelSelectMenuBuilder, ComponentType } = require('discord.js'); module.exports = { data: new SlashCommandBuilder() .setName('testmodal') .setDescription('Opens a modal with all supported input types'), async execute(interaction) { // 构建模态 const modal = new ModalBuilder() .setCustomId('test_modal_inputs') .setTitle('Test Modal Inputs') // 添加各类组件行 .addComponents( new ActionRowBuilder().addComponents( new TextInputBuilder() .setCustomId('construction_speed') .setLabel('Construction Speed (%)') .setStyle(TextInputStyle.Short) .setPlaceholder('e.g. 0 or 15') .setRequired(false) ), new ActionRowBuilder().addComponents( new TextInputBuilder() .setCustomId('notes') .setLabel('Notes or Comments') .setStyle(TextInputStyle.Paragraph) .setPlaceholder('Optional notes...') .setRequired(false) ), new ActionRowBuilder().addComponents( new StringSelectMenuBuilder() .setCustomId('buffs_select') .setPlaceholder('Select all that apply') .setMinValues(0) .setMaxValues(3) .setOptions([ { label: 'VP Buff', value: 'vp' }, { label: 'Mercantilism Buff', value: 'merc' }, { label: 'Double Time Buff', value: 'double' } ]) ), new ActionRowBuilder().addComponents( new UserSelectMenuBuilder() .setCustomId('user_select') .setPlaceholder('Pick a user') ), new ActionRowBuilder().addComponents( new ChannelSelectMenuBuilder() .setCustomId('channel_select') .setPlaceholder('Pick a channel') ) ); await interaction.showModal(modal); }, async handleModalSubmit(interaction) { const fields = getModalFieldValues(interaction); const speed = fields.construction_speed || '0'; const notes = fields.notes || 'None'; const buffs = fields.buffs_select?.join(', ') || 'None'; const user = fields.user_select?.map(u => `<@${u}>`).join(', ') || 'None'; const channel = fields.channel_select?.map(c => `<#${c}>`).join(', ') || 'None'; await interaction.reply({ content: `**Construction Speed:** ${speed}%\n**Notes:** ${notes}\n**Buffs Applied:** ${buffs}\n**Selected User:** ${user}\n**Selected Channel:** ${channel}`, flags: 64 }); } }; function getModalFieldValues(interaction) { const values = {}; for (const row of interaction.components ?? []) { for (const comp of row.components ?? []) { const id = comp.customId; if (!id) continue; if (comp.type === ComponentType.TextInput) { values[id] = comp.value ?? ''; } else if ( [ ComponentType.StringSelect, ComponentType.UserSelect, ComponentType.ChannelSelect, ComponentType.RoleSelect, ComponentType.MentionableSelect ].includes(comp.type) ) { values[id] = comp.values ?? []; } } } return values; }
额外说明
- Discord.js 14.22.1确实支持模态中的选择菜单组件,但必须严格遵循API结构规范;
- Builder类会自动处理字段名转换(如
customId转custom_id)和结构验证,大幅降低手动构建的出错概率; - 确保机器人已正确注册模态提交事件监听器,例如在主文件中添加:
client.on('interactionCreate', async interaction => { if (interaction.isModalSubmit()) { const command = interaction.client.commands.get('testmodal'); if (command?.handleModalSubmit) { await command.handleModalSubmit(interaction); } } });
内容的提问来源于stack exchange,提问作者UnsureCoding
相关产品推荐
相关产品推荐

