41

How can I use a computed property in the data or emit it via bus?

I have the following vue instance, but myComputed is always undefined but computedData is working correctly.

var vm = new Vue({
  data(){
    return{
      myComputed: this.computedData
    }
  },

  computed: {
    computedData(){
      return 'Hello World'
    }
  }
})
Clinton Green
  • 9,697
  • 21
  • 68
  • 103
  • 2
    Why do you need this? You can always just access `this.computedData` the same way you would access `this.myComputed` – jfadich Jun 01 '17 at 23:00
  • It's not working with this. computedData, it's undefined. – Clinton Green Jun 01 '17 at 23:02
  • 2
    What I mean is you can access computed properties the exact same way you access data so there is not need to do what you're trying to do. – jfadich Jun 01 '17 at 23:04
  • So I'm trying to emit the computed property and that wasn't working like so `bus.$emit('send-computed-data', this.computedData);` any ideas then? – Clinton Green Jun 01 '17 at 23:07
  • 2
    Sounds like your this isn't the component from the calling context – roberto tomás Jun 01 '17 at 23:40
  • Possible duplicate of [Is it possible to emit from mounted in Vuejs?](https://stackoverflow.com/questions/44318643/is-it-possible-to-emit-from-mounted-in-vuejs) – Phil Jun 02 '17 at 01:18

9 Answers9

34

Unfortunately, it is impossible to use computed property in data because of component creation timing: data evaluates Before computed properties.

Zvezdochka
  • 1,128
  • 12
  • 8
10

To make things as simple as possible, just do the work in watcher, unless you want to emit the changes to different components or there're a lot of variables you want to notify, then you may have to use Vuex or the event bus:

var vm = new Vue({
  data(){
    return{
      myComputed: '',
      computedData: 'Hello World'
    }
  },
  created() {
    this.myComputed = this.computedData;
  },
  watch: {
    computedData() {
      this.myComputed = this.computedData;
    }
  }
});
kevguy
  • 4,328
  • 1
  • 24
  • 45
  • 1
    You can now do even... `watch: { computedData: { handler: val => this.myComputed = val, immediate: true, } }` And skip the duplicate `created` part altogether... See https://vuejs.org/v2/api/#watch – xpuu Jan 09 '19 at 12:07
4
  1. Computed is already accessible in the template using {{ }}.

  2. But you can use the

watch:{
  //your function here
}

instead of computed

Tinashe
  • 1,052
  • 12
  • 20
3

you can work just with the computed function

   var vm = new Vue({
      data(){
        return{
          //is not necessary
        }
      },
    
      computed: {
        computedData(){
          return 'Hello World'
        }
      }
    })

and in your template

<template>
  <div>{{ computedData }}</div>
</template>
2

If you are using computed/reactive objects then it should be inside the computed and not inside the data.

Simply change your code to use computed instead of data

var vm = new Vue({
  data(){
    return{}
  },

  computed: {
    computedData(){
      return 'Hello World'
    },
    myComputed(){
     return this.computedData
    }
  }
})

you are trying to use data as computed and this shall not be. data doesn't act like computed object.

and it's not because of component creation timing. What if we changed the component creation timing ? this will not solve anything as data will take only the first computed value(only one) and will not update after.

Tawfik Nasser
  • 1,018
  • 9
  • 17
1

You are over-coding it. Computed props are accessible in the same manner as data props in your template.

var vm = new Vue({
  computed: {
    myComputed(){
      return 'Hello World'
    }
  }
})

In the template you have access to this just like you do to data:

<template>
  <div>{{ myComputed }}</div>
</template>

https://v2.vuejs.org/v2/guide/computed.html

tony19
  • 125,647
  • 18
  • 229
  • 307
innerurge1
  • 718
  • 3
  • 17
0

computed is not available at the time data gets initialized.

If it should be a one-time thing (and NOT reactive), you could achieve this by setting the data at the moment where the computed property is available by using the created() hook:



export default {
  data: () => ({
    myDataBackend: '',
  }),
  computed: {
    computedData () {
      return 'Hello World'
    }
  },
  created() {
    this.$set(this, 'myDataBackend', this.computedData)
  }
}

Futher reading: Vue Documentation on Lifecycle Hooks


In case you are trying to work with v-model:

You could also use :value and some event like @change or @keyup in the element instead.

  • :value is the value which the input-element initially works with
  • After writing some letter in the input field, the @keyup event changes the data.
    Typically, events carry the updated form value in target.value
  • The changeMyData method sets the value
  • the computed property listens to the data change and the :value of the input field gets updated.

Note: I used data as a data store. But you could also use for example vuex instead.

<template>
<div>
  <input
    type="text"
    :value="computedData"
    @keyup="changeMyData"
  />
  <p>{{myDataBackend}}</p>
</div>
</template>

<script>

export default {
  data: () => ({
    myDataBackend: 'Hello World'
  }),
  methods: {
    changeMyData(evt) {
        this.$set(this, 'myDataBackend', evt.target.value)
        console.log('Changed the value to: ' + evt.target.value)
    }
  },
  computed: {
    computedData () {
      return this.myDataBackend
    }
  }
}
</script>
tony19
  • 125,647
  • 18
  • 229
  • 307
Andi
  • 1
  • 2
0

Try to convert the computed in a method

var vm = new Vue({
  data(){
    return{
      myComputed: this.computedData
    }
  },

  methods: {
    computedData(){
      return 'Hello World'
    }
  }
})

This is simple and it works (NOT reactive), but has a cost:

https://medium.com/notonlycss/the-difference-between-computed-and-methods-in-vue-js-9cb05c59ed98

Gregorio
  • 393
  • 3
  • 9
0

Yes, now you can use computed (composition api feature) in data by using computed function from vue. Here is an example.

<template>
  <div>
    <div>{{ person.info }}</div>
    <div><button @click="changePersonId">change id</button></div>
  </div>
</template>

<script>
import { computed } from 'vue';

export default {
  data: function() {
    return {
      person: {
        name: 'Evgeny',
        id: this.generateId(),
        info: computed(() => `${this.person.name} with id = ${this.person.id}`)
      }
    }
  },

  methods: {
    generateId() {
      return +`${Math.random()}`.slice(2);
    },

    changePersonId() {
      this.person.id = this.generateId();
    }
  }
}
</script>

It works absolutely as normal computed property. You can assign getters and setters as well:

info: computed({
  // arrow functions syntax must be for right this value
  get: () => {
    return `${this.person.name} with id = ${this.person.id}`
  },
  set: (value) => {
    this.person.name = value
  }
})

Imho, it is good an opportunity to group properties together and share it among child components without repeating computed properties there and to add some common computed props just as object property.

It is very useful, that we can use some vue 3 features in vue 2!

Read abount vue3 computed props

e3genius
  • 1
  • 1