无法输入CKEditor5的EditableElement

ino*_*tch 0 ckeditor ckeditor5

在CKEditor5中,我尝试实现自定义元素以将模型转换为视图以进行编辑.然后,容器元素(@ ckeditor/ckeditor5-engine/src/view/containerelement)中的可编辑元素(@ ckeditor/ckeditor5-engine/src/view/editableelement)专注于父容器元素,无法编辑.

例如,如果实现如下:

buildModelConverter().for(editing.modelToView)
            .fromElement('myElement')
            .toElement(new ContainerElement('div', {}, [new EditableElement('h4')]));
Run Code Online (Sandbox Code Playgroud)

插入'myElement'和keydown"abc"后实际编辑dom的结果.(我希望将"abc"的文本输入到h4标签但是...)

<div>???????
  abc
  <h4>
    <br data-cke-filler="true">
  </h4>
</div>
Run Code Online (Sandbox Code Playgroud)

我也尝试使用widget来应用contenteditable属性.但是,无法在h4中输入文本.

<div class="ck-widget" contenteditable="false">???????
  <h4 class="ck-editable" contenteditable="true">
    <br data-cke-filler="true">
  </h4>
</div>
Run Code Online (Sandbox Code Playgroud)

是这个错误,还是我对容器元素的理解有误?

[额外细节]

我假设为排名列表制作一个小部件插件.

首先,由于具有多个项目,排名列表由<ol><li>标签构成.我通过定义两个模式(例如"rankingList"和"rankingListItem")来解决这个问题,因此我使用嵌套模型元素实现了动态元素.

const item1 = new ModelElement('rankingListItem');
const item2 = new ModelElement('rankingListItem');
const model = new ModelElement('rankingList', {}, [item1, item2]);
// and insert
Run Code Online (Sandbox Code Playgroud)

接下来,排名列表的项目具有链接,图像,标题和注释.因此,排名列表项具有以下DOM结构:

<ol><!-- apply toWidget -->
  <li>
    <a href="link[editable]">
      <img src="image[editable]">
      <h3>title[editable]</h3>
      <p>notes[editable]</p>
    </a>
  </li>
  ...
</ol>
Run Code Online (Sandbox Code Playgroud)

我希望view元素如下:

const {ref, src, title, notes} = data; // how to get data?
const view = new ContainerElement('a', {ref}, [
    new EmptyElement('img', {src}),
    new EditableElement('h3', {}, new Text(title)),
    new EditableElement('p', {}, new Text(title)),
  ]);
// maybe incorrect ...
Run Code Online (Sandbox Code Playgroud)

总之,我想使用可编辑视图来破坏已定义的DOM树.我怎么才能意识到这一点?

Szy*_*lik 5

感谢您描述您的案例.对我们来说,了解开发人员如何使用编辑器以及您的期望是什么意味着很多.

不幸的是,这看起来像一个非常复杂的功能.它看起来也需要自定义UI(编辑链接url和图像src - 除非它们在添加到编辑器后不会更改).你似乎在努力解决两个问题:

  • 模型和视图之间的位置映射,
  • 嵌套的可编辑.

首先,回答你的问题EditableElement- 使用它们h3p元素似乎是正确的.

但是,这种复杂的功能需要定制转换器.转换器构建器(您使用过的)专用于在简单情况下使用,例如元素到元素转换或属性到属性转换.

在漂亮的API背后,构建转换器是一个函数工厂,它创建一个或多个函数.然后将它们作为回调添加到ModelConversionDispatcher(这editor.editing.modelToView是一个实例).ModelConversionDispatcher在转换期间触发一系列事件,这是将模型中的更改转换为视图的过程.

正如我所提到的,你必须自己编写那些转换函数.

由于这对于详细而彻底的答案来说太大了,我只是简单介绍一下你应该感兴趣的内容.不幸的是,还没有关于从头开始创建自定义转换器的指南.这是一个非常广泛的主题.

首先,让我向您解释大多数问题的来源.正如您所知,编辑器有三层:模型(数据),视图(类似DOM的结构)和DOM.模型转换为视图,视图呈现给DOM.另外,另一方面,当您加载数据时,DOM将转换为视图并将视图转换为模型.这就是您需要为您的功能提供模型到视图转换器和视图到模型转换器的原因.

这个难题的重要部分是engine.conversion.Mapper.它的作用是从模型到视图映射元素和位置.正如您可能已经看到的那样,该模型可能与视图完全不同.这些之间的正确位置映射是关键.当您在插入位置(在DOM中)键入字母时,此位置将映射到模型,并且字母将插入模型中,然后仅转换回视图和DOM.如果视图到模型的位置转换错误,您将无法在该位置键入或实际执行任何操作.

Mapper它本身很简单.它所需要的只是指定哪些视图元素绑定到哪些模型元素.例如,在模型中您可能具有:

<listItem type="bulleted" indent="0">Foo</listItem>
<listItem type="bulleted" indent="1">Bar</listItem>
Run Code Online (Sandbox Code Playgroud)

