Search code examples
javascriptvue.jsvuejs2computed-observable

Is there any way to have multiple Vues have a computed listener working on the same value?


Setup:

I have multiple Vue components, and each component has multiple instances in different dialogs in my web app.

For each type of component I have a global state (handrailOptions in the example below) so that each type of component stays in sync across the dialogs.

I'd like for it so that when a component proceeds beyond step 1, I hide the other components in that dialog.

I have achieved this nicely using the computed / watch combo.

However, my problem is that it seems if I try to listen in with computed through more than 1 Vue instance, it hijacks the other listeners.

Problem

Below is a simplified version of what I'm working with, when the app starts up, the console logs 'computed 1' & 'computed 2'. But then when I change handrailOptions.step, only the second fires. ('computed 2' & 'watched 2')

Is there any way to have multiple Vues have a computed listener working on the same value?

handrailOptions = {
    step: 1
};

Vue.component( 'handrail-options', {
    template: '#module-handrail-options',
    data: function() {
        return handrailOptions;
    },
});

var checkoutDialog = new Vue({
    el: '#dialog-checkout',
    computed: {
        newHandrailStep() {
            console.log('computed 1');
            return handrailOptions.step;
        }
    },
    watch: {
        newHandrailStep( test ) {
            console.log('watched 1');
        }
    }
});

new Vue({
    el: '#dialog-estimate-questions',
    computed: {
        newHandrailStep() {
            console.log('computed 2');
            return handrailOptions.step;
        }
    },
    watch: {
        newHandrailStep( test ) {
            console.log('watched 2');
        }
    }
});

Solution

  • This works as expected. I made handrailOptions responsive by making the data object of a new Vue. Making it the data object of a component, as you did, could also work, but the component would have to be instantiated at least once. It makes more sense to have a single object for your global, anyway.

    handrailOptions = {
      step: 1
    };
    
    // Make it responsive
    new Vue({data: handrailOptions});
    
    var checkoutDialog = new Vue({
      el: '#dialog-checkout',
      computed: {
        newHandrailStep() {
          console.log('computed 1', handrailOptions.step);
          return handrailOptions.step;
        }
      },
      watch: {
        newHandrailStep(test) {
          console.log('watched 1');
        }
      }
    });
    
    new Vue({
      el: '#dialog-estimate-questions',
      computed: {
        newHandrailStep() {
          console.log('computed 2', handrailOptions.step);
          return handrailOptions.step;
        }
      },
      watch: {
        newHandrailStep(test) {
          console.log('watched 2');
        }
      }
    });
    
    setInterval(() => ++handrailOptions.step, 1500);
    <script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.min.js"></script>
    <div id="dialog-estimate-questions">
      Main step {{newHandrailStep}}
    </div>
    <div id="dialog-checkout">
        CD step {{newHandrailStep}}
    </div>