Manipulate innerText of a CKEditor ViewElement



我正在为#neoscms的ckedor5创建一个小的自定义插件。Neos正在使用# ckedor5,但是使用自定义视图。

插件或多或少是一个占位符插件。用户可以使用项(标识符和标签)的键值存储配置数据源。CKEditor中的下拉列表由项目填充,当用户从下拉列表中选择一个项目时,它会创建一个占位符元素,该元素应该以一个带有一些数据属性的span元素结束。

主要思想是使用一个空元素和数据属性来标识元素并能够分配活动数据。但事实证明,实时数据是很棘手的。当我在网站上使用额外的JS片段操作span时,CKEditor无法处理此问题。

是否有可能在DOM中操作视图元素,并且仍然有一个工作的编辑器?这个插件工作得很好,如果我只是在向下投射添加内部文本,不替换一些东西。但是实时数据会很好。

带有元素的Neos Backend

也许这段代码给出了一个包的概念。它还没有准备好,因为这或多或少是主要特性;)

import {Plugin, toWidget, viewToModelPositionOutsideModelElement, Widget,} from "ckeditor5-exports";
import PlaceholderCommand from "./placeHolderCommand";
export default class PlaceholderEditing extends Plugin {
static get requires() {
return [Widget];
}
init() {
this._defineSchema();
this._defineConverters();
this.editor.commands.add(
"placeholder",
new PlaceholderCommand(this.editor)
);
this.editor.editing.mapper.on(
"viewToModelPosition",
viewToModelPositionOutsideModelElement(this.editor.model, (viewElement) =>
viewElement.hasClass("internezzo-placeholder")
)
);
this.editor.config.define("placeholderProps", {
types: ["name", "node", "nodePath"],
});
this.editor.config.define("placeholderBrackets", {
open: "[",
close: "]",
});
}
_defineSchema() {
const schema = this.editor.model.schema;
schema.register("placeholder", {
allowWhere: "$text",
isInline: true,
isObject: true,
allowAttributes: [
"name",
"node",
"nodePath",
"data-placeholder-identifier",
"data-node-identifier",
"data-node-path",
],
});
}
_defineConverters() {
const conversion = this.editor.conversion;
const config = this.editor.config;
conversion.for("upcast").elementToElement({
view: {
name: "span",
classes: ["foobar-placeholder"],
},
model: (viewElement, writer) => {
const name = viewElement.getAttribute('data-placeholder-identifier');
const node = viewElement.getAttribute('data-node-identifier');
const nodePath = viewElement.getAttribute('data-node-path');
const modelWriter = writer.writer || writer;
return modelWriter.createElement("placeholder", {name, node, nodePath, editable: false});
},
});
conversion.for("editingDowncast").elementToElement({
model: "placeholder",
view: (modelItem, writer) => {
const viewWriter = writer.writer || writer;
const widgetElement = createPlaceholderView(modelItem, viewWriter);
return toWidget(widgetElement, viewWriter);
},
});
conversion.for("dataDowncast").elementToElement({
model: "placeholder",
view: (modelItem, writer) => {
const viewWriter = writer.writer || writer;
return createPlaceholderView(modelItem, viewWriter);
},
});
// Helper method for downcast converters.
function createPlaceholderView(modelItem, viewWriter) {
const name = modelItem.getAttribute("name");
const node = modelItem.getAttribute("node");
const nodePath = modelItem.getAttribute("nodePath");
const placeholderView = viewWriter.createContainerElement("span", {
class: "foobar-placeholder",
"data-placeholder-identifier": name,
"data-node-identifier": node,
"data-node-path": nodePath,
});
// Would be nice to remove that and have just empty spans that get dynamic data
let innerText = config.get("placeholderBrackets.open") + name;
innerText += config.get("placeholderBrackets.close");
viewWriter.insert(
viewWriter.createPositionAt(placeholderView, 0),
viewWriter.createText(innerText)
);
return placeholderView;
}
}
}

因此,网站使用的额外JS片段正在搜索具有foobar-placeholder类的span,并将带有实时数据的值写入span中。这在前端工作,当然,但使用CKEditor的CMS后端有数据变化的问题。

我找不到CKEditor文档的解决方案,也许我以某种方式滥用了API,但我现在找到了一个适合我的工作解决方案。

我的网站片段现在通过广播消息与插件通信。然后搜索占位符元素并检查是否需要更改属性。

const broadcastChannel = new BroadcastChannel('placeholder:changeData');
broadcastChannel.postMessage({identifier: name, value});

在插件

// Receive new values for placeholder via broadcast
const broadcastChannel = new BroadcastChannel('placeholder:changeData');
broadcastChannel.onmessage = (message) => {
const identifier = get('data.identifier', message);
const newValue = get('data.value', message);
this.editor.model.change( writer => {
if (identifier) {
this._replaceAttribute(writer, identifier, newValue);
}
});
};

现在唯一的缺点是我需要重新加载页面,但已经读到这可能是由于我的元素向下转换和我改变属性。

最新更新