0

在 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')]));

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

<div>​​​​​​​
  abc
  <h4>
    <br data-cke-filler="true">
  </h4>
</div>

我还尝试使用小部件来应用 contenteditable 属性。但是,无法在 h4 中输入文本。

<div class="ck-widget" contenteditable="false">​​​​​​​
  <h4 class="ck-editable" contenteditable="true">
    <br data-cke-filler="true">
  </h4>
</div>

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

[额外细节]

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

首先,排名列表是由<ol><li>标签构成的,因为它有多个项目。我通过定义“rankingList”和“rankingListItem”等两个模式解决了这个问题,因此我使用嵌套模型元素实现了动态元素。

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

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

<ol><!-- apply toWidget -->
  <li>
    <a href="link[editable]">
      <img src="image[editable]">
      <h3>title[editable]</h3>
      <p>notes[editable]</p>
    </a>
  </li>
  ...
</ol>

我希望视图元素如下:

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 ...

总之,我想使用可编辑视图来不破坏已定义的 DOM 树。我怎样才能意识到它?

4

1 回答 1

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>

在视图中,您有:

<ul>
  <li>
    Foo
    <ul>
      <li>Bar</li>
    </ul>
  </li>
</ul>

如果映射器知道 firstlistItem与 first 绑定,<li>并且 secondlistItem与 second 绑定<li>,则它能够正确转换位置。

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

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

假设,那myElement<div>. 所以里面写的任何东西都<div>将直接进入myElement,然后将在开头呈现myElement

<div>
  <h4></h4>
</div>

假设你刚刚写了xat <h4>,那个位置会被映射到模型中的myElementoffset 0,然后渲染到开头的 view 上<div>

模型:

<myElement>x</myElement>

看法:

<div>x<h4></h4></div>

如您所见,在您的情况下,它<h4>应该与myElement.

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

不幸的是,您的实际情况更加复杂,因为您内部有多个元素。CKE5 架构应该能够处理您的情况,但您必须了解,如果没有适当的指南,这几乎是不可能编写的。

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

模型:

<image alt="x" src="y">
  <caption>Foo</caption>
</image>

看法:

<figure class="image">
  <img alt="x" src="y" />
  <figcaption>Foo</caption>
</figure>

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

<rankItem imageSrc="x" linkUrl="y">
  <rankTitle>Foo</rankTitle>
  <rankNotes>Bar</rankNotes>
</rankItem>

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

<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>

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

For rankItem- 基于image元素 ( 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>

其中“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>

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

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

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

于 2017-10-30T11:41:58.340 回答