CKEditor5:点击气球面板按钮时如何更新模型?
CKEditor5 Callout插件对齐功能实现方案
核心思路
CKEditor5采用模型驱动视图的架构,不能直接操作DOM修改CSS类,正确流程是:通过命令修改模型属性 → 转换器将属性映射为视图的CSS类 → 按钮状态绑定到命令的当前值。
1. 扩展模型Schema,添加position属性
首先给callout模型元素添加position属性,定义允许的值和默认值:
// 在你的Callout插件init方法中 const schema = editor.model.schema; schema.extend('callout', { allowAttributes: 'position' }); // 设置默认对齐为全屏宽度 schema.setAttributeProperties('callout', 'position', { default: 'full', isFormatting: true });
2. 创建对齐命令
实现CalloutPositionCommand,负责修改模型的position属性,并处理命令的可用/激活状态:
import { Command } from 'ckeditor5/src/core'; export class CalloutPositionCommand extends Command { execute({ position }) { const model = this.editor.model; const selection = model.document.selection; model.change(writer => { // 获取当前选中的Callout元素 const callout = selection.getSelectedElement() || selection.getFirstPosition().findAncestor('callout'); if (callout) { writer.setAttribute('position', position, callout); } }); } refresh() { const model = this.editor.model; const selection = model.document.selection; const callout = selection.getSelectedElement() || selection.getFirstPosition().findAncestor('callout'); // 只有选中Callout时命令可用 this.isEnabled = !!callout; // 按钮激活状态:当前position与按钮对应值匹配 this.value = callout ? callout.getAttribute('position') : null; } }
在插件init方法中注册命令:
editor.commands.add('calloutPosition', new CalloutPositionCommand(editor));
3. 绑定气球面板按钮的事件与状态
修改气球视图的按钮创建逻辑,并在UI插件中绑定点击事件和状态:
优化CalloutBalloonView的按钮创建
给按钮添加name属性,标记对应的对齐值:
_createButton( name, label, icon) { const button = new ButtonView(); button.set( { label, icon, tooltip: true, class: 'testing', name // 存储对齐标识:left/full/right } ); return button; }
在CalloutUI中绑定按钮逻辑
修改_createBalloonView方法,给每个按钮绑定点击事件和激活状态:
_createBalloonView() { const editor = this.editor; const balloonView = new CalloutBalloonView( editor.locale ); const positionCommand = editor.commands.get('calloutPosition'); // 遍历所有对齐按钮,绑定事件和状态 [balloonView.leftAlignView, balloonView.fullView, balloonView.rightAlignView].forEach(button => { // 点击按钮执行对齐命令 this.listenTo(button, 'execute', () => { editor.execute('calloutPosition', { position: button.name }); }); // 绑定按钮的激活状态到命令当前值 button.bind('isOn').to(positionCommand, 'value', currentValue => currentValue === button.name); }); return balloonView; }
4. 添加模型与视图的转换器
将模型的position属性映射为视图元素的CSS类:
// 在插件init方法中 const conversion = editor.conversion; // 模型→视图:将position属性转为CSS类 conversion.for('downcast').attributeToAttribute({ model: 'position', view: position => { const baseClass = 'callout'; // 根据position值拼接对应的类 const alignClass = position === 'left' ? 'callout--left' : position === 'right' ? 'callout--right' : 'callout--full'; return { key: 'class', value: `${baseClass} ${alignClass}` }; } }); // 视图→模型:从CSS类还原position属性(支持粘贴/手动修改DOM后同步) conversion.for('upcast').attributeToAttribute({ view: { name: 'div', // 替换为你的Callout视图元素标签 classes: ['callout--left', 'callout--right', 'callout--full'] }, model: viewElement => { let position = 'full'; if (viewElement.hasClass('callout--left')) position = 'left'; else if (viewElement.hasClass('callout--right')) position = 'right'; return { key: 'position', value: position }; } });
5. 优化气球面板的显示逻辑(可选)
监听编辑器选中变化,仅当选中Callout时显示气球面板:
// 在CalloutUI的init方法中添加 this.listenTo(editor.editing.view.document, 'selectionChange', () => { const selection = editor.model.document.selection; const callout = selection.getSelectedElement() || selection.getFirstPosition().findAncestor('callout'); if (callout && !this._balloon.hasView(this.balloonView)) { this._showBalloon(); } else if (!callout && this._balloon.hasView(this.balloonView)) { this._balloon.remove(this.balloonView); } });
内容的提问来源于stack exchange,提问作者Brian
相关产品推荐
相关产品推荐

