DEV Community

saroj sasmal
saroj sasmal

Posted on • Updated on

Understanding VueJs Computed properties and Watchers.

Vue.js is an open-source model–view–viewmodel front end JavaScript framework for building user interfaces and single-page applications. The most interesting point about Vue is that, it's super easy to learn. I've been playing around and doing some projects with Vue lately and thought it might be a good idea to share my experience.

With that being said, if you are pretty new to Vue then I would recommend you to visit the official documentation. But do remember to come back here :).

In this blog post, we are going to take a deep dive into VueJS's computed properties and watchers. We will go beyond the traditional definition and try to build some real world things here. I will be using codepens for most of the examples, so feel free to check them out.

So What is a computed property ?

A computed property is a kind of method that always returns a value. We can mainly use it to derive values from other properties. But that's not the real power of computed properties.

Computed properties are reactive in nature meaning when the values used inside of a computed property changes, it reacts to them and re-evaluates the properties again. Most importantly, computed properties are cached which means it can enhance performance while dealing with complex loops.

So computed property is a function that always returns a value, huh ?. Yup!! It's a method which acts as property. If this sounds confusing, don't worry about it. Let's take a close look at it with an example.

<div>
  <div v-if="commentsAvailable">
    comments are not available for this blog at this moment!
  </div>
  <div v-else>
    // Logic to display comments
  </div>
</div>
Enter fullscreen mode Exit fullscreen mode
new Vue({
  data() {
    return {
      comments: [],
    };
  },
  computed: {
    commentsAvailable() {
      return !_.isEmpty(this.comments);
    },
    totalCount() {
     return this.comments.length;
    }
  },
  methods: {
    async fetchComments() {
      try {
        this.comments = await CommentService.fetchComments();
      } catch (err) {
        this.comments = [];
      }
    },
  },
  created() {
    this.fetchComments();
  },
});
Enter fullscreen mode Exit fullscreen mode

So initially we won't be seeing any comments here as the commentsAvailable evaluates itself to false. we can simply use this computed property to sort of display a loader when there are no comments and when comments are fetched, then the loader becomes hidden and comments are shown.

The commentsAvailable computed can be accessed like a normal property this.commentsAvailable inside a method and commentsAvailable inside template.

One other use case is to compute or derive values from the existing data property. Let's consider the following example, where we have a few cart items as a data property and addToCart() which basically adds an item to the cart items. But that is not the deal here, we are interested in the total price of purchase, i.e.total computed property, which calculates the total price of products added to the cart.

So when the cartItems changes(added/removed), the computed property gets reevaluated and we get updated purchase price.

new Vue({
  data() {
    return {
      cartItems: [],
    };
  },
  computed: {
    total() {
      const totalAmount = this.cartItem.reduce(
        (acc, curr) => acc + curr.price * quantity,
        0
      );
      return totalAmount;
    },
  },
  methods: {
    addToCart(item) {
      this.cartItems.push(item);
    },
  },
});
Enter fullscreen mode Exit fullscreen mode

Computed property must be a synchronous operation!

A computed property must be a synchronous method, we simply can't have a method which perform any async operation like fetching results from DB or making an http request etc.

Let's take another example to understand this in a more better way. Please take a look at this following code, where we are simply displaying a few food items and the macros nutrients present in it.

Initially the table is meant to display only protein and calories for the food items. But when we check the Show All Macro checkbox, it displays all the macros for the food items.

<div class="container-fluid">
  <div class="row align-items-center justify-content-center">
    <label>Show All Macros</label>
    <input
      type="checkbox"
      v-model="showDetailView"
      style="margin-left: 10px;"
    />
  </div>
  <div class="row">
    <div>
      <table id="table" class="table">
        <thead>
          <tr>
            <td v-for="h in tableHeaders" :key="h.value">{{h.text}}</td>
          </tr>
        </thead>
        <tbody>
          <tr v-for="(row, index) in rows" :key="row.name">
            <td v-for="header in headers">
              {{ row[header.value] }}
            </td>
          </tr>
        </tbody>
      </table>
    </div>
  </div>
</div>
Enter fullscreen mode Exit fullscreen mode

It's important to notice that the tableHeaders and rows are computed properties, not the normal data properties because they are reactive and when the show all macros prop changes, these are re-evaluated and populate the table accordingly.

new Vue({
  el: "#app",
  computed: {
    tableHeaders() {
      if (!this.showDetailView) {
        return this.headers.filter((h) =>
          this.defaultHeaders.includes(h.value)
        );
      } else {
        return this.headers;
      }
    },
    rows() {
      if (this.showDetailView) {
        //console.log("should return all foods", this.foods);
        return this.foods;
      } else {
        let data = this.foods.map((f) => {
          let obj = {};
          this.defaultHeaders.forEach((key) => (obj[key] = f[key]));
          return obj;
        });
        console.log(" data :", data);
        return data;
      }
    },
  },
});
Enter fullscreen mode Exit fullscreen mode

There may be times, when you need a way to track changes of certain properties and perform some operations in relation to that. Watchers are exactly meant for that purpose.

Using watchers, we can watch for changes and perform operations. Unlike computed properties, the method we can use inside a watcher can perform both sync and async operations depending on the requirements.

One example of a using computed property and watcher in a single component would be in building a pagination control.

Now let's consider the following code pen example which extends the previous code pen with pagination feature.

We have a couple of html controls here, first off the select box, which has v-model attached to it, and a watcher watches for changes and the grid reloads data with selected number of rows whenever it changes.

Then we have a few computed properties here as well, pages which calculates total number of pages based on the total number of rows selected from drop down. And finally the totalItems, rangeStart and rangeEnd as text which displays showing 1-10 of 16 text.

I would encourage you to take a look at the following codepen and just play around with the pagination control to get a sense of how the rangeStart and rangeEnd changes dynamically.

I hope this article has helped you give an insight into computed properties and watchers. If you have any questions, feel free to leave a comment . Till next time, stay safe, have fun and keep exploring..

Top comments (0)