Search code examples
vue.jswysiwygquill

Vuejs Computed Setter with an object two way data binding


Following the basic example how two-ways databinding works:

<template>
<input v-model="fullname"/>
<input v-model="first"/>
<input v-model="last"/>
</template>
<script>
    var app = new Vue({
      el: '#app',
      data: {
        first: '',
        last: ''
      },
      computed: {
        fullname: {
          // getter function
          get: function() {
            return this.first + " " + this.last
          },
          // setter function
          set: function(value) {
            var name = value.split(' ')
            this.first = name[0]
            this.last = name[1]
          }
        }
      }
    })
</script>

As shown here: https://youtu.be/PuxdMnk-u5k?t=17m43s (17:43) - you can easily modify data of first and last name within fullname input.

Now my case is just a little different. I've got an object as below:

myObject: [
   {active: true, itemId: "55", text: 'some text', textOutput: ''}
   {active: true, itemId: "56", text: 'some text', textOutput: ''}
   {active: true, itemId: "58", text: 'some text', textOutput: ''}
   {active: true, itemId: "65", text: 'some text', textOutput: ''}
   {active: true, itemId: "105", text: 'some text', textOutput: ''}
]

next I modify this a little bit via computed - here just add to each item <li> tags:

modifyArrayItem () {
  var output=''
    for (var i=0; i<this.myObject.length; i++) {
      if (this.myObject[i].active) {
        this.myObject[i].textOutput= '<li>' + this.myObject[i].text + '</li>'
        output = output + this.myObject[i].textOutput
      }
      else {
        this.myObject[i].textInput = ''
      }          
    }
    return output
  }, 

and then outputing only active items via v-html

<div  v-html="modifyArrayItem"></div>

Easy so far. Now I want to be able to edit output in my html via, let's say quill-editor like this:

<quill-editor v-model="modifyArrayItem" :options="options"></quill-editor>

Can it be done?

What should I do to be able to edit myObject[n-element].text and myObject[n-element].textOutput basing on itemId number within my quill editor field? (please note that [n-element] != itemId).

How to prevent edited data loss on dynamically changing myObject.active of each element to true/false while editing? How about saving edited text into seperated variable?

edit: some idea - add some kind of invisible tag with itemId value to quill-editor for each <li>?


Solution

  • Yes, it is possible. You can parse the HTML into lines, and assign the lines to their corresponding entry in myObject.

    However, be aware that updating the contents of the editor every time you type something causes the cursor to move to the beginning of the editor.

    It turns out quill has a way of getting and setting selection position. It gets a little hairy, because each edit calls set twice, so you have to check whether anything is actually changing. But this works:

    modifyArrayItem: {
      get() {
        return this.myObject.map(o => "<li>" + o.text + "</li>").join("");
      },
      set(newValue) {
        const oldLines = this.myObject.map((o) => o.text);
        const lines = newValue.split("</p>")
          .map((p) => p.replace('<p>', ''));
        const same = oldLines.join('') === lines.join('');
    
        if (!same) {
          const pos = this.$refs.quill.quill.getSelection();
          for (let i=0; i < this.myObject.length; ++i) {
            this.myObject[i].text = lines[i];
          }
          this.$nextTick(() => {
            this.$refs.quill.quill.setSelection(pos);
          });
        }
      }
    }
    

    Updated codepen