Selecting a single row is Selecting all other rows in Vue.js - vue.js

I am calculating estimated cost. Selecting a product fetch the details about the product and showing it's description and price on the input box. And then upon clicking in add button a new row will appear for another selection. But the problem is the new row appears with the older row data. And changing a single row affects all other rows. Here is my code so far:
<tbody v-for="row in rows" :key="index">
<tr>
<td>
<select #change="selected" v-model="product_id" class="form-control" name="product_id" id="product_id">
<option value="">Select Product</option>
<option v-for="item in items" :value="item.id" v-text="item.product_name"></option>
</select>
</td>
<td>
<textarea type="text" v-model="product.product_details" name="product_details" id="product_details" class="form-control" rows="1" placeholder="Product Details"></textarea>
</td>
<td>
<input v-model.number="product.product_sellPrice" type="number" step="any" class="form-control" name="rate" id="rate" placeholder="Rate">
</td>
<td>
<input v-model.number="product.product_tax" type="number" step="any" class="form-control" name="tax" id="tax" placeholder="Tax">
</td>
<td>
<input v-model="quantity" type="number" class="form-control" name="quantity" id="quantity" placeholder="Quantity">
</td>
<td>
<input :value="total" type="number" step="any" class="form-control" name="total" id="total" placeholder="Total Price">
</td>
<td>
<button class="btn btn-secondary" v-on:click="addrow" >
<i class="fa fa-plus" aria-hidden="true"></i>
</button>
<button v-show="length > 1" class="btn btn-secondary" #click.prevent="removerow(index)">
<i class="fa fa-minus" aria-hidden="true"></i>
</button>
</td>
</tr>
</tbody>
Vue Part
<script>
export default{
props: ['products'],
data() {
return {
rows: [{
}],
items: this.products,
product: '',
product_id: '',
quantity: '',
index: '',
total_price: '',
}
},
methods: {
addrow: function (event) {
event.preventDefault();
this.rows.push({
});
},
removerow: function (index) {
this.rows.splice(index, 1);
},
selected(e){
var id = this.product_id;
console.log(id);
axios.get('/estimate/product/' + id)
.then((response)=>{
this.product = '';
this.product = response.data;
})
.catch((error) => {
console.log(error);
});
},
}
}
</script>
Where am I doing wrong?

key should be a unique identifier for each row. You have index defined in the data object and you use it for all the rows as key, that causes problems.
Also it is not recommended to use the row index as a key since it changes when you add/remove rows.
So you need to add some identifier to each row item and use it as the key, here is a simple example of how to do it:
<div id="vue-instance">
<ul>
<li v-for="(index,item) in inventory" :key="item.name">
{{ item.name }} - ${{ item.price }}
<button #click="add">Add</button>
<button #click="remove(index)">Remove</button>
</li>
</ul>
</div>
var vm = new Vue({
el: '#vue-instance',
data: {
counter: 1,
inventory: [{
name: 'MacBook Air',
price: 1000
}, {
name: 'MacBook Pro',
price: 1800
}, {
name: 'Lenovo W530',
price: 1400
}, {
name: 'Acer Aspire One',
price: 300
}]
},
methods: {
add: function() {
this.inventory.push({
name: 'item' + this.counter++,
price: this.counter * 1000
})
},
remove: function(index) {
this.inventory.splice(index, 1);
}
}
});
https://jsfiddle.net/1rancd5g/

Related

Vue shopping cart add to cart not working

