ckeditor/ckeditor5-vue

CKEditor5 v 34.1.0 error "editor-isreadonly-has-no-setter" with NuxtJS

Closed this issue · 2 comments

We are working on a site using NuxtJs v2.15.8 and have a nuxt/vue component using CKEditor5. The component was originally developed with CKEditor5 v33 but we just upgraded to v 34.1.0.

When loading the page that contains the component we are now getting the following error:
CKEditorError: editor-isreadonly-has-no-setter

This was a breaking change introduced in v 34: https://ckeditor.com/docs/ckeditor5/latest/updating/migration-to-34.html#changed-mechanism-for-setting-and-clearing-the-editor-read-only-mode

We were not previously setting the .isReadOnly property directly so we are not sure where we need to update our code to resolve the error.

We have reviewed the documentation on CKEditor's site (see links below) but are still unclear how to fix the issue - unfortunately we are new to both NuxtJS and ckeditor so this is unfamiliar to us.

I believe the issue is we are not trying to access ckeditor5 in the right place or in the right way. We have tried adding calls to enableReadOnlyMode to different lifecycle hooks and functions in both CKeditorNuxt.vue and MyParentComponent.vue. For example we used the created, beforeMount and mounted lifecycle hooks on both components. In those hooks ckeditor returns undefined - so either we're not accessing it properly or maybe it doesn't actually exist yet at the points in the lifecycle where we're trying to call it.

