CKEditor5自定义插件会更改模型,但不会更改视图

时间:2019-08-25 11:03:00

标签: ckeditor5

我已经创建了CKEditor5的自定义版本,并创建了插件BorderColor。插件运行不正常。问题在于模型正在更改,但视图未更改。

border-color.js

import Plugin from '@ckeditor/ckeditor5-core/src/plugin';

import BorderColorEditing from './border-color-editing';
import BorderColorUi from './border-color-ui';

export default class BorderColor extends Plugin {
    /**
     * @inheritDoc
     */
    static get requires() {
        return [ BorderColorEditing, BorderColorUi ];
    }

    /**
     * @inheritDoc
     */
    static get pluginName() {
        return 'BorderColor';
    }
}

border-color-ui.js

import ColorUI from '@ckeditor/ckeditor5-font/src/ui/colorui';

export default class BorderColorUi extends ColorUI {
    /**
     * @inheritDoc
     */
    constructor( editor ) {
        const t = editor.locale.t;

        super( editor, {
            commandName: 'borderColor',
            componentName: 'borderColor',
            dropdownLabel: t( 'Rahmenfarbe' )
        } );
    }

    /**
     * @inheritDoc
     */
    static get pluginName() {
        return 'BorderColorUI';
    }
}

border-color-editing.js

import Plugin from '@ckeditor/ckeditor5-core/src/plugin';

import BorderColorCommand from './border-color-command';
import { isDefault, isSupported, supportedOptions } from './utils';

export default class BorderColorEditing extends Plugin {
    /**
     * @inheritDoc
     */
    constructor( editor ) {
        super( editor );

        editor.config.define( 'borderColor', supportedOptions );
    }

    /**
     * @inheritDoc
     */
    init() {
        const editor = this.editor;
        const schema = editor.model.schema;

        // Filter out unsupported options.
        const enabledOptions = editor.config.get( 'borderColor.colors' );

        // Allow alignment attribute on all blocks.
        schema.extend( '$block', { allowAttributes: 'borderColor' } );
        editor.model.schema.setAttributeProperties( 'borderColor', { isFormatting: true } );
        editor.model.schema.addAttributeCheck( ( context, attributeName ) => {
            if ( context.endsWith( 'table' ) || context.endsWith( 'tableRow' ) || context.endsWith( 'tableCell' )) {
                return true;
            }
        } );
        const definition = _buildDefinition( enabledOptions.filter( option => !isDefault( option ) ) );

        editor.conversion.attributeToAttribute( definition );

        editor.commands.add( 'borderColor', new BorderColorCommand( editor ) );
    }
}

// Utility function responsible for building converter definition.
// @private
function _buildDefinition( options ) {
    const definition = {
        model: {
            key: 'borderColor',
            values: options.slice()
        },
        view: {}
    };

    for ( const option of options ) {
        const _def = { key: 'style', value: {} };
        _def.value[ 'border-color' ] = option.color;
        definition.view[ option ] = _def;
    }

    return definition;
}

border-color-command.js

import Command from '@ckeditor/ckeditor5-core/src/command';
import first from '@ckeditor/ckeditor5-utils/src/first';

import { isDefault } from './utils';

const BORDER_COLOR = 'borderColor';

export default class BorderColorCommand extends Command {
    /**
     * @inheritDoc
     */
    refresh() {
        let childBlocks;
        if ( this.editor.model.document.selection.getSelectedElement() != null )
            childBlocks = this.getSelectedBlocks( this.editor.model.document, 'all-tablerow' );
        const firstBlock = this.editor.model.document.selection.getSelectedElement() != null
            ? this.editor.model.document.selection.getSelectedElement()
            : first( this.editor.model.document.selection.getSelectedBlocks() );

        // As first check whether to enable or disable the command as the value will always be false if the command cannot be enabled.
        this.isEnabled = !!firstBlock && this._canBeAligned( firstBlock );

        /**
         * A value of the current block's alignment.
         *
         * @observable
         * @readonly
         * @member {String} #value
         */
        if ( this.isEnabled && firstBlock.hasAttribute( BORDER_COLOR ) )
            this.value = firstBlock.getAttribute( BORDER_COLOR );
        else if ( this.isEnabled && childBlocks && childBlocks[ 0 ].hasAttribute( BORDER_COLOR ) )
            this.value = childBlocks[ 0 ].getAttribute( BORDER_COLOR );
        else
            this.value = '';
    }

