问题
I'am trying to implement a plugin that translates plaintext placeholders of type __firstName__
into a model like <placeholder key="__firstName__"></placeholder>
and vice versa.
I followed the tutorial on implementing an inline widget which really helped to get started.
I got the upcast
part working which parses the text, splits it up and creates text- and placeholder-elements. So:
<p>Hi __firstName__,</p>
becomes:
<$root>
<paragraph>
"Hi "
<placeholder key="__firstName__">
</placeholder>
","
</paragraph>
</$root>
I'am now struggling to get the dataDowncast
part working. I made these changes regarding only the dataDowncast
, returning a text instead of an element with a text inside:
conversion.for('dataDowncast').elementToElement({
model: 'placeholder',
view: (modelItem, viewWriter) => {
var key = modelItem.getAttribute('key');
return viewWriter.createText(key);
}
});
I'm now facing two problems.
1.: The dataDowncast
results in this:
<p>Hi _,_firstName__</p>
Everything following the placeholder seems to be shifted to the left or in other words seems to ignore the placeholder-length (-1) completely. Is the elementToElement
even meant to except a text instead of an element? Or do i simply have to inform the writer about the length of the text somehow?
2.: If there are more than two placeholders one after another in the model:
<$root>
<paragraph>
<placeholder key="__firstName__">
</placeholder>
<placeholder key="__lastName__">
</placeholder>
<placeholder key="__salutation__">
</placeholder>
</paragraph>
</$root>
the dataDowncast
raises this error:
Uncaught TypeError: Cannot read property 'name' of undefined
at Mapper.getModelLength (mapper.js:428)
at Mapper._findPositionIn (mapper.js:493)
at Mapper.on (mapper.js:94)
at Mapper.fire (emittermixin.js:211)
at Mapper.toViewPosition (mapper.js:277)
at DowncastDispatcher.modelViewSplitOnInsert (converters.js:214)
at DowncastDispatcher.fire (emittermixin.js:211)
at DowncastDispatcher._testAndFire (downcastdispatcher.js:473)
at DowncastDispatcher.convertInsert (downcastdispatcher.js:184)
at DataController.toView (datacontroller.js:207)
This could be a consequential error.
Dependencies uses:
"@ckeditor/ckeditor5-editor-inline": "12.0.0",
"@ckeditor/ckeditor5-widget": "11.0.0",
No plugins active.
回答1:
Is the
elementToElement()
even meant to except a text instead of an element?
All 3 elementToElement()
helpers (two-way, upcast and downcast) are expecting that on both ends (in the model and in the view) you have elements. They convert one element to another.
In general, there are 4 basic types of converters:
- between model and view elements,
- between a model attribute and a view element (must be AttributeElement then),
- between model and view attributes,
- from model markers to a view highlight or element.
It's possible to write custom converters for bigger chunks of view structures (when a bigger chunk of view is represented by a single thing in the model) but this applies to non-editable part of the view (e.g. the body of a widget). In the editable part of the content, everything in the view must map well to something in the model because every position in the view must map to some position in the model. And vice versa.
In your case, the problem is that when you converted a model element to a view text, that model element is not mapped to anything in the view. That breaks a series of things – starting from position mapping to some other pieces of code which will expect that they can map elements too.
Theoretically, it might be possible to override how positions and elements map here. CKEditor 5's Mapper offers some mechanisms for that (check out the events), but I would not go this way. It requires really deep knowledge and tests (even I'm unsure how much work it requires 😅).
So, do follow the placeholder guide where a model element is converted to a view element and vice versa.
来源:https://stackoverflow.com/questions/55247198/how-to-implement-a-placeholder-plugin-that-works-with-plain-text-placeholders