Rimuru Tempest
Rimuru Tempest

Reputation: 675

Change v-model value without changin the actual data

So i've this data

data: () => ({
    products: [
        { id: 1, name: "Prod 1", price: 2, stock: 5 },
        { id: 2, name: "Prod 2", price: 3, stock: 6 }
    ]
})

Template

<table>
    <thead>
        <tr>
            <th>id</th>
            <th>name</th>
        </tr>
    </thead>
    <tbody>
        <tr v-for="product in products" :key="product.id">
            <td>{{ product.id }}</td>
            <td>{{ product.name }}</td>
            <td>
                <input
                type="text"
                class="form-control"
                v-model="product.price"
                @paste.prevent
              />
            </td>
            <td>
                <input
                type="text"
                class="form-control"
                maxlength="999"
                v-model="product.stock"
                @paste.prevent
                @keypress="onlyNumber($event)"
                @input="handleInputStock($event.target.value)"
                @blur="updateStock($event.target.value, product.id)"
              />
            </td>
        </tr>
    </tbody>
</table>

So what I want is that when the user hit delete/backspace from the stock input field the value cannot be empty (blank) or it must be greater than or equal to zero. but without changing the products.stock value. this is because I need the product.stock value to compare with the changed value (stock input field) before sending to the server. So if stock value is equal to product.stock don't send to server otherwise send and update stock value.

so here's what i've done so far.

prevent the stock value empty but not working

handleInputStock(value) {
  return +value.replace(/[^0-9]/g, "");
},

update stock

updateStock(stock, productId) {
  const productStock = this.products.find(product => product.id == productId).stock;

  if (!(stock == productStock)) {
    // do ajax
  }
},

onlyNumber

onlyNumber(e) {
  const charCode = e.which ? e.which : event.keyCode;
  if (charCode > 31 && (charCode < 48 || charCode > 57)) {
    e.preventDefault();
  }
},

Upvotes: 0

Views: 2373

Answers (3)

Jesse Reza Khorasanee
Jesse Reza Khorasanee

Reputation: 3491

Can we have two versions of products? One for the server, one for v-models.

var server_products = [
    { id: 1, name: "Prod 1", stock: 5 },
    { id: 2, name: "Prod 2", stock: 6 }
]

//...

data: () => ({
    products = server_products
})
updateStock(stock, productId) {
  server_products.forEach((product) => {
      if(product.id === productId && stock !== product.stock){
        product.stock = stock
        // do ajax
      }
  })
},
//...

If not than you can use vue's watch property so vue finds changes to the array for you.

//...
data: () => ({
    products: [
        { id: 1, name: "Prod 1", stock: 5 },
        { id: 2, name: "Prod 2", stock: 6 }
    ]
}),
watch: {
    'products': {
        handler: function(newValue) {
            // do ajax
        },
        deep: true
    }
}
//...

Upvotes: 1

Aslam H
Aslam H

Reputation: 1801

The best way is to create a ProductComponent and watch every product separately inside its own component, as shown below:

Product.vue

<ProductComponent 
   v-for="product in products" 
   :product="product" 
   :key="product.id" />

ProductComponent.vue

<template>
   <tr>
      <td>{{ product.name }}</td>
      <td>
          <input
            type="text"
            class="form-control"
            v-model="product.price"
            @paste.prevent
          />
        </td>
        <td>
            <input
            type="text"
            class="form-control"
            maxlength="999"
            v-model="product.stock"
            @paste.prevent
            @keypress="onlyNumber($event)"
            @blur="updateStock($event.target.value, product.id)"
          />
       </td>
   </tr>
</template>

<script>
export default {
  props: {
    product: {
      type: Object,
      default: {},
    },
  },

  data: () => ({ actual_stock: "" })
  
  // this is for handle stock cannot be empty or GTE:0
  // also you dont need handleInputStock anymore
  watch: {
     product: {
        handler(val) {
           this.actual_stock = val.stock;
        },
        immediate: true,
     },
     "product.stock": function (newVal, oldVal) {
        this.product.stock = +newVal;
     },
  },

  methods: {
     updateStock(stock, productId) {
        if (!(stock == this.actual_stock)) {
           // do ajax
        }
     }
  }
}
</script>

If you want to handle it on parent side, you may use $emit to send an event upwards.

Upvotes: 1

CRUD DS
CRUD DS

Reputation: 466

Personally this feels like a higher level question to which your flow of product editing needs tweaking. Here is what I can think of:

  1. User enters all the information.
  2. User hits submit button.
  3. Check whether of not the stock count is empty or 0.
  4. Return an error message if it is.
  5. Submit and update otherwise.

It might be worth looking into vuelidate that handles such validation in JavaScript. Meanwhile, we are also coming up with a tool called CRUDS DS (a WIP) that handles such situation with ease.

Upvotes: 1

Related Questions