在视图中你有:

<ul>
  <li>
    Foo
    <ul>
      <li>Bar</li>
    </ul>
  </li>
</ul>
Run Code Online (Sandbox Code Playgroud)

如果映射器知道第一个listItem绑定第一个<li>而第二个listItem绑定第二个<li>,则它能够正确转换位置.

回到你的情况.每个转换器都必须提供数据Mapper.由于您使用了转换器构建器,因此构建的转换器已经执行此操作.但它们很简单,所以当你提供:

buildModelConverter().for(editing.modelToView)
        .fromElement('myElement')
        .toElement(new ContainerElement('div', {}, [new EditableElement('h4')]));
Run Code Online (Sandbox Code Playgroud)

假设,它myElement与...绑定<div>.所以写在里面的任何内容都<div>将直接发送myElement,然后在下面开始呈现myElement:

<div>
  <h4></h4>
</div>
Run Code Online (Sandbox Code Playgroud)

假设你刚写x<h4>,那个位置将被映射到myElement偏移0的模型,然后呈现给查看之初<div>.

模型:

<myElement>x</myElement>
Run Code Online (Sandbox Code Playgroud)

视图:

<div>x<h4></h4></div>
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,在您的情况下,它<h4>应该被绑定myElement.

目前,我们处于重构阶段.其中一个目标是为转换器制造商提供更多实用功能.其中一个实用程序是具有包装元素的元素的转换器,如上面的"div + h4"情况.这也是图像特征的一种情况.图像由<image>模型表示,但它<figure><img /></figure>在视图中.您可以查看ckeditor5-image这些转换器现在的样子.我们想简化它们.

不幸的是,你的真实案例更加复杂,因为你里面有多个元素.CKE5架构应该能够处理你的情况,但你必须明白,如果没有适当的指南,这几乎是不可能的.

如果你想修补,你应该学习 ckeditor5-image回购.这并不容易,但这是最好的方法.Image插件与ImageCaption你的情况非常相似.

模型:

<image alt="x" src="y">
  <caption>Foo</caption>
</image>
Run Code Online (Sandbox Code Playgroud)

视图:

<figure class="image">
  <img alt="x" src="y" />
  <figcaption>Foo</caption>
</figure>
Run Code Online (Sandbox Code Playgroud)

在你的情况下,我会在这些线之间看到模型:

<rankItem imageSrc="x" linkUrl="y">
  <rankTitle>Foo</rankTitle>
  <rankNotes>Bar</rankNotes>
</rankItem>
Run Code Online (Sandbox Code Playgroud)

而且我会使视图更重,但编写转换器会更容易:

<li contenteditable="false">
  <a href="y">
    <img src="x" />
    <span class="data">
      <span class="title" contenteditable="true">Foo</span>
      <span class="notes" contenteditable="true">Bar</span>
    </span>
  </a>
</li>
Run Code Online (Sandbox Code Playgroud)

为- rankTitlerankNotes- 基于caption元素(ckeditor5-image/src/imagecaption/imagecaptionengine.js).

For rankItem- 基于imageelement(ckeditor5-image/src/image/).

再一次 - 请记住,我们正在简化所有这一切.我们希望人们编写自己的功能,甚至是那些像你这样复杂的功能.但是,我们知道它现在有多复杂.这就是为什么目前没有文档 - 我们正在寻求改变和简化事情.

最后 - 您可以使用Link转换器构建器构建插件和元素来创建更简单的排名列表:

  • rankList- > <ol class="rank">,
  • rankItem- > <li>,
  • rankImage- > <img />,
  • rankNotes- > <span class="notes">,
  • rankTitle- > <span class="title">.

但是,它可能会搞砸,因为整个结构将是可编辑的.

模型:

<rankList>
  <rankItem>
    <rankImage linkHref="" />
    <rankTitle>Foo</rankTitle>
    <rankNotes>Bar</rankNotes>
  </rankItem>
  ...
</rankList>
Run Code Online (Sandbox Code Playgroud)

其中"Foo"和"Bar"也有linkHref属性集.

视图:

<ol class="rank">
  <li>
    <a href=""><img src="" /></a>
    <span class="title"><a href="">Title</a></span>
    <span class="notes"><a href="">Foo</a></span>
  </li>
  ...
</ol>
Run Code Online (Sandbox Code Playgroud)

只要我们在重构之前和编写指南之前,这样的事情,虽然远非完美,但应该更容易编写.

当然,您还必须提供视图到模型转换器.您可能想要自己编写它们(看看ckeditor5-list/src/converters.js viewModelConverter()- 虽然它会更容易,因为它将是扁平的,而不是嵌套列表).或者您可以通过转换器构建器生成它们.

也许可以使用上面的方法(更简单)但使用contentEditable属性来控制结构.rankList必须要转换成<ol>contentEditable="false".例如,也许你可以某种方式toWidget用于更好的选择处理.rankNotes并且rankTitle必须转换为元素contentEditable="true"(可能使用toWidgetEditable()).