A closer look at the Rich Text Editor of #orclAPEX 20.2

Oracle APEX version 20.2 comes bundled with a new Rich Text Editor widget. It is based on the CKEditor5 JavaScript library, whereas before it was leveraging CKEditor4. This is a bigger deal than it seems, as version 5 is a complete rewrite of the library and is API-incompatible with previous versions.


Part 1: An Overview


What does this move mean for existing apps?

If you’ve upgraded your APEX instance to 20.2, you should not see any differences right away in your existing apps, as CKEditor4 is still bundled in APEX 20.2. You will however see a deprecation notice in the new item attribute called Version. You can switch to v5 yourself by changing this attribute. Note that you won’t be able to switch back.

For newly created items, you won’t see this Version attribute at all, as the Rich Text Editor will by default be based on CKEditor5. There is no way to create new items based on CKEditor4 in APEX 20.2.

The APEX team chose to bundle both versions for 1 APEX iteration so no apps would break in case of an automatic upgrade – say on an Oracle-maintained APEX instance on the Oracle Cloud. This way, developers still have some time to manually update the version themselves, check for, and fix any possible issues that may arise from this move.

What happens if I don’t upgrade to CKEditor5?

In APEX 20.2, your Rich Text Editor items will continue to work as before. Come the APEX 21.1 upgrade however, if they haven’t been manually updated, they will be updated automatically.

This means that if you’ve written any custom JavaScript code to alter the widget’s appearance or behavior, you will probably start seeing JavaScript errors pop up, possibly making your page unusable.

It is a good idea to update all instances manually now, to spare yourself a possible headache later.

Why the move to CKEditor5?

CKEditor4 is a wildly popular, stable, and feature-rich WYSIWYG editor. It is however rather outdated – in terms of looks, architecture, and security mechanisms.

The team behind CKEditor decided that in order to keep up with the competition they needed to completely rebuild the editor – using ES6, a new data modal, a new design, a more modern API, better accessibility, a stricter approach to security, and more. Even though they have guaranteed support for CKEditor4 until 2023, the general recommendation for everyone is to start moving to version 5.

You can read more about the differences between v4 and v5 here.

How exactly is the APEX Rich Text Editor item now different?

Some of the differences include, but are not limited to:

1) The look

The new editor features a more modern, flat, simple design. Definitely a welcome enhancement!

2) Interactive Grid Compatible

The editor no longer needs an iFrame element to hold its content – which means it can be moved freely around the DOM – which means it can finally be used as an Interactive Grid column type!

Report View:

Single Row View:

3) Markdown Mode

By default, the Rich Text Editor takes HTML as input and likewise outputs HTML.
CKEditor5 however also comes with a Markdown plug-in. This means we can feed the editor Markdown content, edit it in a WYSIWYG way, and the final item value will magically be Markdown again.

I doubt this feature will be widely used, but it is awesome nonetheless.

Note #1: If you do go the Markdown route, you might have to programmatically fiddle with the toolbar items, as not all of them would produce valid Markdown. For example, the Underline action has no Markdown counterpart and would simply produce <u>u tags</u>.

Note #2: APEX 20.2 comes bundled with CKEditor5 v21.1.0. Since this version was released, a few updates have come out which greatly improve the Markdown experience. I would expect APEX 21.1 to benefit from the new enhancements.

4) A Simplified Toolbar

You will notice the updated page item lacks some of the old attributes. The toolbar can no longer be positioned at the bottom of the editor, and can no longer be collapsed. Moreover, toolbar items will no longer overflow onto a new line but will be hidden away in an overflow menu. This is actually great for mobile users.

5) Gone is View Source

CKEditor5 does not offer all of the same features as CKEditor4. One missing feature that is often brought up is the ability to view and edit the HTML source directly.

The View/Edit Source mode would go against the new security approach of v5, as well as the new data model, which only understands what it’s programmed to understand, and not any arbitrary HTML. For example, if the editor’s input contains some element or attribute that none of the editor’s plug-ins knows how to interpret, it will get ignored. This is a big difference in how v4 and v5 operate.

