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'
            //  }
            // } );
        }
    }