Search code examples
javascriptvue.jsvuejs2bootstrap-vue

Vue Js data is not updated inside computed generated elements


I have used a bootstrap table in my projects. The fields are populated at computed such as

<b-table :items="items" :fields="tableFields">...</b-table>

   computed: {
    tableFields() {
      const result = [
        {
          key: "status",
          label: "Status",
        },
        {
          key: "name",
          label: "Name",
        },
        {
          key: "weight",
          label: "Weight",
        },
      ];
      return result;
    },
  },

I have some variables in data for example:

  data() {
    return {
      validationErrors: {},
    };
  },

validationErrors is updated inside a method such as:

methods: { 
   validateInput(data) { 
     const id = data.item.id;
     const value = data.item.weight;

     this.validationErrors[id] = "";

     if (value === "") {
        this.validationErrors[id] = "Weight is required";
      } else if (value < 1) {
        this.validationErrors[id] = "Value should be equal or greater than 1";
      } else if (value > 100) {
        this.validationErrors[id] = "Value cannot be greater than 100";
      } 
   }
   saveWeight() {
      // Validate all inputs before proceeding
      this.items.forEach((item) => {
        this.validateInput({ item: item });
      });
    },
}

And those two methods are fired from two html elements such as:

    <b-table :items="items" :fields="tableFields">
      <template v-slot:cell(weight)="data">
        <b-form-input
          v-model="data.item.weight"
          type="number"
          min="1"
          max="100"
          @input="validateInput(data)"
        />
        <small>
          {{ validationErrors[data.item.id] }}
        </small>
      </template>
   </b-table>

   {{ validationErrors }}   // keep it for demo purpose
    
    <b-button
      variant="primary"
      :disabled="hasvalidationErrors || !isTotalValid"
      @click="saveWeight()"
    >
      Save
    </b-button>

So, the validation rules are checked in both of the cases when the user changes something on the input or presses the save button and if there are any validation errors, those will be stored inside validationErrors. The issue is, initially when the input has non-valid values (0 in this case), and the user doesn't interact with any input element and the user directly clicks the save button; then the updated validationErrors is not displayed inside the table; but it's displayed correctly outside table.

This is the screenshot of my demo. The red-marked portions showed the initial values and the green-marked portions showed the updated value:

enter image description here

The validationErrors inside the table is updated only when the user change value of any inputs. I don't understand why outside of the table it works and inside of the table, it doesn't work! Is it because of populating fields of the table from computed? How can I solve this issue?

Here is my Code Demo


Solution

  • It's about creating reactive data properties in vue 2. If the property is not present in validationErrors object when the component is mounted, then to create it and make it reactive you should use this.$set() utility method. In validateInput method you should check if the property exists, then create it like mentioned:

    validateInput(data) {
      const id = data.item.id;
      const value = data.item.weight;
    
      if (!this.validationErrors[id])
        this.$set(this.validationErrors, id, "");
    
      // rest of the code
    }
    

    I should point that with your current code, even after the user updates the input, the validationErrors properties remain unreactive, the rerendering of the template being most likely triggered by the update of the table cell data.

    PS: Another method to overpass the reactivity issue, is to reasign the entire updated object. Like:

    validateInput(data) {
      const id = data.item.id;
      const value = data.item.weight;
      
      // Clone the validationErrors object
      let errors = JSON.parse(JSON.stringify(this.validationErrors))
    
      // update the errors object instead of this.validationErrors
    
      // ...
    
      // Reassign the updated object at the end
      this.validationErrors = errors
    }