简体   繁体   English

无法输入到CKEditor5的EditableElement

[英]Can not input to EditableElement of CKEditor5

In CKEditor5, I tried implementing custom element to convert model to view for editing. 在CKEditor5中,我尝试实现自定义元素以将模型转换为视图以进行编辑。 Then, editable element(@ckeditor/ckeditor5-engine/src/view/editableelement) in container element(@ckeditor/ckeditor5-engine/src/view/containerelement) is focused on the parent container element and can not be edited. 然后,容器元素(@ ckeditor / ckeditor5-engine / src / view / containerelement)中的可编辑元素(@ ckeditor / ckeditor5-engine / src / view / editableelement)集中在父容器元素上,无法进行编辑。

For example, if it is implemented as follows: 例如,如果实现如下:

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

The result of actual editing dom after inserting 'myElement' and keydown "abc". 插入“ myElement”和按下“ abc”后实际编辑dom的结果。 (I hope inputting text of "abc" to h4 tag but...) (我希望将“ abc”的文本输入到h4标签中,但是...)

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

I also tried using widget for applying contenteditable attribute. 我也尝试使用小部件来应用contenteditable属性。 But, text couldn't be entered in h4. 但是,无法在h4中输入文本。

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

Is this bug, or I made mistake understanding of container element? 是此错误,还是我对容器元素的理解有误?

[Additional details] [额外细节]

I am assuming to make a widget plugin for ranking list. 我假设要制作一个用于排名列表的小部件插件。

First, the ranking list is structured by <ol> and <li> tags because of having multiple items. 首先,由于具有多个项目,因此排名列表由<ol><li>标记构成。 I solved that by defining two schema such as "rankingList" and "rankingListItem", so I realized dynamic elements using nested model elements. 我通过定义两个模式(例如“ rankingList”和“ rankingListItem”)解决了这一问题,因此我使用嵌套的模型元素实现了动态元素。

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

Next, the item of ranking list has link, image, title and note. 接下来,排名列表的项目具有链接,图像,标题和注释。 Therefore, the ranking list item has the following DOM structure: 因此,排名列表项具有以下DOM结构:

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

I expect the view element is the following: 我希望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 ...

In conclusion, I want to use editable view not to break defined DOM tree. 总之,我想使用可编辑视图不破坏定义的DOM树。 How can I realize it? 我怎么知道呢?

Thank you for describing your case. 感谢您描述您的情况。 It means a lot for us at the moment to know how developers are using the editor and what are your expectations. 目前,对于我们而言,了解开发人员如何使用该编辑器以及您的期望是非常重要的。

Unfortunately, this looks like a very complex feature. 不幸的是,这看起来像一个非常复杂的功能。 It also looks like it would need custom UI (to edit link url and image src -- unless they do not change after added to the editor). 看起来还需要自定义UI(以编辑链接url和image src-除非添加到编辑器后它们没有更改)。 It seems that you struggle with two problems: 您似乎遇到了两个问题:

  • position mapping between the model and the view, 模型和视图之间的位置映射,
  • nested editables. 嵌套的可编辑内容。

First, to answer your question about EditableElement - it seems to be correct to use them for h3 and p elements. 首先,回答有关EditableElement的问题-将它们用于h3p元素似乎是正确的。

However, such complex feature needs custom converters. 但是,这种复杂功能需要自定义转换器。 Converter builders (which you used) are dedicated to being used in simple cases, like element-to-element conversion, or attribute-to-attribute conversion. 转换器构建器(您使用过的)专用于简单情况下,例如元素到元素的转换或属性到属性的转换。

Behind the nice API, build converter is a function factory, that creates one or multiple functions. 在漂亮的API后面,构建转换器是一个函数工厂,它创建一个或多个函数。 Those are then added as callbacks to ModelConversionDispatcher (which editor.editing.modelToView is an instance of). 那些然后加入作为回调来ModelConversionDispatcher (其editor.editing.modelToView是一个实例)。 ModelConversionDispatcher fires a series of events during the conversion, which is a process of translating a change in the model to the view. ModelConversionDispatcher在转换期间触发一系列事件,这是将模型中的更改转换为视图的过程。

As I've mentioned, you would have to write those converting functions by yourself. 如前所述,您将必须自己编写这些转换函数。

