我将一种新型的模型元素定义为插件;我们将其称为Foo
。模型中的Foo
节点应转换为视图中的section
元素。到现在为止还挺好。我设法通过定义简单的转换规则来做到这一点。我还设法定义了一个新的FooCommand
,用于将所选块转换(重命名)为Foo
。
我试图将那些Foo
模型节点上的属性转换为视图元素上的属性(反之亦然)。假设Foos有一个名为fooClass
的属性,该属性应映射到视图元素的class
属性。
<Foo fooClass="green-foo"> should map to/from <section class="green-foo">
我可以在FooCommand
中成功接收参数,但我似乎无法在命令处理的块上设置它们:
execute(options = {}) {
const document = this.editor.document;
const fooClass = options.fooClass;
document.enqueueChanges(() => {
const batch = options.batch || document.batch();
const blocks = (options.selection || document.selection).getSelectedBlocks();
for (const block of blocks) {
if (!block.is('foo')) {
batch.rename(block, 'foo');
batch.setAttribute(block, 'fooClass', fooClass);
}
}
});
}
以下是init
插件中Foo
函数的代码,包括模型→视图和视图→模型转换:
init() {
const editor = this.editor;
const doc = editor.document;
const data = editor.data;
const editing = editor.editing;
editor.commands.add('foo', new FooCommand(editor));
doc.schema.registerItem('foo', '$block');
buildModelConverter().for(data.modelToView, editing.modelToView)
.fromElement('foo')
.toElement(modelElement => {
const fooClass = modelElement.item.getAttribute('fooClass'));
return new ContainerElement('section', {'class': fooClass});
});
buildViewConverter().for(data.viewToModel)
.fromElement('section')
.toElement(viewElement => {
let classes = Array.from(viewElement.getClassNames());
let modelElement = new ModelElement('foo', {'fooClass': classes[0]});
return modelElement;
});
}
当我尝试通过
运行命令时editor.execute('foo', { fooClass: 'green-foo' })
我可以看到green-foo
可以使用FooCommand
值,但另一方面,模型→视图转换中的modelElement
没有fooClass
属性
我确定我错过了这一点并滥用了API。如果有人能够对这个问题有所了解,我真的很感激。我可以根据需要提供更多细节。
感谢@Reinmar和@jodator关于配置文档架构以允许自定义属性的建议。我真的以为会照顾它,但不是。无论如何,它可能是必要的步骤,但在模型→视图转换期间,我仍然无法从模型元素中获取属性值。
首先,让我添加一条我遗漏的重要信息:CKEditor5与我合作的版本是 1.0.0-alpha2 。我知道有几个API必然会改变,但我仍然希望能够使用现有版本。
如果我理解正确,可以将string
或function
传递给toElement
来电。关于使用后者的问题:传递给函数的参数究竟是什么?我假设它将是要转换的模型元素(节点?)。是这样的吗?如果是这样,为什么通过batch.setAttribute
(在document.enqueueChanges
内)在该节点上设置的属性在请求时不可用?应该是吗?
其他测试似乎表明发生了某种执行顺序问题。我观察到,即使我第一次尝试从modelElement
参数读取该属性时该属性不可用,如果我稍后再次阅读它也会如此。让我试着说明下面的情况。首先,我将修改转换代码,使其在读取时属性值不可用时使用一些虚拟值:
buildModelConverter().for(data.modelToView, editing.modelToView)
.fromElement('foo')
.toElement(modelElement => {
let fooClass = modelElement.item.getAttribute('fooClass') || 'naught';
let viewElement = new ContainerElement('section');
viewElement.setAttribute('class', fooClass);
return viewElement;
});
现在我重新加载页面并在控制台上执行以下说明:
c = Array.from(editor.document.getRoot().getChildren());
c[1].is('paragraph'); // true
// Changing the node from 'paragraph' to 'foo' and adding an attribute
// 'fooClass' with value 'green-foo' to it.
editor.document.enqueueChanges(() => {
const batch = editor.document.batch();
batch.rename(c[1], 'foo');
batch.setAttribute(c[1], 'fooClass', 'green-foo');
return batch;
});
c[1].is('paragraph'); // false
c[1].is('foo'); // true
c[1].hasAttribute('fooClass'); // true
c[1].getAttribute('fooClass'); // 'green-foo'
即使看起来正在生成预期的输出,但只要看一下生成的视图元素就会显示问题:
<section class="naught"/>
最后,即使我尝试重置模型元素上的fooClass
属性,更改也不会反映在视图元素上。这是为什么?不能通过enqueueChanges
进行更改会导致视图更新吗?
很抱歉很长的帖子,但我尽量传达尽可能多的细节。希望有人能够发现我的错误或误解CKEditor 5的API实际工作原理。
我转向Document
's events并尝试了changesDone
事件。它成功地解决了&#34;时间问题。问题,因为只有在处理完所有更改后才会始终触发。尽管如此,仍然存在响应模型变化而未更新视图的问题。为清楚起见,模型确实发生了变化,但视图并没有反映出来。这是电话:
editor.document.enqueueChanges(() => editor.document.batch().setAttribute(c[1], 'fooClass', 'red-foo'));
答案 0 :(得分:1)
我百分百肯定自己写了整个功能。我使用的是1.0.0-beta.1 API,它与你的API完全不同。
基本上 - 它有效。它还不是100%正确,但我会达到目的。
实现需要转换元素+属性的功能时,它需要单独处理元素和属性转换,因为它们由CKEditor 5单独处理。
因此,在下面的代码中,您会发现我使用了elementToElement()
:
editor.conversion.elementToElement( {
model: 'foo',
view: 'section'
} );
所以模型的<foo>
元素和视图的<section>
元素之间的转换器。这是一个双向转换器,因此它处理向上转换(视图 - &gt;模型)和向下转换(模型 - &gt;视图)转换。
注意:它不处理属性。
理论上,作为view
属性,您可以编写一个回调函数来读取模型元素的属性,并创建具有此属性集的视图元素。但这不起作用,因为这种配置只有在向下转换(模型 - &gt;视图)的情况下才有意义。我们怎样才能使用该回调来转发视图结构呢?
注意:您可以单独编写用于向下和向上管道的转换器(使用editor.conversion.for()
),在这种情况下,您可以真正使用回调。但在这种情况下,它确实没有意义。
另一个问题是,假设你写了一个元素转换器,它同时设置了属性。 Tada,您加载<section class=ohmy>
并获取<foo class=ohmy
&gt;在你的模型中。
但是......如果属性会在模型中发生变化怎么办?
在向下转换管道中,CKEditor 5将元素更改与属性更改分开处理。它将它们作为单独的事件发射。因此,当您在FooCommand
标题上执行writer.rename()
时,我们会在DowncastDispatcher
中收到以下事件:
remove
与<heading>
insert:section
但是属性也被改变了(writer.setAttribute()
),所以我们也得到了:
setAttibute:class:section
elementToElement()
转换帮助器侦听insert:section
事件。所以它对setAttribute:class:selection
是盲目的。
因此,当您更改属性的值时,您需要进行attributeToAttribute()
转换。
我们在发布1.0.0-beta.1之前不想回复您的问题,因为1.0.0-beta.1带来了Differ。
在1.0.0-beta.1之前,所有更改在应用时立即转换。因此,rename()
会导致即时的remove
和insert:section
事件。此时,您在后一个元素中获得的元素将不会设置class
属性。
感谢不同,我们能够在应用所有更改后(在执行change()
块之后)启动转换。这意味着,一旦模型insert:section
元素已设置<foo>
属性,就会触发class
事件。这就是为什么你可以写一个基于回调的转换器......你不应该这样做:D
import { downcastAttributeToAttribute } from '@ckeditor/ckeditor5-engine/src/conversion/downcast-converters';
import { upcastAttributeToAttribute } from '@ckeditor/ckeditor5-engine/src/conversion/upcast-converters';
class FooCommand extends Command {
execute( options = {} ) {
const model = this.editor.model;
const fooClass = options.class;
model.change( writer => {
const blocks = model.document.selection.getSelectedBlocks();
for ( const block of blocks ) {
if ( !block.is( 'foo' ) ) {
writer.rename( block, 'foo' );
writer.setAttribute( 'class', fooClass, block );
}
}
} );
}
}
class FooPlugin extends Plugin {
init() {
const editor = this.editor;
editor.commands.add( 'foo', new FooCommand( editor ) );
editor.model.schema.register( 'foo', {
allowAttributes: 'class',
inheritAllFrom: '$block'
} );
editor.conversion.elementToElement( {
model: 'foo',
view: 'section'
} );
editor.conversion.for( 'upcast' ).add(
upcastAttributeToAttribute( {
model: 'class',
view: 'class'
} )
);
editor.conversion.for( 'downcast' ).add(
downcastAttributeToAttribute( {
model: 'class',
view: 'class'
} )
);
// This should work but it does not due to https://github.com/ckeditor/ckeditor5-engine/issues/1379 :(((
// EDIT: The above issue is fixed and will be released in 1.0.0-beta.2.
// editor.conversion.attributeToAttribute( {
// model: {
// name: 'foo',
// key: 'class'
// },
// view: {
// name: 'section',
// key: 'class'
// }
// } );
}
}
这段代码非常有效,除了它在任何可能包含它的元素上转换class
属性。这是因为我必须使用非常通用的downcastAttributeToAttribute()
和upcastAttributeToAttribute()
转换器,因为bug that I found(编辑:它已修复,将在1.0.0-beta.2中提供)。如果一切正常并且它将在1.0.0-beta.2中运行,那么注释掉的代码就是如何定义代码。
很遗憾我们错过了这么简单的案例,但这主要是因为我们所有的功能......都比这复杂得多。