    /**
     * Executes the command. Applies the alignment `value` to the selected blocks.
     * If no `value` is passed, the `value` is the default one or it is equal to the currently selected block's alignment attribute,
     * the command will remove the attribute from the selected blocks.
     *
     * @param {Object} [options] Options for the executed command.
     * @param {String} [options.value] The value to apply.
     * @fires execute
     */
    execute( options = {} ) {
        const editor = this.editor;
        const model = editor.model;
        const doc = model.document;

        const value = options.value;

        model.change( writer => {
            let childBlocks;
            if ( this.editor.model.document.selection.getSelectedElement() != null )
                childBlocks = this.getSelectedBlocks( this.editor.model.document, 'all-tablerow' );
            const firstBlock = this.editor.model.document.selection.getSelectedElement() != null
                ? this.editor.model.document.selection.getSelectedElement()
                : first( this.editor.model.document.selection.getSelectedBlocks() );

            let currentAlignment;
            if ( firstBlock.hasAttribute( BORDER_COLOR ) )
                currentAlignment = firstBlock.getAttribute( BORDER_COLOR );
            else if ( childBlocks && childBlocks[ 0 ].hasAttribute( BORDER_COLOR ) )
                currentAlignment = childBlocks[ 0 ].getAttribute( BORDER_COLOR );

            // Remove alignment attribute if current alignment is:
            // - default (should not be stored in model as it will bloat model data)
            // - equal to currently set
            // - or no value is passed - denotes default alignment.
            const removeAlignment = isDefault( value ) || currentAlignment === value || !value;
            console.log( 'childBlocks / firstBlock', childBlocks, firstBlock, currentAlignment);
            const blocks = childBlocks ? Array.from( childBlocks ) : [];
            blocks.push( firstBlock );
            if ( removeAlignment ) {
                removeAlignmentFromSelection( blocks, writer );
            } else {
                setAlignmentOnSelection( blocks, writer, value );
            }
        } );
    }

    getSelectedBlocks( doc, value ) {
        let blocks;
        if ( doc.selection.getSelectedElement() == null )
            blocks = Array.from( doc.selection.getSelectedBlocks() ).filter( block => this._canBeAligned( block ) )
        else {
            blocks = [ doc.selection.getSelectedElement() ];
            if ( value.indexOf( 'tablerow' ) > -1 ) {
                var children = doc.selection.getSelectedElement().getChildren();
                console.log( 'Selected Element Children', children );
                var out = [];
                let _next = children.next();
                while ( !_next.done ) {
                    console.log( _next );
                    if ( _next.value.getChildren ) {
                        const children2 = _next.value.getChildren();
                        let _next2 = children2.next();
                        while ( !_next2.done ) {
                            console.log( _next2 );
                            out[ out.length ] = _next2.value;
                            _next2 = children2.next();
                        }
                    }
                    else {
                        out[ out.length ] = _next.value;
                    }
                    _next = children.next();
                }
                blocks = out;
            }
        }

        return blocks;
    }
    /**
     * Checks whether a block can have alignment set.
     *
     * @private
     * @param {module:engine/model/element~Element} block The block to be checked.
     * @returns {Boolean}
     */
    _canBeAligned( block ) {
        return this.editor.model.schema.checkAttribute( block, BORDER_COLOR );
    }
}

// Removes the alignment attribute from blocks.
// @private
function removeAlignmentFromSelection( blocks, writer ) {
    for ( const block of blocks ) {
        writer.removeAttribute( BORDER_COLOR, block );
    }
}

// Sets the alignment attribute on blocks.
// @private
function setAlignmentOnSelection( blocks, writer, border ) {
    for ( const block of blocks ) {
        writer.setAttribute( BORDER_COLOR, border, block );
    }
}

该代码也可以在Github上找到:CKEditor Custom Build Github

1 个答案:

答案 0 :(得分:0)

我找到了解决方案。

我编辑了border-color-editing.js: 我替换了以下代码:

editor.conversion.attributeToAttribute( definition );

具有:

editor.conversion.for( 'downcast' ).attributeToAttribute( {
            model: 'borderColor',
            view: function( option ) {
                const allSelectedBlocks = getAllSelectedBlocks( this.editor );
                const borderAttribute = allSelectedBlocks[ 0 ].getAttribute( BORDER );
                if ( !borderAttribute || !option )
                    return { key: 'style', value: '' };

                const _value = {};
                const styleOption = borderAttribute.replace( '-tablerow', '' );
                _value[ 'border' + ( styleOption == 'all' ? '' : '-' + styleOption ) + '-color' ] = option ? option : 'black';

                return { key: 'style', value: _value };
            }.bind( this )
        } );