I cannot see what i have done wrong, i am trying to add my products to my cart but i am not having any luck. How do i add to cart? i can figure out the delete and clear from understanding how to add to cart. i have read through some documentation and managed to get this far but now i am stuck.
i am not sure if i am targeting wrong or i have named something wrong. I used a normal array and it worked alright but i am trying to do it with an api now, does this change the methods?
<section>
<div class ="container" >
<div id="app" >
<div class="row">
<div class="col-md-12" >
<div class="card">
<div class="card-body">
<!-- Cart -->
<table>
<tr>
<th>Product</th>
<th>Price</th>
<th>Quantity</th>
<th>Total</th>
</tr>
<tr v-for="(product, index) in cart">
<td>{{ item.productName }}</td>
<td>{{ item.price }}</td>
<td>{{ item.quantity }}</td>
<td>{{ item.price * item.quantity }} Coins</td>
</tr>
</table>
</div>
</div>
<!-- Shop -->
<div class="row">
<div class="col-md-3" v-for="product in products" :key="product.id" >
<div class="card" >
<div class="card-body">
<div class="product">
<img :src="product.productImage" alt="Product Img">
<h4 class="text-info">{{ product.productName }}</h4>
<h4 class="text-muted">{{ product.price }} Coins</h4>
<p class="text-muted">{{ product.productDescription }}</p>
<input type="number" name="quantity" class="form-control" value="1">
<input type="hidden" name="productName" value="{{productID}}">
<input type="hidden" name="price" value="{{productID.price}}">
<input type="submit" name="add_to_cart" #click="addToCart" class="btn btn-primary btn-block fa-lg gradient-custom-2 mb-3" value="Add to Cart">
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<script>
var app = new Vue({
el: '#app',
data: {
products: '',
cart: []
},
methods: {
getproducts: function () {
axios.get('http://localhost:8888/DevsDev2022/productAPI/products')
.then(function (response) {
app.products = response.data;
})
.catch(function (error) {
console.log(error);
});
},
addToCart: function (product) {
var cartItem = this.cart.find(item => item.productName === product.productName);
if (cartItem) {
cartItem.quantity++;
} else {
this.cart.push({
productName: product.productName,
price: product.price,
quantity: 1
});
}
},
removeFromCart: function (product) {
var cartItem = this.cart.find(item => item.productName === product.productName);
if (cartItem.quantity > 1) {
cartItem.quantity--;
} else {
this.cart.splice(this.cart.indexOf(cartItem), 1);
}
},
clearCart: function () {
this.cart = [];
}
},
mounted: function () {
this.getproducts();
}
});
</script>
You are missing your template tag around your template
Please take a look at following snippet:
new Vue({
el: '#app',
data() {
return {
products: [{id: 1, productName: 'aaa', price: 25, productDescription: 'rrrrrrr'}, {id: 2, productName: 'bbb', price: 35, productDescription: 'eeeee'}],
cart: []
}
},
methods: {
getproducts() {
/*axios.get('http://localhost:8888/DevsDev2022/productAPI/products')
.then(function (response) {
app.products = response.data;
})
.catch(function (error) {
console.log(error);
});*/
},
addToCart(product) {
var cartItem = this.cart.find(item => item.productName === product.productName);
if (cartItem) {
cartItem.quantity++;
} else {
this.cart.push({
productName: product.productName,
price: product.price,
quantity: 1
});
}
},
removeFromCart(product) {
var cartItem = this.cart.find(item => item.productName === product.productName);
if (cartItem.quantity > 1) {
cartItem.quantity--;
} else {
this.cart.splice(this.cart.indexOf(cartItem), 1);
}
},
clearCart() {
this.cart = [];
}
},
mounted: function () {
this.getproducts();
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<section>
<div class ="container" >
<div class="row">
<div class="col-md-12" >
<div class="card">
<div class="card-body">
<!-- Cart -->
<table>
<tr>
<th>Product</th>
<th>Price</th>
<th>Quantity</th>
<th>Total</th>
</tr>
<tr v-for="(product, index) in cart">
<td>{{ product.productName }}</td>
<td>{{ product.price }}</td>
<td>{{ product.quantity }}</td>
<td>{{ product.price * product.quantity }} Coins</td>
</tr>
</table>
</div>
</div>
<!-- Shop -->
<div class="row">
<div class="col-md-3" v-for="product in products" :key="product.id" >
<div class="card" >
<div class="card-body">
<div class="product">
<img :src="product.productImage" alt="Product Img">
<h4 class="text-info">{{ product.productName }}</h4>
<h4 class="text-muted">{{ product.price }} Coins</h4>
<p class="text-muted">{{ product.productDescription }}</p>
<input type="number" name="quantity" class="form-control" value="1">
<input type="hidden" name="productName" :value="product.name">
<input type="hidden" name="price" :value="product.price">
<input type="submit" name="add_to_cart" #click="addToCart(product)" class="btn btn-primary btn-block fa-lg gradient-custom-2 mb-3" value="Add to Cart">
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
</div>

Validating dynamic array in vuelidate

I am very new to vue and I am trying to validate an array using vuelidate which is used to render a dynamic table. The problem is with the validation() method as I can comprehend.
According to vuelidate docs, https://vuelidate.js.org/#sub-collections-validation, the $each method supports array. When I use it, the validation never fails. However, when I omit $each, and, try to validate first index of the array, it returns as corrected - validation fails.
To be more precise, I am trying to validate each added row(s), and if there's a problem with the validation, it'd add a class to the affected row.
Component App.vue,
This is the HTML code:
<template>
<div>
<br>
<div class="text-center">
<button type="button" class="btn btn-outline-primary" #click="addRow()">Shto</button>
</div>
<br>
<p
v-for="error of v$.$errors"
:key="error.$uid"
>
<strong>{{ error.$validator }}</strong>
<small> on property</small>
<strong>{{ error.$property }}</strong>
<small> says:</small>
<strong>{{ error.$message }}</strong>
</p>
<form name="articles" #submit.prevent="submitForm">
<table class="table table-hover table-responsive">
<thead class="thead-inverse">
<tr>
<th>Nr.</th>
<th class="col-3">Numri</th>
<th class="col-4">Përshkrimi</th>
<th class="col-1">Sasia</th>
<th class="col-1">Çmimi</th>
<th class="col-2">Shuma</th>
<th></th>
</tr>
</thead>
<tbody>
<tr v-for="(item, idx) in items_table" :key="item">
<td>
{{idx+1}}
</td>
<td>
<input v-model="item.part_no" name="part_no[]" class="form-control" type="text" />
</td>
<td>
<textarea v-model="item.part_name" name="part_name[]" class="form-control" type="text"></textarea>
</td>
<td>
<input v-model="item.part_qty" name="part_qty[]" class="form-control " type="number" step="0.01">
</td>
<td>
<input v-model="item.part_price" name="part_price[]" class="form-control" type="number" step="0.01">
</td>
<td>
<input :value="item.part_total" name="part_total[]" class="form-control text-center border-0" style="background-color: transparent; font-size: 18 px;" type="text" disabled>
</td>
<td>
<button type="button" #click="deleteRow(idx)" class="btn btn-danger btn-md btn-block">X</button>
</td>
</tr>
</tbody>
</table>
<div class="text-center">
<button type="submit" name="" id="" class="btn btn-primary btn-lg btn-block">Valido</button>
</div>
</form>
<div class="text-center">
<textarea name="" id="verbose_log" cols="70" rows="15" refs="logg"></textarea>
</div>
</div>
</template>
This is the content from script tag:
<script>
import useVuelidate from '#vuelidate/core'
import { required } from '#vuelidate/validators'
export default {
name: 'App',
setup: () => ({ v$: useVuelidate() }),
validation() {
return {
items_table: {
$each: {
part_no: {
required,
}
}
},
}
},
data() {
return {
items_table: [
{
part_no: '', part_name: '', part_qty: '', part_price: '', part_total: ''
}
],
items_subtotal: 0.00,
items_total: 0.00,
}
},
methods: {
deleteRow(index) {
if(this.items_table.length == 1) {
this.items_table.splice(index, 1);
this.items_subtotal = 0.00;
this.items_total = 0.00;
this.addRow();
} else if(this.items_table.length > 0) {
this.items_table.splice(index, 1);
}
},
addRow() {
this.items_table.push({part_no: '', part_name: '', part_qty: '', part_price: '', part_total: ''});
},
submitForm() {
this.v$.$touch();
//if (this.v$.$error) return;
console.log(this.v$);
document.getElementById('verbose_log').innerHTML = JSON.stringify(this.$data, null, 4);
}
},
computed: {
//
}
}
</script>
For the sake of clarity, I have excluded two methods which calculate line total and the total itself.

Search input in dynamic table

I'm new to Vue.js, so thanks for the help!!
I'm trying to implement a Search input for each column that filters the results for the user
this is my HTML file:
<div id="table" >
<div class=" container jumbotron mt-3 ">
<enter-data :resources="resources" ></enter-data>
</div>
<div class=" container jumbotron mt-1 ">
<table-list :resources="resources" :heads="heads" ></table-list>
</div>
</div>
And this is my JS file:
Vue.component('table-list', {
props: ['resources', 'heads'],
data () {
return {
selected: [],
selectAll: true,
userIds: [],
searchQuery: ''
}
},
computed: {
filteredResources: function () {
if (this.searchQuery) {
return this.resources.filter(item => {
// return this.item.value.toLowerCase().includes(this.searchQuery.toLowerCase());
// return this.searchQuery.toLowerCase().split(' ').every(v => item.value.toLowerCase().includes(v))
return this.item.FName.startsWith(this.searchQuery)
// return item.indexOf(this.searchQuery)>= 0;
})
} else {
return this.resources
}
}
},
methods: {
sortTable: function (resource) {
this.resources.sort(function (a, b) {
if (a[resource.value] > b[resource.value]) {
return 1
} else if (a[resource.value] < b[resource.value]) {
return -1
}
return 0
})
}
},
template: `
<div class="w-100 text-center d-flex justify-content-center flex-wrap">
<div class="w-75">
<!-- <input type="text" placeholder="search.." v-modle="searchQuery" >-->
<table class="table table-striped">
<thead>
<tr>
<th>
<input type="checkbox">
</th>
<th v-for="resource in heads" class="cursor" #click="sortTable(resource)">
<div />
{{ resource.name }}
</th>
</tr>
</thead>
<tbody>
<tr>
<td />
<td />
<td>
<input v-mode="searchQuery" type="text">
</td>
<td>
<input type="text">
</td>
</tr>
<tr v-for="resource in filteredResources">
<td>
<input type="checkbox">
</td>
<td v-for="column in heads">
{{ resource[column.value] }}
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
`
})
var app = new Vue({
el: '#table',
data: {
resources: [
{ 'id': 0, 'FName': 'Setareh', 'LName': 'Pashapour', 'Extra': 'Alishah' },
{ 'id': 1, 'FName': 'Soheil', 'LName': 'Pashapour' },
{ 'id': 2, 'FName': 'Simin', 'LName': 'Hajizadeh' },
{ 'id': 3, 'FName': 'Nouyan', 'LName': 'Arman' },
{ 'id': 4, 'FName': 'Abed', 'LName': 'Hamrang' },
{ 'id': 5, 'FName': 'Navid', 'LName': 'Ahanj' }
],
heads: [{ name: '#', value: 'id' }, { name: 'First Name', value: 'FName' }, { name: 'Last Name', value: 'LName' }]
}
})
Unfortunately it doesn't work
searchQuery data doesn't have any impact to my filter computed and nothing happend if i type any thing on it
my Any suggestions?
You're using the wrong directive name for form input binding. Change from v-mode to v-model for below line.
<input v-mode="searchQuery" type="text">

Disable/Enable input for each item from a loop VueJS

I try to enable/disable each input from a loop. The problem is that my method it works just after refresh. After I modify something in code and save, then the input works.
<tr v-for="(item, index) in customer_names" :key="item.id">
<td>
<input :disabled="!item.disabled"
v-model="item.name"
type="text"
</td>
</tr>
<div class="edit_mode"
:class="{'display-none':!item.disabled}">
<i class="fa fa-save" #click="disableInput(index)" aria-hidden="true"></i>
</div>
<div class="edit_mode"
:class="{'display-none':item.disabled}">
<i class="fa fa-edit" #click="enableInput(index)" aria-hidden="true"></i>
</div>
props:['customer_names'],
data(){
return{
disabled: [],
}
}
enableInput(index){
console.log('enableInput',this.customer_names[index].disabled);
this.customer_names[index].disabled = false;
},
disableInput(index){
console.log('disabeInput',this.customer_names[index].disabled);
this.customer_names[index].disabled = true;
}
I didn't fully understand your problem. I deduced that you might want to enable or disable the text fields that you create from the data provided. If this is still not what you meant, correct your question by pasting more source code, and explain your problem in more detail.
Vue.component("custom", {
template: "#custom-template",
props: ["customer_names"],
methods: {
enableInput(item) {
item.disabled = false;
},
disableInput(item) {
item.disabled = true;
},
toggleInput(item) {
item.disabled = !item.disabled;
}
}
});
new Vue({
data() {
return {
items: [
{ name: "fus", disabled: false },
{ name: "ro", disabled: false },
{ name: "dah", disabled: true }
]
};
}
}).$mount("#app");
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<custom :customer_names="items" />
</div>
<script type="text/x-template" id="custom-template">
<table cellpadding=5>
<tr>
<th>Input</th>
<th>Version 1</th>
<th>Version 2</th>
</tr>
<tr v-for="item in customer_names" :key="item.id">
<td>
<input :disabled="item.disabled" v-model="item.name" type="text" />
</td>
<td>
<button #click="item.disabled = false">E</button>
<button #click="item.disabled = true">D</button>
<button #click="item.disabled = !item.disabled">T</button>
</td>
<td>
<button #click="enableInput(item)">E</button>
<button #click="disableInput(item)">D</button>
<button #click="toggleInput(item)">T</button>
</td>
</tr>
</table>
</script>

In Vuejs select list box getting same value for all rows

I am creating 4 rows with 3 columns (2 text box and 1 select box). What ever the value selected in select component getting updated with other 4 rows also.
Here is my code
<tr
v-for="ingredient in ingredients"
v-bind:key="ingredient.id"
>
<td>
<input
type="text"
class="form-control"
id=""
v-model="ingredient.item_name"
/>
</td>
<td>
<input
type="text"
class="form-control"
id=""
v-model="ingredient.quantity"
/>
</td>
<td>
<select v-model="selected">
<option v-for="unit in units" v-bind:key="unit" > {{ unit }} </option>
</select>
</td>
</tr>
Here is my data
export default {
data() {
return {
selected:[],
units: ['gms', 'pound', 'kgs']
};
},
should works like this:
new Vue({
el: '#app',
data: {
ingredients: [
{ id: 1, item_name: 'foo', quantity: 0, selected: '' },
{ id: 2, item_name: 'bar', quantity: 0, selected: '' },
],
units: ['gms', 'pound', 'kgs']
}
})
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app">
<table>
<tr
v-for="ingredient in ingredients"
v-bind:key="ingredient.id"
>
<td>
<input
type="text"
class="form-control"
v-model="ingredient.item_name"
/>
</td>
<td>
<input
type="number"
class="form-control"
v-model="ingredient.quantity"
/>
</td>
<td>
<select v-model="ingredient.selected">
<option v-for="unit in units" v-bind:key="unit" > {{ unit }} </option>
</select>
</td>
</tr>
</table>
<pre>{{ ingredients }}</pre>
</div>