Since this is too big of a subject for a detailed and thorough answer, I'll just briefly present you what you should be interested in. Unfortunately, there are no guides yet about creating custom converters from scratch. 由于没有太多主题可以提供详细且详尽的答案,所以我仅向您简要介绍您应该感兴趣的内容。遗憾的是,目前还没有有关从头开始创建自定义转换器的指南。 This is a very broad subject. 这是一个非常广泛的主题。

First, let me explain you from where most of your problems come from. 首先,让我从大多数问题的根源向您解释。 As you already know, the editor has three layers: model (data), view (DOM-like structure) and DOM. 如您所知,编辑器具有三层:模型(数据),视图(类似于DOM的结构)和DOM。 Model is converted to view and view is rendered to DOM. 将模型转换为视图,然后将视图呈现为DOM。 Also, the other way, when you load data, DOM is converted to view and view is converted to model. 同样,另一种方式是,当您加载数据时,DOM被转换为视图,而视图被转换为模型。 This is why you need to provide model-to-view converter and view-to-model converter for your feature. 这就是为什么您需要为功能提供模型到视图转换器和视图到模型转换器的原因。

The important piece of this puzzle is engine.conversion.Mapper . 这个难题的重要部分是engine.conversion.Mapper Its role is to map elements and positions from model to view. 它的作用是从模型到视图映射元素和位置。 As you already might have seen, the model might be quite different than the view. 您可能已经看到,模型可能与视图完全不同。 Correct position mapping between those is key. 这些之间正确的位置映射是关键。 When you type a letter at caret position (in DOM), this position is mapped to model and the letter is inserted in the model and only then converted back to the view and DOM. 当您在插入符号位置(在DOM中)键入字母时,该位置将映射到模型,并且该字母会插入模型中,然后才转换回视图和DOM。 If view-to-model position conversion is wrong, you will not be able to type, or really do anything, at that place. 如果视图到模型的位置转换错误,则您将无法在该位置键入或做任何事情。

Mapper is pretty simple on its own. Mapper本身非常简单。 All it needs is that you specify which view elements are bound to which model elements. 它所需要的只是指定哪些视图元素绑定到哪些模型元素。 For example, in the model you might have: 例如,在模型中,您可能具有:

<listItem type="bulleted" indent="0">Foo</listItem>
<listItem type="bulleted" indent="1">Bar</listItem>

While in the view you have: 在视图中时,您有:

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

If the mapper knows that first listItem is bound with first <li> and the second listItem is bound with second <li> , then it is able to translate positions correctly. 如果映射器知道第一个listItem与第一个<li>绑定,第二个listItem与第二个<li>绑定,则它能够正确转换位置。

Back to your case. 回到你的情况。 Each converter has to provide data for Mapper . 每个转换器必须为Mapper提供数据。 Since you used converter builder, the converters build by it already do this. 由于您使用的是转换器构建器,因此由它生成的转换器已经做到了。 But they are simple, so when you provide: 但是它们很简单,因此当您提供以下信息时:

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

it is assumed, that myElement is bound with the <div> . 假定myElement<div>绑定。 So anything that is written inside that <div> will go straight to myElement and then will be rendered at the beginning of myElement : 因此,在<div>写入的所有内容将直接进入myElement ,然后在myElement的开头myElement

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

Assuming that you just wrote x at <h4> , that position will be mapped to myElement offset 0 in the model and then rendered to view at the beginning of <div> . 假设您只是在<h4>处写了x ,该位置将被映射到模型中的myElement偏移量0 ,然后在<div>的开头呈现给视图。

Model: 模型:

<myElement>x</myElement>

View: 视图:

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

As you can see, in your case, it is <h4> which should be bound with myElement . 如您所见,您的情况是<h4>应该与myElement绑定。

At the moment, we are during refactoring phase. 目前,我们处于重构阶段。 One of the goals is providing more utility functions for converter builders. 目标之一是为转换器制造商提供更多实用程序功能。 One of those utilities are converters for elements which have a wrapper element, like in that "div + h4" case above. 这些实用程序之一是具有包装器元素的元素的转换器,例如上述“ div + h4”情况。 This is also a case of image feature. 这也是图像特征的情况。 The image is represented by <image> in model but it is <figure><img /></figure> in the view. 该图像在模型中由<image>表示,但在视图中为<figure><img /></figure> You can look at ckeditor5-image to see how those converters look like now. 您可以查看ckeditor5-image来查看这些转换器的外观。 We want to simplify them. 我们要简化它们。

