Search code examples
ckeditorckeditor5

Inline Editor - disable editor and display HTML / render content (Vue)


I am using CKEditor5 with Vue. In my Vuex store, I have the following property:

const state = {
  EditMode: false,
}

On a button click by a user with permission, I modify the Vuex store. If EditMode: true, I want to display the in-line editor. Else, display the raw HTML editorData (the user is not authorized to edit, or not in edit mode). I do that below:

<template>
    <vx-card :title="editorName" v-if="this.$store.state.EditMode">
        <ckeditor :editor="editor" v-model="editorData" :config="editorConfig"></ckeditor>
    </vx-card>
    <vx-card :title="editorName" v-else>
        <div v-html="editorData"></div>
    </vx-card>
</template>

<script>
    import InlineEditor from '@ckeditor/ckeditor5-build-inline'

    export default {
        name: "RichTextEditor",
        props: {
            editorName: {
                type: String,
                required: true,
            },
        },
        data() {
            return {
                loaded: false,
                time: null,
                timeElapsedSinceEdit: 0,
                editor: InlineEditor,
                editorData: 'New entry!',
                editorConfig: {
                    toolbar: {
                        items: [
                            '|',
                            'heading',
                            'fontFamily',
                            'fontSize',
                            'fontColor',
                            'bold',
                            'underline',
                            'italic',
                            'alignment',
                            'link',
                            'highlight',
                            'superscript',
                            'subscript',
                            '|',
                            'indent',
                            'outdent',
                            '|',
                            'blockQuote',
                            'horizontalLine',
                            'imageUpload',
                            'insertTable',
                            'mediaEmbed',
                            'undo',
                            'redo'
                        ]
                    },
                    language: 'en',
                    image: {
                        toolbar: [
                            'imageTextAlternative',
                            'imageStyle:full',
                            'imageStyle:side'
                        ]
                    },
                    table: {
                        contentToolbar: [
                            'tableColumn',
                            'tableRow',
                            'mergeTableCells',
                            'tableCellProperties',
                            'tableProperties'
                        ]
                    },
                },
            }
        },


        // Below code is situation-specific and not completely relevant
        watch: {
            editorData: function() {
                if (this.loaded) {
                    this.upsertData()
                }
            }
        },
        methods: {
            async pollData() {
                await
                    this.$http.get('/api/rte/' + this.editorName)
                        .then((response) => {
                            this.editorData = response.data.content
                        })
                        .catch((error) => {
                            if (window.environment == "production") {
                                location.href = 'pages/error-500/'
                            } else {
                                console.log(error.stack)
                            }
                        })

                this.loaded = true;
            },
            async upsertData() {
                console.log('up')
                await
                    this.$http.post('/api/rte/' + this.editorName + '/upsert', {
                        data: this.editorData,
                    })
                        .then((response) => {
                            this.$vs.notify({
                                title: 'Action Completed',
                                text: response.data.message,
                                color: 'success',
                                position: 'top-right'})
                        })
                        .catch((error) => {
                            if (window.environment == "production") {
                                location.href = 'pages/error-500/'
                            } else {
                                console.log(error)
                            }
                        })
            },
        },
        created() {
            this.pollData();
        },
    }
</script>

This works, but the in-line styling isn't respected with v-html (sizing and centering). If this.$store.state.EditMode: false, I get the following output:

enter image description here

If this.$store.state.EditMode: true I get this in the in-line editor (as expected).

enter image description here

Raw HTML (editorData property after pollData() is called)

<figure class="image image_resized" style="width:25.51%;"><img src="https://encrypted-tbn0.gstatic.com/images?q=tbn%3AANd9GcTRJO0xRohucbxcjlRoiRaw2cWYTbilYch5NQ&amp;usqp=CAU" alt="Free clipart megaphone announcement public domain vectors - Clipartix"></figure><h2 style="text-align:center;"><span style="color:hsl(30,75%,60%);"><strong>We have a new Intranet!</strong></span></h2><p style="text-align:center;"><a href="https://www.challengerunner.com/enroll/1e6738-2g5q">Summer / Fall Wellness Challenge Link</a></p>

Research showed that Vue's v-html doesn't respect scoped styling. I'm not entirely sure how that applies to in-line styling. To test output, I replaced my else with the raw HTML and got the same visual output as when I used v-html:

<vx-card :title="editorName" v-else>
    <figure class="image image_resized" style="width:25.51%;"><img src="https://encrypted-tbn0.gstatic.com/images?q=tbn%3AANd9GcTRJO0xRohucbxcjlRoiRaw2cWYTbilYch5NQ&amp;usqp=CAU" alt="Free clipart megaphone announcement public domain vectors - Clipartix"></figure><h2 style="text-align:center;"><span style="color:hsl(30,75%,60%);"><strong>We have a new Intranet!</strong></span></h2><p style="text-align:center;"><a href="https://www.challengerunner.com/enroll/1e6738-2g5q">Summer / Fall Wellness Challenge Link</a></p>
</vx-card>

What is the proper way to disable the inline editor and maintain visual consistency?


Solution

  • <template>
        <vx-card :title="editorName" v-if="loaded">
            <ckeditor :editor="editor" v-model="editorData" :config="editorConfig" :readonly="editorDisabled" :disabled="editorDisabled" ></ckeditor>
        </vx-card>
    </template>
    
    //...
            watch:{
                '$store.state.EditMode'(value, oldValue) {
                    if(value) {
                        this.editorDisabled = false;
                    } else {
                        this.editorDisabled = true;
                    }
                },
            },
    //...
    

    Question answered here:

    https://github.com/ckeditor/ckeditor5-vue/issues/154