1

I just wanna use Vue computed, but I get undefined(styleObj.width), and it seems that the computed function was not called (didn't log 'computed'). And when I changed data while the computed function was still not be called and the data (styleObj.width) was still undefined.

The code is simple and I hope you know what I'm talking about.

<!DOCTYPE html>
<html>
  <head>
    <script src="http://vuejs.org/js/vue.js"></script>
    <meta charset="utf-8" />
    <title>JS Bin</title>
    <style>
      #app {
        height: 100px;
        background: red;
      }
    </style>
  </head>
  <body>
    <div id="app" :style="styleObj"></div>

    <script>
      var vm = new Vue({
        el: '#app',
        data: {
          num: 100,
          styleObj: {
            width: this.calcWidth, // always be undefined even num has changed
          },
        },
        computed: {
          calcWidth() {
            console.log('computed') // never log 'computed' which means the
            // calcWidth not be called
            return this.num + 'px'
          },
        },
      })

      setTimeout(() => {
        vm.num = 200
      }, 2000)
    </script>
  </body>
</html>

I have 2 questions:

  1. Why the calcWidth function never be called? I think it will be called twice, at the beginning and in 2 sec, but it never be called. Why?

  2. Why the styleObj.width has always been undefined?

0

2 Answers 2

5

There are several problems.

The way it's written currently, the this in width:this.calcWidth will not be referring to the correct object. It needs to be within a function to get the scoping correct. That is easily fixed by changing data to a function.

The next problem is that computed properties are not available within the data function. The order is roughly:

  1. Provide/inject
  2. Props
  3. Data
  4. Computed

Things lower down that list can use things higher up the list, not the other way around.

Instead you can make the whole style object a computed property:

computed: {
  styleObj () {
    return {
      width: this.num + 'px'
    }
  }
}

or if you prefer to retain calcWidth:

computed: {
  styleObj () {
    return {
      width: this.calcWidth
    }
  }
}
Sign up to request clarification or add additional context in comments.

2 Comments

very clear.but I still don't know why the calcWidth function not be called?because we can't use compued properties in data so it won't be called?@skirtle
@Archsx 1. Your this reference is incorrect so when you wrote this.calcWidth it wasn't even accessing the right object. 2. Even if you solved that it wouldn't work because computed properties aren't wired up until after data. 3. The timer has no effect on calcWidth as computed properties are computed lazily so unless something asks for calcWidth it won't be evaluated.
1

You can't use computed property in data, because data evaluates before the computed properties did.

You can use a watcher to achieve the intended result:

watch: {
  num(val) {
    this.styleObj.width = val + 'px'
  }
}

If num changes the watcher get triggered. See the documentation on that.

Edit: You can use the argument immediate to trigger directly:

watch: {
  num: {
    immediate: true,
    handler(val) {
       this.styleObj.width = val + 'px'
    }
  }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.