Below is ckeditor.js from the generated online build:

     import DecoupledDocumentEditor from '@ckeditor/ckeditor5-editor-decoupled/src/decouplededitor.js';
     import Alignment from '@ckeditor/ckeditor5-alignment/src/alignment.js';
     import Autoformat from '@ckeditor/ckeditor5-autoformat/src/autoformat.js';
     import AutoImage from '@ckeditor/ckeditor5-image/src/autoimage.js';
     import Base64UploadAdapter from '@ckeditor/ckeditor5-upload/src/adapters/base64uploadadapter.js';
     import BlockQuote from '@ckeditor/ckeditor5-block-quote/src/blockquote.js';
     import Bold from '@ckeditor/ckeditor5-basic-styles/src/bold.js';
     import CloudServices from '@ckeditor/ckeditor5-cloud-services/src/cloudservices.js';
     import Essentials from '@ckeditor/ckeditor5-essentials/src/essentials.js';
     import FontBackgroundColor from '@ckeditor/ckeditor5-font/src/fontbackgroundcolor.js';
     import FontColor from '@ckeditor/ckeditor5-font/src/fontcolor.js';
     import FontFamily from '@ckeditor/ckeditor5-font/src/fontfamily.js';
     import FontSize from '@ckeditor/ckeditor5-font/src/fontsize.js';
     import Heading from '@ckeditor/ckeditor5-heading/src/heading.js';
     import Image from '@ckeditor/ckeditor5-image/src/image.js';
     import ImageCaption from '@ckeditor/ckeditor5-image/src/imagecaption.js';
     import ImageInsert from '@ckeditor/ckeditor5-image/src/imageinsert.js';
     import ImageResize from '@ckeditor/ckeditor5-image/src/imageresize.js';
     import ImageStyle from '@ckeditor/ckeditor5-image/src/imagestyle.js';
     import ImageToolbar from '@ckeditor/ckeditor5-image/src/imagetoolbar.js';
     import ImageUpload from '@ckeditor/ckeditor5-image/src/imageupload.js';
     import Indent from '@ckeditor/ckeditor5-indent/src/indent.js';
     import IndentBlock from '@ckeditor/ckeditor5-indent/src/indentblock.js';
     import Italic from '@ckeditor/ckeditor5-basic-styles/src/italic.js';
     import Link from '@ckeditor/ckeditor5-link/src/link.js';
     import LinkImage from '@ckeditor/ckeditor5-link/src/linkimage.js';
     import List from '@ckeditor/ckeditor5-list/src/list.js';
     import ListProperties from '@ckeditor/ckeditor5-list/src/listproperties.js';
     import MediaEmbed from '@ckeditor/ckeditor5-media-embed/src/mediaembed.js';
     import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph.js';
     import PasteFromOffice from '@ckeditor/ckeditor5-paste-from-office/src/pastefromoffice.js';
     import Strikethrough from '@ckeditor/ckeditor5-basic-styles/src/strikethrough.js';
     import Subscript from '@ckeditor/ckeditor5-basic-styles/src/subscript.js';
     import Superscript from '@ckeditor/ckeditor5-basic-styles/src/superscript.js';
     import Table from '@ckeditor/ckeditor5-table/src/table.js';
     import TableCaption from '@ckeditor/ckeditor5-table/src/tablecaption.js';
     import TableCellProperties from '@ckeditor/ckeditor5-table/src/tablecellproperties';
     import TableColumnResize from '@ckeditor/ckeditor5-table/src/tablecolumnresize.js';
     import TableProperties from '@ckeditor/ckeditor5-table/src/tableproperties';
     import TableToolbar from '@ckeditor/ckeditor5-table/src/tabletoolbar.js';
     import TextTransformation from '@ckeditor/ckeditor5-typing/src/texttransformation.js';
     import TodoList from '@ckeditor/ckeditor5-list/src/todolist';
     import Underline from '@ckeditor/ckeditor5-basic-styles/src/underline.js';
     
     class Editor extends DecoupledDocumentEditor {}
     
     // Plugins to include in the build.
     Editor.builtinPlugins = [
         Alignment,
         Autoformat,
         AutoImage,
         Base64UploadAdapter,
         BlockQuote,
         Bold,
         CloudServices,
         Essentials,
         FontBackgroundColor,
         FontColor,
         FontFamily,
         FontSize,
         Heading,
         Image,
         ImageCaption,
         ImageInsert,
         ImageResize,
         ImageStyle,
         ImageToolbar,
         ImageUpload,
         Indent,
         IndentBlock,
         Italic,
         Link,
         LinkImage,
         List,
         ListProperties,
         MediaEmbed,
         Paragraph,
         PasteFromOffice,
         Strikethrough,
         Subscript,
         Superscript,
         Table,
         TableCaption,
         TableCellProperties,
         TableColumnResize,
         TableProperties,
         TableToolbar,
         TextTransformation,
         TodoList,
         Underline
     ];
     
     // Editor configuration.
     Editor.defaultConfig = {
         toolbar: {
             items: [
                 'heading',
                 '|',
                 'fontSize',
                 'fontFamily',
                 '|',
                 'fontColor',
                 'fontBackgroundColor',
                 '|',
                 'bold',
                 'italic',
                 'underline',
                 'strikethrough',
                 'subscript',
                 'superscript',
                 '|',
                 'alignment',
                 '|',
                 'numberedList',
                 'bulletedList',
                 'todoList',
                 '|',
                 'outdent',
                 'indent',
                 '|',
                 'link',
                 'blockQuote',
                 'imageUpload',
                 'imageInsert',
                 'insertTable',
                 'mediaEmbed',
                 '|',
                 'undo',
                 'redo'
             ]
         },
         language: 'en',
         image: {
             toolbar: [
                 'imageTextAlternative',
                 'imageStyle:inline',
                 'imageStyle:block',
                 'imageStyle:side',
                 'linkImage'
             ]
         },
         table: {
             contentToolbar: [
                 'tableColumn',
                 'tableRow',
                 'mergeTableCells',
                 'tableCellProperties',
                 'tableProperties'
             ]
         }
     };

     export default Editor;

