如何让hcard.js在Angular 2+与CKEditor 5中实现联系人拖拽功能
实现CKEditor 5 Angular项目中的联系人(Hcard)拖拽功能
核心思路
CKEditor 5的拖拽功能需要结合自定义Widget插件与DragDrop模块实现,核心是监听外部拖拽事件,将目标元素转换为编辑器可识别的模型结构,再通过转换器映射为视图Widget。
具体实现步骤
1. 编写自定义Hcard Widget插件
创建hcard-widget.plugin.ts文件,实现Widget的模型定义、转换规则及拖拽处理逻辑:
import { Plugin } from '@ckeditor/ckeditor5-core'; import { Widget } from '@ckeditor/ckeditor5-widget'; import { toWidget } from '@ckeditor/ckeditor5-widget/src/utils'; import { DragDrop } from '@ckeditor/ckeditor5-upload'; export class HcardWidget extends Plugin { static get requires() { return [Widget, DragDrop]; } init() { const editor = this.editor; const schema = editor.model.schema; const conversion = editor.conversion; // 注册模型结构 schema.register('hcard', { allowWhere: '$block', isObject: true, allowContentOf: '$block' }); schema.register('hcardName', { allowIn: 'hcard', allowContentOf: '$text' }); // 视图→模型转换(处理拖拽进来的HTML) conversion.for('upcast').elementToElement({ model: 'hcard', view: { name: 'div', classes: 'hcard' } }); conversion.for('upcast').elementToElement({ model: 'hcardName', view: { name: 'span', classes: 'hcard-name' } }); // 模型→视图转换(渲染编辑器内的Widget) conversion.for('downcast').elementToElement({ model: 'hcard', view: (_, { writer }) => { const div = writer.createContainerElement('div', { class: 'hcard' }); return toWidget(div, writer, { label: '联系人卡片' }); } }); conversion.for('downcast').elementToElement({ model: 'hcardName', view: (_, { writer }) => writer.createEditableElement('span', { class: 'hcard-name' }) }); // 监听编辑器drop事件,处理外部拖拽 editor.plugins.get(DragDrop).on('drop', (evt, data) => { const dragHtml = data.dataTransfer.getData('text/html'); const doc = new DOMParser().parseFromString(dragHtml, 'text/html'); const hcardEl = doc.querySelector('.hcard'); if (hcardEl) { evt.stop(); // 阻止默认拖拽行为 const name = hcardEl.querySelector('.hcard-name')?.textContent || ''; // 插入到编辑器模型 editor.model.change(writer => { const hcard = writer.createElement('hcard'); const nameNode = writer.createElement('hcardName'); writer.appendText(name, nameNode); writer.append(nameNode, hcard); editor.model.insertContent(hcard, editor.model.document.selection); }); } }, { priority: 'high' }); } }
2. Angular组件中集成插件与拖拽触发元素
在使用CKEditor的组件中配置编辑器,并添加可拖拽的联系人卡片:
import { Component } from '@angular/core'; import { ClassicEditor } from '@ckeditor/ckeditor5-editor-classic'; import { Essentials, Paragraph } from '@ckeditor/ckeditor5-core'; import { HcardWidget } from './hcard-widget.plugin'; @Component({ selector: 'app-ckeditor-hcard', template: ` <div class="hcard draggable" draggable="true" (dragstart)="handleDragStart($event)"> <span class="hcard-name">张三</span> </div> <ckeditor [editor]="Editor" [config]="editorConfig" [(ngModel)]="editorContent"> </ckeditor> `, styles: [` .hcard { padding: 8px; border: 1px solid #ddd; border-radius: 4px; margin: 10px 0; cursor: grab; background: #f8f8f8; } .hcard-name { font-weight: 600; } ckeditor { margin-top: 20px; } `] }) export class CkeditorHcardComponent { Editor = ClassicEditor; editorContent = ''; editorConfig = { plugins: [Essentials, Paragraph, HcardWidget], toolbar: ['undo', 'redo'] }; handleDragStart(event: DragEvent) { const target = event.target as HTMLElement; event.dataTransfer?.setData('text/html', target.outerHTML); } }
3. 模块中导入CKEditor依赖
在对应Angular模块中引入CKEditor模块:
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { FormsModule } from '@angular/forms'; import { CKEditorModule } from '@ckeditor/ckeditor5-angular'; import { CkeditorHcardComponent } from './ckeditor-hcard.component'; @NgModule({ imports: [BrowserModule, FormsModule, CKEditorModule], declarations: [CkeditorHcardComponent], bootstrap: [CkeditorHcardComponent] }) export class AppModule { }
关键注意点
- 拖拽时必须传递
text/html格式的数据,确保编辑器能解析目标元素 - 自定义Widget的Schema配置需严格遵循CKEditor 5的模型规则,避免内容过滤
drop事件监听需设置高优先级,防止默认拖拽行为覆盖自定义逻辑
内容的提问来源于stack exchange,提问作者TommyF
相关产品推荐
相关产品推荐

