Ckeditor 将参数从命令传递到转换器
我定义了一种新类型的模型元素作为插件;让我们把它称为Ckeditor 将参数从命令传递到转换器,ckeditor,ckeditor5,Ckeditor,Ckeditor5,我定义了一种新类型的模型元素作为插件;让我们把它称为Foo。模型中的Foo节点应转换为视图中的节元素。到目前为止,一切顺利。我通过定义简单的转换规则来做到这一点。我还设法定义了一个新的Foo命令,该命令将选定的块转换(重命名)为Foo 我一直试图将那些Foomodel节点上的属性转换为视图元素上的属性(反之亦然)。假设foo有一个名为fooClass的属性,该属性应该映射到视图元素的class属性 <Foo fooClass="green-foo"> should map to/fr
Foo
。模型中的Foo
节点应转换为视图中的节
元素。到目前为止,一切顺利。我通过定义简单的转换规则来做到这一点。我还设法定义了一个新的Foo命令
,该命令将选定的块转换(重命名)为Foo
我一直试图将那些Foo
model节点上的属性转换为视图元素上的属性(反之亦然)。假设foo有一个名为fooClass
的属性,该属性应该映射到视图元素的class
属性
<Foo fooClass="green-foo"> should map to/from <section class="green-foo">
下面是Foo
插件中init
函数的代码,包括模型→视图和视图→模型转换:
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' })
我可以看到绿色foo
值可用于FooCommand
,但是模型中的modelement
是可用的→另一方面,视图转换没有fooClass
属性
<Foo fooClass="green-foo"> should map to/from <section class="green-foo">
我肯定我没有抓住要点,误用了API。如果有人能解释一下这个问题,我真的很感激。我可以根据需要提供更多细节
初步建议后的后续行动
感谢@Reinmar和@jodator就配置文档架构以允许自定义属性提出的建议。我真的认为这会解决它,但不是。无论如何,这可能是一个必要的步骤,但我仍然无法在建模期间从模型元素中获取属性值→视图转换
首先,让我添加一条我遗漏的重要信息:我正在使用的CKEditor5版本是1.0.0-alpha2。我知道一些API肯定会发生变化,但我仍然希望在当前版本中能够正常工作
模型→视图转换
如果我理解正确,可以将字符串
或函数
传递给toElement
调用。关于使用后者的问题:传递给函数的参数到底是什么?我假设它将是要转换的模型元素(节点?)。是这样吗?如果是这样,为什么通过batch.setAttribute
(在文档中.enqueueChanges
)在该节点上设置的属性在请求时不可用?应该是吗
排序问题?
额外的测试似乎表明存在某种执行顺序问题。我已经注意到,即使当我第一次尝试从modelement
参数中读取属性时,该属性不可用,但如果我稍后再次读取它,它将是可用的。让我试着说明下面的情况。首先,我将修改转换代码,使其在读取属性值时不可用时使用一些伪值:
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"/>
百分之百确定的是,整个特写都是我自己写的。我使用的是1.0.0-beta.1API,它与您使用的完全不同 基本上,它是有效的。这还不是100%正确,但我会说的 如何转换元素+属性对? 当实现需要转换元素+属性的特性时,需要单独处理元素和属性转换,因为它们是由CKEditor 5单独处理的 因此,在下面的代码中,您会发现我使用了: 因此,模型的
元素和视图的
元素之间有一个转换器。这是一个双向转换器,因此它处理向上(视图->模型)和向下(模型->视图)转换
注意:它不处理属性
<Foo fooClass="green-foo"> should map to/from <section class="green-foo">
从理论上讲,作为view
属性,您可以编写一个回调,该回调将读取模型元素的属性,并使用该属性集创建视图元素。但这是行不通的,因为这样的配置只有在向下转换(model->view)的情况下才有意义。我们如何使用该回调来向下转换视图结构
注意:您可以分别为下行和上行管道编写转换器(通过使用),在这种情况下,您可以真正使用回调。但在这种情况下,这并没有什么意义
属性可以独立更改!
另一个问题是,假设您编写了一个元素转换器,它同时设置属性。Tada,你加载
并在模型中获取
但是。。。如果该属性将在模型中更改,该怎么办
在下行管道中,CKEditor 5将元素更改与属性更改分开处理。它将它们作为单独的事件激发。因此,当对标题执行FooCommand
时,它将调用writer.rename()
,并在中获得以下事件:
使用
插入:部分
但是属性也发生了变化(writer.setAttribute()
),因此我们还得到:
setAttibute:class:section
elementToElement()
转换帮助程序侦听insert:section
事件。所以它对setAttribute:class:selection
是盲目的
因此,更改属性值时,需要进行attributeToAttribute()
转换
排序
在我们发布1.0.0-beta.1之前,我不想回答你的问题,因为1.0.0-beta.1带来了
在1.0.0-beta.1之前,所有更改在应用时立即转换。因此,rename()
将立即导致remove
和insert:section
事件。此时,在后一个元素中得到的元素还没有设置class
属性
多亏了D
editor.conversion.elementToElement( {
model: 'foo',
view: 'section'
} );
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'
// }
// } );
}
}