在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>
我还尝试使用widget来应用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>
我希望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 ...
总之,我想使用可编辑的视图来破坏已定义的DOM树。 我怎么能意识到它?
答案 0 :(得分:5)
感谢您描述您的案例。对我们来说,了解开发人员如何使用编辑器以及您的期望是什么意味着很多。
不幸的是,这看起来像一个非常复杂的功能。它看起来也需要自定义UI(编辑链接url和图像src - 除非它们在添加到编辑器后不会更改)。你似乎很难解决两个问题:
首先,回答关于EditableElement
的问题 - 将它们用于h3
和p
元素似乎是正确的。
但是,这种复杂的功能需要自定义转换器。转换器构建器(您使用过的)专用于在简单情况下使用,例如元素到元素转换或属性到属性转换。
在漂亮的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>
如果映射器知道第一个listItem
与第一个<li>
绑定,而第二个listItem
与第二个<li>
绑定,则它能够正确转换位置。
回到你的案子。每个转换器都必须为Mapper
提供数据。由于您使用了转换器构建器,因此构建的转换器已经执行此操作。但它们很简单,所以当你提供:
buildModelConverter().for(editing.modelToView)
.fromElement('myElement')
.toElement(new ContainerElement('div', {}, [new EditableElement('h4')]));
假设myElement
与<div>
绑定。所以写在<div>
内的任何内容都会直接转到myElement
,然后会在myElement
的开头呈现:
<div>
<h4></h4>
</div>
假设您刚刚在x
处写了<h4>
,那么该位置将映射到模型中的myElement
偏移0
,然后在{的开始处呈现以进行查看{1}}。
型号:
<div>
查看:
<myElement>x</myElement>
正如您所看到的,在您的情况下,<div>x<h4></h4></div>
应与<h4>
绑定。
目前,我们处于重构阶段。其中一个目标是为转换器制造商提供更多实用功能。其中一个实用程序是具有包装元素的元素的转换器,例如&#34; div + h4&#34;上面的案例。这也是图像特征的一种情况。图像在模型中由myElement
表示,但在视图中为<image>
。您可以查看<figure><img /></figure>
以了解这些转换器现在的样子。我们想简化它们。
不幸的是,你的真实情况更复杂,因为你里面有多个元素。 CKE5架构应该能够处理你的情况,但你必须明白,如果没有适当的指南,这几乎是不可能的。
如果你想修补一下,你应该学习 ckeditor5-image
回购。这不容易,但这是最好的方式。 ckeditor5-image
插件与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>
和rankTitle
- 基于rankNotes
元素(caption
)。
对于ckeditor5-image/src/imagecaption/imagecaptionengine.js
- 基于rankItem
元素(image
)。
再次 - 请记住,我们正在简化所有这一切。我们希望人们编写自己的功能,甚至是那些像你这样复杂的功能。但是,我们知道它现在有多复杂。这就是为什么目前没有文档的原因 - 我们正在寻求改变和简化事情。
最后 - 您可以使用ckeditor5-image/src/image/
插件创建排名列表更简单,并使用转换器构建器构建元素:
Link
- &gt; rankList
,<ol class="rank">
- &gt; rankItem
,<li>
- &gt; rankImage
,<img />
- &gt; rankNotes
,<span class="notes">
- &gt; rankTitle
。但是,它可能会搞砸,因为整个结构都是可编辑的。
型号:
<span class="title">
在哪里&#34; Foo&#34;和&#34; Bar&#34;也设置了<rankList>
<rankItem>
<rankImage linkHref="" />
<rankTitle>Foo</rankTitle>
<rankNotes>Bar</rankNotes>
</rankItem>
...
</rankList>
属性。
查看:
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>
。例如,也许您可以以某种方式使用contentEditable="false"
来更好地进行选择处理。 toWidget
和rankNotes
必须转换为rankTitle
元素(也许使用contentEditable="true"
)。