React中TinyMCE自定义按钮动态构建菜单项问题求助
React版TinyMCE动态构建自定义菜单按钮菜单项问题
我用React版TinyMCE组件加载编辑器,已经实现了文档里的自定义菜单项示例,但想动态构建自定义菜单按钮的菜单项。根据文档说明,fetch函数可以在菜单每次打开时构建菜单项,我尝试把fetch指向外部的buildCustomEditorButtons函数来实现动态构建,但遇到了语法或实现问题。
现有可运行示例代码
<Editor apiKey='123' onChange={(e) => this.handleDirty(e)} onInit={(evt, editor) => this.editorRef.current = editor} initialValue={this.renderInputValue('body')} init={{ height: 600, menubar: true, plugins: [ 'advlist', 'autolink', 'lists', 'link', 'image', 'charmap', 'preview', 'anchor', 'searchreplace', 'visualblocks', 'code', 'fullscreen', 'insertdatetime', 'media', 'table', 'code', 'help', 'wordcount' ], toolbar: 'undo redo | blocks | ' + 'bold italic forecolor | alignleft aligncenter ' + 'alignright alignjustify | bullist numlist outdent indent | ' + 'removeformat | insertvarbutton help', setup: function (editor) { /* Menu items are recreated when the menu is closed and opened, so we need a variable to store the toggle menu item state. */ var toggleState = false; /* example, adding a toolbar menu button */ editor.ui.registry.addMenuButton('insertvarbutton', { text: 'Insert Variable', fetch: function (callback) { let test = [] let items = [ { type: 'menuitem', text: 'Menu item 1', onAction: function () { editor.insertContent(' <em>You clicked menu item 1!</em>'); } }, { type: 'nestedmenuitem', text: 'Menu item 2', icon: 'user', getSubmenuItems: function () { return [ { type: 'menuitem', text: 'Sub menu item 1', icon: 'unlock', onAction: function () { editor.insertContent(' <em>You clicked Sub menu item 1!</em>'); } }, { type: 'menuitem', text: 'Sub menu item 2', icon: 'lock', onAction: function () { editor.insertContent(' <em>You clicked Sub menu item 2!</em>'); } } ]; } } ]; callback(items); } }); }, content_style: 'body { font-family:Helvetica,Arial,sans-serif; font-size:14px }' }} />
期望的实现方式
setup: function (editor) { /* Menu items are recreated when the menu is closed and opened, so we need a variable to store the toggle menu item state. */ var toggleState = false; /* example, adding a toolbar menu button */ editor.ui.registry.addMenuButton('insertvarbutton', { text: 'Insert Variable', fetch: this.buildCustomEditorButtons(editor) }); },
外部构建函数代码
buildCustomEditorButtons(editor) { let test = [] for (const [key, value] of Object.entries(this.state.templateVars.system_vars)) { test << { type: "nestedmenuitem", text: key, getSubmenuItems: function () { value.map((i) => { return { type: 'menuitem', text: i.name, onAction: function () { editor.insertContent(i.value); } } }) } } } return(test) }
问题分析与修复方案
你的代码存在几个关键问题,逐个修复即可:
fetch函数的调用方式错误
TinyMCE的fetch需要接收一个函数,而你直接执行了this.buildCustomEditorButtons(editor),相当于把函数的返回值传给了fetch,不符合要求。需要让buildCustomEditorButtons返回一个接受callback参数的函数,或者在fetch内部调用它并传入callback。数组添加元素的语法错误
JavaScript中没有test << { ... }的数组添加语法,应该使用test.push({ ... })。map函数返回值未处理value.map(...)生成了新的菜单项数组,但你没有在getSubmenuItems中返回这个数组,导致子菜单无法正常渲染。this指向问题setup如果用普通函数定义,内部的this不会指向React组件实例,需要改用箭头函数来确保this指向正确。
修复方案一:让构建函数返回符合要求的fetch函数
buildCustomEditorButtons(editor) { // 返回一个接收callback参数的函数,适配TinyMCE的fetch要求 return (callback) => { let test = []; for (const [key, value] of Object.entries(this.state.templateVars.system_vars)) { test.push({ type: "nestedmenuitem", text: key, getSubmenuItems: () => { // 返回map生成的子菜单项数组 return value.map((i) => ({ type: 'menuitem', text: i.name, onAction: () => { editor.insertContent(i.value); } })); } }); } callback(test); }; }
对应的setup配置(用箭头函数确保this指向组件实例):
setup: (editor) => { var toggleState = false; editor.ui.registry.addMenuButton('insertvarbutton', { text: 'Insert Variable', fetch: this.buildCustomEditorButtons(editor) }); },
修复方案二:在fetch内部直接调用构建函数
setup: (editor) => { var toggleState = false; editor.ui.registry.addMenuButton('insertvarbutton', { text: 'Insert Variable', fetch: (callback) => { const items = this.buildCustomEditorButtons(editor); callback(items); } }); },
此时对应的构建函数可以直接返回菜单项数组:
buildCustomEditorButtons(editor) { let test = []; for (const [key, value] of Object.entries(this.state.templateVars.system_vars)) { test.push({ type: "nestedmenuitem", text: key, getSubmenuItems: () => { return value.map((i) => ({ type: 'menuitem', text: i.name, onAction: () => { editor.insertContent(i.value); } })); } }); } return test; }
内容的提问来源于stack exchange,提问作者Sean
相关产品推荐
相关产品推荐

