Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

VueJs: Textarea input binding

I'm trying to figure out how to detect change of the value on the textarea from within the component.

For input we can simply use

<input
  :value="value"
  @input="update($event.target.value)"
>

However on textarea this won't work.

What I'm working with is the CKEditor component, which should update wysiwyg's content when model value of the parent component (attached to this child component) is updated.

My Editor component currently looks like this:

<template>
    <div class="editor" :class="groupCss">
        <textarea :id="id" v-model="input"></textarea>
    </div>
</template>

<script>
    export default {
        props: {
            value: {
                type: String,
                default: ''
            },
            id: {
                type: String,
                required: false,
                default: 'editor'
            }
        },
        data() {
            return {
                input: this.$slots.default ? this.$slots.default[0].text : '',
                config: {
                    ...
                }
            }
        },
        watch: {
            input(value) {
                this.update(value);
            }
        },
        methods: {
            update(value) {
                CKEDITOR.instances[this.id].setData(value);
            },
            fire(value) {
                this.$emit('input', value);
            }
        },
        mounted () {
            CKEDITOR.replace(this.id, this.config);
            CKEDITOR.instances[this.id].setData(this.input);
            this.fire(this.input);
            CKEDITOR.instances[this.id].on('change', () => {
                this.fire(CKEDITOR.instances[this.id].getData());
            });
        },
        destroyed () {
            if (CKEDITOR.instances[this.id]) {
                CKEDITOR.instances[this.id].destroy()
            }
        }
    }
</script>

and I include it within the parent component

<html-editor
    v-model="fields.body"
    id="body"
></html-editor>

however, whenever parent component's model value changes - it does not trigger the watcher - effectively not updating the editor's window.

I only need update() method to be called when parent component's model fields.body is updated.

Any pointer as to how could I approach it?

like image 897
Sebastian Sulinski Avatar asked Jul 09 '17 10:07

Sebastian Sulinski


1 Answers

That's a fair bit of code to decipher, but what I would do is break down the text area and the WYSIWYG HTML window into two distinct components and then have the parent sync the values, so:

TextArea Component:

<template id="editor">
  <textarea :value="value" @input="$emit('input', $event.target.value)" rows="10" cols="50"></textarea>
</template>

/**
 *  Editor TextArea
 */
Vue.component('editor', {
  template: '#editor',
  props: {
    value: {
      default: '',
      type: String
    }
  }
});

 

All I'm doing here is emitting the input back to the parent when it changes, I'm using input as the event name and value as the prop so I can use v-model on the editor. Now I just need a wysiwyg window to show the code:

WYSIWYG Window:

/**
 * WYSIWYG window
 */
Vue.component('wysiwyg', {
  template: `<div v-html="html"></div>`,
  props: {
    html: {
      default: '',
      type: String
    }
  }
}); 

Nothing much going on there, it simply renders the HTML that is passed as a prop.

Finally I just need to sync the values between the components:

<div id="app">
  <wysiwyg :html="value"></wysiwyg>
  <editor v-model="value"></editor>
</div>

new Vue({
  el: '#app',
  data: {
    value: '<b>Hello World</b>'
  }
})

Now, when the editor changes it emits the event back to the parent, which updates value and in turn fires that change in the wysiwyg window. Here's the entire thing in action: https://jsfiddle.net/Lnpmbpcr/

like image 116
craig_h Avatar answered Sep 17 '22 12:09

craig_h