Angular中如何维护contenteditable div的显示文本与未格式化存储文本
解决方案
核心思路
全程维护两个独立数据:
- 视图层:contenteditable里渲染带样式的@用户富文本,每个@用户的span节点绑定
data-user-id自定义属性存储对应用户ID,避免重名冲突 - 数据层:单独维护
rawComment变量,每次视图内容变更时遍历DOM节点拼接出带##userId##标记的原始内容,直接用于后续存储
修改后的代码
HTML代码
<div id="commentInput" class='commentInput' placeholder="Add Comment..." [mention]="users" [mentionConfig]="mentionConfig" contenteditable="true" (itemSelected)="itemSelected($event)" (input)="updateRawComment()"> </div>
TS代码
// 用于提交存储的原始带标记文本 rawComment: string = ''; mentionConfig = { labelKey:'fullName', dropUp: true, allowSpace: true, mentionSelect: (e: any) => { return '##' + e.userId + '##'; } }; public htmlDoc: HTMLElement; @Input() users: Users; itemSelected(event: any) { const user = this.users.find(user => user.userId === event.userId); if (!user) return; setTimeout(() => { this.htmlDoc = document.getElementById('commentInput'); // 替换时给span加自定义属性存userId this.htmlDoc.innerHTML = this.htmlDoc.innerHTML.replace( '##' + event.userId + '##', ` <span style="font-weight: bold; color: #0072f0" data-user-id="${event.userId}">@${user.fullName}</span> ` ); this.selectEnd(); // 选中用户后同步更新原始文本 this.updateRawComment(); }, 10); } selectEnd() { const range = document.createRange(); range.selectNodeContents(this.htmlDoc); range.collapse(false); const selection = window.getSelection(); selection.removeAllRanges(); selection.addRange(range); } // 核心方法:遍历DOM生成带标记的原始文本 updateRawComment() { this.htmlDoc = document.getElementById('commentInput'); let rawContent = ''; // 遍历所有子节点 this.htmlDoc.childNodes.forEach(node => { // 普通文本节点直接取内容 if (node.nodeType === Node.TEXT_NODE) { rawContent += node.textContent; } // 是@用户的span节点,替换为##userId##格式 else if (node.nodeType === Node.ELEMENT_NODE && node.tagName === 'SPAN' && node.dataset.userId) { rawContent += `##${node.dataset.userId}##`; } // 其他富文本节点可根据业务需求扩展处理逻辑 }); this.rawComment = rawContent.trim(); }
使用说明
提交评论时直接使用rawComment变量即可,该变量会实时同步输入框的所有内容,所有@用户都会自动替换为##userId##格式,不会受用户名重复影响。
内容的提问来源于stack exchange,提问作者Cody2099
相关产品推荐
相关产品推荐

