Home > front end >  How to detect changes in property values in object in vue?
How to detect changes in property values in object in vue?

Time:11-10

On top of How to watch only after the initial load from API in VueJS?, I wanted to detect any changes in values of the properties in the json object.

Initially the user object is

user: {
        userId: 0,
        id: 0,
        title: "",
        completed: false,
      },

I have two input fields,

 <input type="text" v-model="user.userId" /> <br />
 <input type="text" v-model="user.title" /> <br />

and a button <button :disabled="isLoaded">Update</button>

If none of the input values changed, the button should be still disabled. Example, if the userId is changed to 1, the button should be enabled but if the value is changed back to 0, the button should be disabled. I referred Vue js compare 2 object and remove differences in watcher and I tried following but failed.

<template>
  <div id="app">
    <div v-if="!isFetching">
      <input type="text" v-model="user.userId" /> <br />
      <br />
      <input type="text" v-model="user.title" /> <br />
      <br />
      <button :disabled="isLoaded">Update</button>
    </div>
    <div v-else>Loading...</div>
  </div>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      user: {
        userId: 0,
        id: 0,
        title: "",
        completed: false,
      },
      isFetching: false,
      isLoaded: true,
    };
  },
  watch: {
    user: {
      handler(oldVal, newVal) {
        this.checkObject(oldVal, newVal);
      },
      deep: true,
    },
  },
  methods: {
    checkObject: (obj1, obj2) => {
      const isEqual = (...objects) =>
        objects.every(
          (obj) => JSON.stringify(obj) === JSON.stringify(objects[0])
        );
      console.log(obj1, obj2);
      console.log(isEqual(obj1, obj2));
    },
  },
  created() {
    this.isFetching = true;
    fetch("https://jsonplaceholder.typicode.com/todos/1")
      .then((response) => response.json())
      .then((json) => {
        this.user = json;
        this.isLoaded = true;
      })
      .finally(() => (this.isFetching = false));
  },
};
</script>

Here's a live demo: https://codesandbox.io/embed/friendly-hopper-ynhxc?fontsize=14&hidenavigation=1&theme=dark

CodePudding user response:

Here is one way you could solve this. So below I'm storing two user objects, one is my base line comparison compareUser, and the other is the user that is under edit. When something changes which the deep watch on user will notify me about, I use a utility function like isEqual from lodash to perform a semantic comparison of the base line object and the current user object, and see if there are any differences.

If I want to update my base line that I'm comparing to, then I update the compareUser from the current user by cloning it.

You can of course replace things like isEqual and cloneDeep by rolling your own to avoid the extra library if that's an issue.

<script>
import { isEqual, cloneDeep } from "lodash";

const createDefault = function () {
  return {
    userId: 0,
    id: 0,
    title: "",
    completed: false,
  };
};

export default {
  name: "App",
  data() {
    return {
      compareUser: createDefault(),
      user: createDefault(),
      isLoaded: false,
      isDifferent: false,
    };
  },
  watch: {
    user: {
      handler() {
        this.isDifferent = !isEqual(this.user, this.compareUser);
      },
      deep: true,
    },
  },
  methods: {
    setCompareUser(user) {
      this.compareUser = cloneDeep(user);
      this.isDifferent = false;
    },
  },
  async created() {
    const response = await fetch(
      "https://jsonplaceholder.typicode.com/todos/1"
    );
    const user = await response.json();
    this.user = user;
    this.setCompareUser(user);
    this.isLoaded = true;
  },
};
</script>

Demo: https://codesandbox.io/s/modern-tdd-yg6c1

  • Related