We then wrapped this in a custom Nuxt component called CKeditorNuxt.vue (below).

   <template>
      <ckeditor
        :editor="editor"
        :value="value"
        :config="config"
        :tagName="tagName"
        :disabled="disabled"
        @ready="onReady"
        @input="event => $emit('input', event)"
      />
    </template>

    <script>
    let DecoupledEditor
    let CKEditor

    if (process.client) {
      DecoupledEditor = require('../ckeditor5-custom/build/ckeditor.js');
      CKEditor = require('@ckeditor/ckeditor5-vue2');
    } else {
      CKEditor = { component : {template:'<div></div>'}}
    }

    export default {
      name: "CkeditorNuxt",
      components: {
        ckeditor: CKEditor.component
      },
      props: {
        value: {
          type: String,
          required: false
        },
        tagName: {
          type: String,
          required: false,
          default: 'div'
        },
        disabled: {
          type: Boolean,
          required: false,
          default: false
        },
        uploadUrl: {
          type: String,
          required: false
        },
        config: {
          type: Object,
          required: false,
          default: function () {
          }
        }
      },
      data() {
        return {
          editor: DecoupledEditor,
        };
      },
      methods: {
        onReady( editor )  {
          // previously we were using the "disabled" prop defined above to effectively
          // set whether it was readonly and whether the toolbar appears - we 
          // may want to change this to use the new method
          if (!this.$props.disabled) {
            editor.ui.getEditableElement().parentElement.insertBefore(
            editor.ui.view.toolbar.element,
            editor.ui.getEditableElement()
          );
         }
        },
      }
    };
    </script>

The above CKeditorNuxt.vue component is then used in another Nuxt component (we'll call it MyParentComponent.vue) as a child:

    <div>  
      <CkeditorNuxt id="id-ex" class="class-ex" v-model="myviewmmodel" />
    <div>

    <script>
      import CkeditorNuxt from "./CkeditorNuxt";

      export default {
        name: "MonographSubSectionDetail",
        components: {
          CkeditorNuxt
        },
      };
    </script>

Some of the variations we've tried using in the created, beforeMount and Mounted hooks in the CKeditorNuxt.vue component:

editor.disableReadOnlyMode( "defaultEditorLock" );

And

CKEditor.disableReadOnlyMode( "defaultEditorLock" );

And

DecoupledEditor.disableReadOnlyMode( "defaultEditorLock" );

In the MyParentComponent.vue component we've also tried calling CkeditorNuxt.disableReadOnlyMode directly.

All of these return the editor as undefined.

Not sure what we need to do to access the component and call disable/enableReadOnlyMode. We're not sure if this is a bug or if we're just loading the component wrong because we're n00bz.

In either case, thank you for any assistance you can provide.

Documentation we have used while researching the error:

https://ckeditor.com/docs/ckeditor5/latest/support/error-codes.html#error-editor-isreadonly-has-no-setter

https://ckeditor.com/docs/ckeditor5/latest/updating/migration-to-34.html#changed-mechanism-for-setting-and-clearing-the-editor-read-only-mode

https://nuxtjs.org/pt/docs/concepts/nuxt-lifecycle/

https://vuejs.org/guide/essentials/lifecycle.html#registering-lifecycle-hooks

https://stackoverflow.com/questions/72022607/ck-editor-5-custom-build-onchange-not-working

This issue was also cross posted to stack overflow:
https://stackoverflow.com/questions/72735453/ckeditor5-v-34-1-0-error-editor-isreadonly-has-no-setter-with-nuxtjs

If someone here can provide an answer I will share it there. Thank you

Hi, thanks for the report. Could you check which version of ckeditor5-vue are you using? This component also uses read-only mechanism internally and it was updated in version 4.0.0 (see changelog). So if you're using latest CKEditor 5 packages and some older version of ckeditor5-vue(which uses the old read-only mechanism), this might be the cause of the error.

@FilipTokarski That was it! I was using an older version of @ckeditor/ckeditor5-vue2 (haven't upgrade to vue3 yet). Once I updated to the current version that resolved the issue. Thank you for your help!