Contribute to this guide

guide向上转换 - 视图到模型

# 简介

视图转换为模型的过程称为向上转换

Basic upcast conversion diagram.

向上转换过程每当任何数据被加载到编辑器中时都会发生。

传入数据成为视图,然后通过注册的转换器转换为模型。

如果你只想快速启用 CKEditor 5 功能明确不支持的一些常见 HTML 标签,可以使用通用 HTML 支持功能,而不是编写你自己的自定义向上转换器。

# 注册转换器

要指示引擎如何将特定视图元素转换为模型元素,你需要使用editor.conversion.for( 'upcast' ) 方法注册一个向上转换器

editor.conversion
    .for( 'upcast' )
    .elementToElement( {
        view: 'p',
        model: 'paragraph'
    } );

上面的转换器将处理所有<p> 视图元素转换为<paragraph> 模型元素的转换。

你刚刚了解了elementToElement() 向上转换转换助手方法!更多助手将在后面的章节中介绍。

# 向上转换管道

与向下转换不同的是,你只需要处理数据管道和编辑管道,向上转换过程只发生在数据管道中,被称为数据向上转换

编辑视图只能通过首先更改模型来更改,因此编辑管道只需要向下转换过程。

Upcast conversion pipeline diagram.

前面的代码示例为两个管道同时注册了一个转换器。这意味着<paragraph> 模型元素将同时在数据视图编辑视图中转换为<p> 视图元素。

# 转换为文本属性

表示内联文本格式的视图元素(例如<strong><i>)需要转换为模型文本节点上的属性。

要注册此类转换器,请使用elementToAttribute() 方法

editor.conversion
    .for( 'upcast' )
    .elementToAttribute( {
        view: 'strong',
        model: 'bold'
    } );

<strong> 标签包装的文本将被转换为一个模型文本节点,并应用一个bold 属性,如所示

加粗文本

如果你需要将属性从视图元素“复制”到模型元素,请使用attributeToAttribute() 方法

模型元素必须有自己的转换器注册,否则属性将无法复制到任何地方。

editor.conversion
    .for( 'upcast' )
    .attributeToAttribute( {
        view: 'src',
        model: 'source'
    } );

假设编辑器中的一些其他功能注册了<img><image> 模型元素的向上转换器,你可以扩展此功能以允许src 属性。此属性将被转换为模型元素上的source 属性,如以下代码段所示

这只是一个例子。实际上,图像元素和源属性的支持由图像功能提供,因此你无需自己编写<image source="xxx"><img src="xxx"> 元素转换。

# 转换为元素

将视图元素转换为相应的模型元素可以通过使用elementToElement() 方法注册转换器来实现

editor.conversion
    .for( 'upcast' )
    .elementToElement( {
        view: {
            name: 'div',
            classes: [ 'example' ]
        },
        model: 'example'
    } );

此转换器将处理所有<div class="example"> 视图元素转换为<example> 模型元素的转换。

使用你自己的自定义模型元素需要先在模式中定义它。

# 转换结构

正如你在上一章中所学,单个模型元素可以向下转换为多个视图元素的结构。

相反的过程将不得不检测该结构(例如,主元素)并将其转换为一个简单的模型元素。

对于向上转换,没有structureToElement() 助手可用。要为整个结构注册一个向上转换器并创建一个单个模型元素,你必须使用基于事件的 API。以下示例展示了如何实现它

editor.conversion.for( 'upcast' ).add( dispatcher => {
    // Look for every view <div> element.
    dispatcher.on( 'element:div', ( evt, data, conversionApi ) => {
        // Get all the necessary items from the conversion API object.
        const {
            consumable,
            writer,
            safeInsert,
            convertChildren,
            updateConversionResult
        } = conversionApi;

        // Get view item from data object.
        const { viewItem } = data;

        // Define elements consumables.
        const wrapper = { name: true, classes: 'wrapper' };
        const innerWrapper = { name: true, classes: 'inner-wrapper' };

        // Tests if the view element can be consumed.
        if ( !consumable.test( viewItem, wrapper ) ) {
            return;
        }

        // Check if there is only one child.
        if ( viewItem.childCount !== 1 ) {
            return;
        }

        // Get the first child element.
        const firstChildItem = viewItem.getChild( 0 );

        // Check if the first element is a <div>.
        if ( !firstChildItem.is( 'element', 'div' ) ) {
            return;
        }

        // Tests if the first child element can be consumed.
        if ( !consumable.test( firstChildItem, innerWrapper ) ) {
            return;
        }

        // Create model element.
        const modelElement = writer.createElement( 'myElement' );

        // Insert element on a current cursor location.
        if ( !safeInsert( modelElement, data.modelCursor ) ) {
            return;
        }

        // Consume the main outer wrapper element.
        consumable.consume( viewItem, wrapper );
        // Consume the inner wrapper element.
        consumable.consume( firstChildItem, innerWrapper );

        // Handle children conversion inside inner wrapper element.
        convertChildren( firstChildItem, modelElement );

        // Necessary function call to help setting model range and cursor
        // for some specific cases when elements being split.
        updateConversionResult( modelElement, data );
    } );
} );

此转换器将检测所有<div class="wrapper"><div class="inner-wrapper"><p>...</p></div></div> 结构(通过扫描外部<div> 并将其转换为单个<myElement> 模型元素)。效果应返回如下

示例结构

使用你自己的自定义模型元素需要先在模式中定义它。

# 进一步阅读

如果你想了解本指南中提到的向上转换助手的更多信息,我们已经为你总结了它们,包括完整的描述和示例。我们还建议你查看向下转换指南,了解如何将编辑器模型状态转换为数据输出和编辑视图。