Unfortunately, your real case is even more complicated because you have multiple elements inside. 不幸的是,您的实际情况更加复杂,因为内部有多个元素。 CKE5 architecture should be able handle your case but you have to understand that this is almost impossible to write without proper guides. CKE5体系结构应该能够处理您的情况,但是您必须了解,如果没有适当的指导,这几乎是不可能写的。

If you want to tinker though, you should study ckeditor5-image repo. 如果您想ckeditor5-image ,则应该研究 ckeditor5-image库。 It won't be easy, but this is the best way to go. 这并不容易,但这是最好的方法。 Image plugin together with ImageCaption are very similar to your case. Image插件以及ImageCaption与您的情况非常相似。

Model: 模型:

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

View: 视图:

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

While in your case, I'd see the model somewhere between those lines: 在您的情况下,我会在两行之间看到模型:

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

And I'd make the view a bit heavier but it will be easier to write converters: 而且我会让视图更重一点,但是编写转换器会更容易:

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

For rankTitle and rankNotes - base them on caption element ( ckeditor5-image/src/imagecaption/imagecaptionengine.js ). 对于rankTitlerankNotes基于caption元素( ckeditor5-image/src/imagecaption/imagecaptionengine.js )。

For rankItem - base it on image element ( ckeditor5-image/src/image/ ). 对于rankItem基于image元素( ckeditor5-image/src/image/ )。

Once again - keep in mind that we are in the process of simplifying all of this. 再次提醒您-我们正在简化所有步骤。 We want people to write their own features, even those complicated ones like yours. 我们希望人们编写自己的功能,甚至像您这样的复杂功能。 However, we are aware of how complex it is right now. 但是,我们知道它现在有多么复杂。 That's why there are no docs at the moment - we are looking to change and simplify things. 因此,目前没有文档-我们正在寻求更改和简化操作。

And lastly - you could create that ranking list simpler, using Link plugin and elements build with converter builder: 最后,您可以使用Link插件和使用Converter Builder构建的元素来更简单地创建该排名列表:

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

However, it will be possible to mess it up because the whole structure will be editable. 但是,由于整个结构都是可编辑的,因此有可能将其弄乱。

Model: 模型:

<rankList>
  <rankItem>
    <rankImage linkHref="" />
    <rankTitle>Foo</rankTitle>
    <rankNotes>Bar</rankNotes>
  </rankItem>
  ...
</rankList>

Where "Foo" and "Bar" also have linkHref attribute set. 其中“ Foo”和“ Bar”也设置了linkHref属性。

View: 视图:

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

Something like this, while far from perfect, should be much easier to write as long as we are before the refactor and before writing guides. 这样的事情虽然远非完美,但只要我们在重构之前和编写指南之前就应该容易编写得多。

Of course you will also have to provide view-to-model converters. 当然,您还必须提供视图模型转换器。 You might want to write them on your own (take a look at ckeditor5-list/src/converters.js viewModelConverter() -- although yours will be easier because it will be flat, not nested list). 您可能想要自己编写它们(请查看ckeditor5-list/src/converters.js viewModelConverter() -尽管您会更轻松,因为它是平坦的,而不是嵌套列表)。 Or you can generate them through converter builder. 或者,您可以通过转换器生成器生成它们。

Maybe it will be possible to use the approach above (simpler) but use contentEditable attribute to control the structure. 也许可以使用上面的方法(更简单),但是使用contentEditable属性来控制结构。 rankList would have to be converted to <ol> with contentEditable="false" . rankList必须使用contentEditable="false"转换为<ol> Maybe you could somehow use toWidget for better selection handling, for example. 例如,也许您可​​以以某种方式使用toWidget进行更好的选择处理。 rankNotes and rankTitle would have to be converted to element with contentEditable="true" (maybe use toWidgetEditable() ). rankNotesrankTitle必须转换为具有contentEditable="true"元素(可能使用toWidgetEditable() )。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM