10

I've got basic template that is outputing text from wysiwyg editor via two-ways data binding as below:

<template>
  <div>
    <quill-editor 
      v-model="debounceText"
      :options="editorOptionProTemplate"
      >
    </quill-editor>
  <div  v-html="textComputed"></div>
  </div>
</template>

<script>
data () {
  return {
    text: ''
  }
},
computed: {
debounceText: {
  get() { return this.text; },
  set: _.debounce(function(newValue) {
    this.text = newValue;
  }, 100)
 },
//using computed for many variants for styling output in web (here just adding <b> tag)
  textComputed() {
    return '<b>' + this.text + '</b>'
  }
 }
</script>

at this level all works fine

Now, I'm changing variable into array (object), using v-for (many elemnents to edit at the same time and outputing them in the array as below):

<template>
  <div v-for="item in items">
    <quill-editor 
      v-model="item.text"
      :options="editorOptionProTemplate"
      >
    </quill-editor>
  <div v-html="textComputedArray"></div>
  </div>
</template>

<script>
data () {
  return {
    items: [
      {active: true, text: 'text1', textOutput: ''},
      {active: true, text: 'text2', textOutput: ''},
      {active: true, text: 'text3', textOutput: ''},
      {active: true, text: 'text4', textOutput: ''},
      {active: true, text: 'text5', textOutput: ''}
   ]
  }
},

textComputedArray: {
        var output=''
          for (var i=0; i<this.items.length; i++) {
            if (this.items[i].active) {
              this.items[i].textOutput= this.items[i].text + '<br />'
              output = output + this.items[i].textOutput
            }
            else {
              this.items[i].textOutput= ''
            }          
          }
          return output
        },
</script>

how should I modify my code to apply debounceText computed to this output? I think that I simply cannot add computed to my template, and also I cannot pass any parameter into computed property.

Maybe someone more experienced than me will give me some solution/advice?

2
  • Make a component. Commented Feb 28, 2018 at 11:51
  • @RoyJ hello again :) do you think this is the right way? I'll check it! Commented Feb 28, 2018 at 11:54

1 Answer 1

21

Any time you have an array and you think each item needs a computed, you should look at making a component. That is how data and computeds get attached to each other.

In a lot of cases, you can make a computed based on the array, and it's fine, but you should be aware that any change to the array causes a recompute of the entire computed array. With a component, only the affected rows are recomputed. A simple example is embedded here.

new Vue({
  el: '#app',
  data: {
    arr: [1, 2, 3]
  },
  computed: {
    carr() {
      console.log("Computing the whole array");
      return this.arr.map(x => 'hi ' + x);
    }
  },
  components: {
    addHi: {
      props: ['value'],
      template: '<div>{{a}}</div>',
      computed: {
        a() {
          console.log("Computing for", this.value);
          return 'hi ' + this.value;
        }
      }
    }
  },
  created() {
    setTimeout(() => {
      console.log("** Changing a value");
      this.arr.splice(2, 1, 'X');
    }, 1500);
  }
});
<script src="//unpkg.com/vue@latest/dist/vue.js"></script>
<div id="app">
  <div v-for="a in carr">{{a}}</div>
  <add-hi v-for="a in arr" :value="a" :key="a"></add-hi>
</div>

If you need your computed to be writable, you won't be able to edit individual items, so you're really forced to make a component. It is pretty straightforward: just move the HTML into the template of the component, move the computed into the component (adjusting it to work on the prop value), and then – because it is operating on a prop – change the set function to use $emit rather than changing its value directly.

debouncedQuillEditor: {
  props: ['value', 'options'],
  template: '<quill-editor v-model="debouncedValue" :options="options"></quill-editor>',
  computed: {
    debouncedValue: {
      get() {
        return this.value;
      },
      set: _.debounce(function(newValue) {
        this.$emit('input', newValue);
      }, 100)
    }
  }
},

I made a fiddle to demonstrate. I made a second component to handle the output HTML, although it could have been included in the first component.

Sign up to request clarification or add additional context in comments.

4 Comments

Thank you for the answer, I'll look at this soon!
this is working, and user experience is much better now! I also noticed a small easter egg in the template ;)
Oops. :D Updated the fiddle.
"Any time you have an array and you think each item needs a computed, you should look at making a component." - Great heuristics! Simple and cuts through confusion.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.