在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>
是这个错误,还是我对容器元素的理解有误?
[Additional details]
我假设为排名列表制作一个小部件插件 .
首先,由于具有多个项目,排名列表由 <ol>
和 <li>
标签构成 . 我通过定义两个模式(如"rankingList"和"rankingListItem")解决了这个问题,因此我使用嵌套模型元素实现了动态元素 .
const item1 = new ModelElement('rankingListItem');
const item2 = new ModelElement('rankingListItem');
const model = new ModelElement('rankingList', {}, [item1, item2]);
// and insert
接下来,排名列表的项目具有链接,图像, Headers 和注释 . 因此,排名列表项具有以下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树 . 我怎么能意识到这一点?
1 回答
感谢您描述您的案例 . 对我们来说,了解开发人员如何使用编辑器以及您的期望是什么意味着很多 .
不幸的是,这看起来像一个非常复杂的功能 . 它看起来也需要自定义UI(编辑链接url和图像src - 除非它们在添加到编辑器后不会更改) . 你似乎在努力解决两个问题:
模型和视图之间的位置映射,
嵌套可编辑 .
首先,回答你关于
EditableElement
的问题 - 将它们用于h3
和p
元素似乎是正确的 .但是,这种复杂的功能需要定制转换器 . 转换器构建器(您使用过的)专用于在简单情况下使用,例如元素到元素转换或属性到属性转换 .
在漂亮的API背后,构建转换器是一个函数工厂,它创建一个或多个函数 . 然后将它们作为回调添加到
ModelConversionDispatcher
(editor.editing.modelToView
是其实例) .ModelConversionDispatcher
在转换期间触发一系列事件,这是将模型中的更改转换为视图的过程 .正如我所提到的,你必须自己编写那些转换函数 .
由于这对于详细而彻底的答案来说太大了,我只是简单介绍一下你应该感兴趣的内容 . 不幸的是,还没有关于从头开始创建自定义转换器的指南 . 这是一个非常广泛的主题 .
首先,让我向您解释大多数问题的来源 . 如您所知,编辑器有三个层:模型(数据),视图(类似DOM的结构)和DOM . 模型转换为视图,视图呈现给DOM . 另外,另一方面,当您加载数据时,DOM将转换为视图并将视图转换为模型 . 这就是您需要为您的功能提供模型到视图转换器和视图到模型转换器的原因 .
这个难题的重要部分是
engine.conversion.Mapper
. 它的作用是从模型到视图映射元素和位置 . 正如您可能已经看到的那样,该模型可能与视图完全不同 . 正确的位置映射是关键 . 当您在插入位置(在DOM中)键入字母时,此位置将映射到模型,并且字母将插入模型中,然后仅转换回视图和DOM . 如果视图到模型的位置转换错误,您将无法在该位置键入或实际执行任何操作 .Mapper
本身很简单 . 它所需要的只是指定哪些视图元素绑定到哪些模型元素 . 例如,在模型中您可能具有:在视图中你有:
如果映射器知道第一个
listItem
与第一个<li>
绑定,第二个listItem
与第二个<li>
绑定,则它能够正确转换位置 .回到你的情况 . 每个转换器都必须为
Mapper
提供数据 . 由于您使用了转换器构建器,因此构建的转换器已经执行此操作 . 但它们很简单,所以当你提供:假设
myElement
与<div>
绑定 . 所以写在<div>
里面的任何内容都将直接发送到myElement
然后将在myElement
的开头呈现:假设您刚刚在
<h4>
写了x
,那么该位置将映射到模型中的myElement
offset0
,然后渲染到<div>
开头的视图 .模型:
视图:
正如您所看到的,在您的情况下,它应该是
<h4>
,它应该与myElement
绑定 .目前,我们处于重构阶段 . 其中一个目标是提供更多实用功能对于转换器制造商 . 其中一个实用程序是具有包装元素的元素的转换器,如上面的"div + h4"情况 . 这也是图像特征的一种情况 . 图像在模型中由
<image>
表示,但在视图中为<figure><img /></figure>
. 您可以查看ckeditor5-image
以了解这些转换器现在的样子 . 我们想简化它们 .不幸的是,你的真实情况更复杂,因为你里面有多个元素 . CKE5架构应该能够处理你的情况,但你必须明白,如果没有适当的指南,这几乎是不可能的 .
如果你想修补一下,你应该 study
ckeditor5-image
repo . 这并不容易,但这是最好的方式 .Image
插件和ImageCaption
非常类似于你的情况 .模型:
视图:
在你的情况下,我会在这些线之间看到模型:
而且我会使视图更重一些,但编写转换器会更容易:
对于
rankTitle
和rankNotes
- 将它们基于caption
元素(ckeditor5-image/src/imagecaption/imagecaptionengine.js
) .对于
rankItem
- 基于image
元素(ckeditor5-image/src/image/
) .再一次 - 请记住,我们正在简化所有这一切 . 我们希望人们编写自己的功能,甚至是像你这样复杂的功能 . 但是,我们知道它现在有多复杂 . 这就是为什么目前没有文档 - 我们希望改变和简化事情 .
最后 - 您可以使用
Link
插件创建排名列表更简单,并使用转换器构建器构建元素:rankList
- ><ol class="rank">
,rankItem
- ><li>
,rankImage
- ><img />
,rankNotes
- ><span class="notes">
,rankTitle
- ><span class="title">
.但是,它可能会搞砸,因为整个结构将是可编辑的 .
模型:
其中"Foo"和"Bar"也设置了
linkHref
属性 .视图:
只要我们在重构之前和编写指南之前,这样的事情,虽然远非完美,但应该更容易编写 .
当然,您还必须提供视图到模型转换器 . 您可能想要自己编写它们(看看
ckeditor5-list/src/converters.js
viewModelConverter()
- 虽然它会更容易,因为它将是扁平的,而不是嵌套列表) . 或者您可以通过转换器构建器生成它们 .Maybe 可以使用上面的方法(更简单)但使用
contentEditable
属性来控制结构 .rankList
必须使用contentEditable="false"
转换为<ol>
. 例如,也许您可以以某种方式使用toWidget
来进行更好的选择处理 .rankNotes
和rankTitle
必须使用contentEditable="true"
(也许使用toWidgetEditable()
)转换为元素 .