簡體   English   中英

操作 CKEditor ViewElement 的 innerText

[英]Manipulate innerText of a CKEditor ViewElement

我正在為#neoscms 的CKEditor5 創建一個小自定義插件。 Neos 正在使用#ckeditor5,但具有自定義視圖。

該插件或多或少是一個占位符插件。 用戶可以為項目(標識符和標簽)配置具有鍵值存儲的數據源。 CKEditor 中的下拉列表中填充了項目,當用戶從下拉列表中選擇項目時,它會創建一個占位符元素,該元素應以帶有一些數據屬性的 span 元素結尾。

主要思想是有一個空元素,只有數據屬性來識別元素並能夠分配實時數據。 但事實證明,實時數據的事情很棘手。 當我在網站上使用額外的 JS 片段操作跨度時,CKEditor 無法處理此問題。

是否可以在 DOM 中操作視圖元素並且仍然有工作的編輯器? 如果我只是在 downCasting 中添加內部文本並且不替換某些內容,則插件可以正常工作。 但是實時數據會很好。

帶有元素的 Neos 后端

也許該代碼給出了包的概念。 它還沒有准備好,因為這或多或少是主要功能;)

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的跨度,並將一個帶有實時數據的值寫入跨度。 當然,這在前端有效,但使用 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);
                }
            });
        };

現在唯一的缺點是我需要重新加載頁面,但已經讀到這可能是由於我的元素向下轉換並且我更改了屬性。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM