Tom
Tom

Reputation: 85

How watch global variable in component vuejs?

I need global variables for errors. But I don't want set input variable for every component. How I can watch $errors in component ABC without input variable?

(without <abc :errors="$errors"></abc>)

index.js:

Vue.prototype.$errors = {};

new Vue({
    el: '#app',
    render: h => h(App),
}

App.vue:

...
name: 'App',
components: {
ABC
}
...
methods:{
getContent() {
this.$errors = ...from axis...
}

Component ABC:

<template>
<div>{{ error }}</div>
</template>
...
watch: {
???
}

Upvotes: 7

Views: 16209

Answers (2)

Wolle
Wolle

Reputation: 491

  1. as Estradiaz said: You can use Vuex and access the value outside of Vue like in this answer: https://stackoverflow.com/a/47575742/10219239

  2. This is an addition to Skirtles answer: You can access such variables via Vue.prototype.variable. You can set them directly, or use Vue.set, it works either way.

My code (basically the same as Skirtless): main.js

const mobile = Vue.observable({ mobile: {} });
Object.defineProperty(Vue.prototype, '$mobile', {
  get() { return mobile.mobile; },
  set(value) { mobile.mobile = value; }
});

function widthChanged() {
  if (window.innerWidth <= 768) {
    if (!Vue.prototype.$mobile) Vue.set(Vue.prototype, '$mobile', true);
  } else if (Vue.prototype.$mobile) Vue.set(Vue.prototype, '$mobile', false);
}

window.addEventListener("resize", widthChanged);

widthChanged();

Home.vue:

watch: {
  '$mobile'(newValue) {
    // react to Change in width
  }
}

Upvotes: 4

skirtle
skirtle

Reputation: 29092

Here's an example of how it could be done:

const errors = Vue.observable({ errors: {} })

Object.defineProperty(Vue.prototype, '$errors', {
  get () {
    return errors.errors
  },
  
  set (value) {
    errors.errors = value
  }
})

new Vue({
  el: '#app',
  
  methods: {
    newErrors () {
      // Generate some random errors
      const errors = {}
      
      for (const property of ['name', 'type', 'id']) {
        if (Math.random() < 0.5) {
          errors[property] = 'Invalid value'
        }
      }
      
      this.$errors = errors
    }
  }
})

new Vue({
  el: '#app2',
  
  watch: {
    $errors () {
      console.log('$errors has changed')
    }
  }
});
<script src="https://unpkg.com/[email protected]/dist/vue.js"></script>

<div id="app">
  <pre>{{ $errors }}</pre>
  <button @click="newErrors">New errors</button>
</div>

<div id="app2">
  <pre>{{ $errors }}</pre>
</div>

I've created two Vue instances to illustrate that the value really is shared. Clicking the button in the first instance will update the value of $errors and the watch is triggered in the second instance.

There are a few tricks in play here.

Firstly, reactivity can only track the reading and writing of properties of an observable object. So the first thing we do is create a suitable object:

const errors = Vue.observable({ errors: {} })

We then need to wire this up to Vue.prototype.$errors. By defining a get and set for that property we can proxy through to the underlying property within our observable object.

All of this is pretty close to how data properties work behind the scenes. For the data properties the observable object is called $data. Vue then uses defineProperty with get and set to proxy though from the Vue instance to the $data object, just like in my example.

Upvotes: 13

Related Questions