For more info, see this Twitter thread and this GitHub issue.

If this functionality is absolutely critical in your apps, you can use the free CKEditor4 plug-in offered by FOS.


Part 2: Customizing the Editor via JavaScript


As mentioned previously, the new widget is API-incompatible with the old one. Some differences include:

The Global Object
In CKEditor4, the global library object was called CKEDITOR, it is now ClassicEditor.

Getting a handle on the widget instance
In CKEditor4, you could execute CKEDITOR.instances.P1_ITEM, you can now do the same via apex.item(‘P1_ITEM’).getEditor()

Plug-ins
There are no CKEditor external plug-ins anymore, and it looks like none can be added dynamically either. To see what plug-ins you have access to, run the following in the browser console on a page containing a Rich Text Editor.

ClassicEditor.builtinPlugins.map(plugin => plugin.pluginName)

JavaScript Initialization Code
Configuring the editor via this attribute has also changed. In short, all customization will have to go through options.editorOptions

function(options){
    // add your changes to: options.editorOptions
    return options;
}

Of course, to know what changes we can actually set, we must consult the API documentation. Let’s have a look at some examples:

Example #1 Customizing the toolbar

Setting the JavaScript Initialization Code attribute to the following:

function(options){
    options.editorOptions.toolbar = [
        'heading', '|',
        'bold', 'italic', 'underline', '|',
        'todoList', 'insertTable'
    ];
    return options;
}

Will result in:

Example #2 Customizing a feature

The default table widget inserts a table, but a rather boring one. We can enable all table controls, doing the following:

function(options){
    options.editorOptions.table = {
        contentToolbar: [
            'tableColumn', 'tableRow', 'mergeTableCells',
            'tableProperties', 'tableCellProperties'
        ]
    };
    return options;
}

Which will display an extra toolbar when working with tables:

Example #3 Creating a custom toolbar button

While probably not a very common requirement, the following example shows how to create a simple toolbar button:

function(options){

    class CustomButton extends ClassicEditor.libraryClasses.Plugin {
        init(){
            const editor = this.editor;
            editor.ui.componentFactory.add('customButton', locale => {
                const view = new ClassicEditor.libraryClasses.ButtonView(locale);
                view.set({
                    // an icon can be provided as SVG
                    label: 'Custom Button',
                    withText: true,
                    tooltip: true
                });
                view.on('execute', () => {
                    alert('cutom button clicked!');
                });
                return view;
            });
        }
    }

    options.editorOptions.extraPlugins.push(CustomButton);
    options.editorOptions.toolbar.push('customButton');

    return options;
}

Resulting in:

When creating custom elements for CKEditor5, such as a toolbar button, you will need to reference some base utility classes, such as Plugin or ButtonView. Unfortunately, not all of these classes are available in the CKEditor5 build that comes bundled in APEX.

In its natural habitat, CKEditor5 is meant to be built and extended in a static NodeJS context, where all of these helper classes can be easily referenced, used, and included in the final build. In an APEX context, we do not have direct access to all of these classes.

Luckily, 2 classes were exposed under ClassicEditor.libraryClasses, exactly Plugin and ButtonView. These should suffice for building simple buttons. I suspect more utility classes will be exposed in future APEX versions, but realistically, this distribution will never be as versatile as a custom build.

Example #4 Interacting with the widget after initialization

To end on a simpler example, this is how you would dynamically toggle the readOnly mode of the editor:

apex.item('P1_EDITOR').getEditor().isReadOnly = true; // or false

You would run this code after initialization, say in a dynamic action.


Part 3: Conclusion


The move to CKEditor5 might seem a bit disruptive, but it had to happen at some point. I for one am very grateful for the new modern design, and the ability to use the Rich Text Editor as an Interactive Grid column.

Regarding the library itself, simply by looking at its GitHub repository and release log, development is moving extremely fast, with new features and plug-ins being introduced at an impressive rate.

I look forward to exploring this Rich Text Editor further in future blog posts.