Search code examples
javascriptvue.jsecmascript-6es6-classvue-reactivity

Vuejs Es6 class reactivity


i'm trying to have a computed property in vuejs associated to a es6 class. My Vue instance looks like this:

...
props: ['customClass'],
computed: {
    localClass: {
         get() {
             return this.customClass
         },
         set (value) {
             console.log("changed")
         }
     }
 }
 ...

My class looks like this

class CustomClass {
    constructor () {
        this.selected = false
    }
}

If i try to do something like that:

this.localClass.selected = true

but the setter is never called, like the reactivity has been lost and i don't understand why.

I also try:

Vue.set(this.localClass, 'selected', true)

I pass customClass as a prop, but even creating a new instance directly in the component it doesn't change the result.

In vuejs docs i don't recall a section talking about reactivity problem in es6 class, so i was wondering if someone know why and how to make my class reactive.

Thanks in advance


Solution

  • The setter of a computed property, say myComputedProperty, is triggered when you assign to that property (e.g. this.myComputedProperty = {something: 'else'}.

    What you probably are looking for is a watcher, more specifically, a watcher with deep: true, such as:

    watch: {
      localClass: {
        deep: true,
        handler() {
          out.innerHTML += "watched!";
        }
      }
    },
    

    Demo below.

    class CustomClass {
      constructor() {
        this.selected = false
      }
    }
    Vue.component('custom', {
      template: '#custom',
      props: ['customClass'],
      computed: {
        localClass: {
          get() {
            return this.customClass
          },
          set(value) {
            out.innerHTML += "changed!\n";
          }
        }
      },
      watch: {
        localClass: {
          deep: true,
          handler() {
            out.innerHTML += "watched!\n";
          }
        }
      },
      methods: {
        assignToSelected() {
          this.localClass.selected = true
        },
        assignToLocalClass() {
          this.localClass = {
            selected: true
          }
        }
      }
    });
    new Vue({
      el: '#app',
      data: {
        test: new CustomClass()
      },
    })
    #out { background: black; color: gray; }
    span { font-size: x-small; font-family: verdana }
    <script src="https://unpkg.com/vue"></script>
    
    <template id="custom">
      <div>
        {{ localClass }}
        <br>
        <button @click="assignToSelected">assignToSelected</button>
        <span>Note: will trigger "watched!" just once, because, since the value is hardcoded in the method (see code) subsequent clicks won't modify the value.</span>
        <br><br>
        <button @click="assignToLocalClass">assignToLocalClass</button>
        <span>Note: assignToLocalClass() will trigger the computed setter, but wont trigger the watcher because the computed setter currently sets nothing, so nothing changed for the watcher to trigger.</span>
      </div>
    </template>
    
    <div id="app">
      <custom :custom-class="test"></custom>
    </div>
    
    <pre id="out"></pre>