In Vuejs select list box getting same value for all rows - vue.js

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>

Related

I'm having a render error in my nuxt page with a property that cannot be read

I'm trying to add rows to a table dynamically with a method in nuxt.js, but the prop that I have defined doesn't work...
I receive this error from the console:
Property or method "invoice_customer" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property.
I'm trying with this template:
<template>
<div>
<h3>Customers</h3>
<input type="text" v-model="search" placeholder="Search for names..." />
<table v-if="customers.length > 0">
<tr>
<th>name</th>
<th>address</th>
</tr>
<tr v-for="customer in customers" :key="customer.id">
<td>{{ customer.name }}</td>
<td>{{ customer.address }}</td>
</tr>
</table>
<div class="center">
<p>Customer</p>
<select v-if="customers.length > 0">
<option v-for="customer in customers" :key="customer.id">
{{ customer.name }}
</option>
</select>
</div>
<table v-if="invoice_customers.length > 0">
<tr>
<th>Id</th>
<th>Amount</th>
<th>Expire Date</th>
<th>Note</th>
</tr>
<tr
v-for="invoice_customers in invoice_customer"
:key="invoice_customers.id"
>
<td>
<input
type="number"
name=""
id=""
v-model="invoice_customer.amount"
/>
</td>
<td>
<b-form-datepicker
id="example-datepicker"
v-model="invoice_customer.expire_date"
class="mb-2"
></b-form-datepicker>
</td>
<td>
<input type="text" name="" id="" v-model="invoice_customer.note" />
</td>
</tr>
</table>
<b-button variant="outline-success" #click="addNewInstallment">+ Add New</b-button>
</div>
</template>
and this is my script:
<script>
export default {
name: "IndexPage",
data() {
return {
selected: "",
customers: [],
search: "",
invoice_customers: [{
id: 0,
amount: 0,
expire_date: "",
note: "",
}],
};
},
props: {
},
async mounted() {
const response = await this.$axios.$get("/api/customers");
this.customers = response.data;
return this.customers.filter((p) => {
// return true if the product should be visible
// in this example we just check if the search string
// is a substring of the product name (case insensitive)
return p.name.toLowerCase().indexOf(this.search.toLowerCase()) != -1;
});
},
methods: {
addNewInstallment() {
this.invoice_customers.push({
id: 0,
amount: 0,
expire_date: "",
note: "",
});
}
}
}
</script>
invoice customer is defined in v-for cycle
You inverted the order in your v-for, it should be
v-for="invoice_customer in invoice_customers"

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.

TrandingView Widget in Vue view

I'm doing a view which shows a Vue component, this view has to display 4 tradingview graphs corresponding to 4 cryptocurrencies (BTC, ETH, EOS and XRP). The problem is that I can only show a single graphic of the four and only shows me the graph of the last cryptodivisa of the array.
The view should show this:
But it only shows me a single graphic:
This is the component code that does everything:
<template>
<div class="container">
<br>
<form class="form-inline" action="POST" v-on:submit.prevent>
<table>
<tr>
<td style="font-weight:bold; width:200px;">Exchange</td>
<td style="width:250px;">
<input class="form-check-input"
type="radio"
id="bittrex"
name="bittrex"
value="bittrex"
v-model="queryExchange.picked"/> Bittrex
<input class="form-check-input"
type="radio"
id="binnace"
name="binance"
value="binance"
v-model="queryExchange.picked"
checked> Binance
</td>
</tr>
<tr><td colspan="4" style="height:20px"></td></tr>
<tr>
<td style="font-weight:bold; width:200px;">Intervalo:</td>
<td>
<select id="Intervalo" name="Intervalo" v-model="queryExchange.interval">
<option value="W">1 Semana</option>
<option value="D">1 Día</option>
<option value="240">4 Hrs</option>
<option value="60">1 Hr</option>
<option value="30">30 min</option>
<option value="15">15 min</option>
<option value="5">5 min</option>
</select>
</td>
</tr>
<tr><td colspan="4" style="height:20px"></td></tr>
<tr>
<td style="font-weight:bold; width:200px;">
<button type="button" class="btn btn-primary"
v-on:click="queryForm()">
Mostrar
</button>
</td>
</tr>
<tr><td colspan="4" style="height:20px"></td></tr>
</table>
</form>
<div id="contenedor">
</div>
</div>
<script>
export default {
data () {
return {
queryExchange: {
picked: 'binance',
interval: '60',
},
cryptoCurrency: ['BTC', 'ETH', 'EOS', 'XRP']
}
},
methods: {
queryForm(){
let exchangeSelect = this.queryExchange.picked.toUpperCase();
let intervalSelect = this.queryExchange.interval;
this.cryptoCurrency.forEach(element => {
new TradingView.widget(
{
"width": 400,
"height": 300,
"symbol": exchangeSelect+':'+element+'BTC',
"interval": intervalSelect,
"timezone": "Etc/UTC",
"theme": "Dark",
"style": "1",
"locale": "en",
"toolbar_bg": "#f1f3f6",
"allow_symbol_change": true,
"enable_publishing": false,
"save_image": false,
"hideideas": true,
"container_id": "contenedor"
}
);
});
}
}
}
</script>
So far I can not find the mistake, I occupy Laravel 5.6 and Vuejs 2

Selecting a single row is Selecting all other rows in 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/

Update value based on other input fields

I have created a component for creating dynamic rows with input fields for invoice items.
<template>
<tr>
<td class="p-0" align="center"><label class="m-0">{{ row.id + 1 }}</label></td>
<td class="p-0" align="center">
<input type="text" v-model="row.name" name="product_name[]" value="" class="form-control form-control-sm border-0" placeholder="Product Name ...">
</td>
<td class="p-0" align="center">
<input type="text" v-model="row.hsn" name="product_hsn[]" value="" class="form-control form-control-sm border-0" placeholder="900574">
</td>
<td class="p-0" align="center">
<input type="number" v-model="row.qty" name="product_qty[]" value="1" class="form-control form-control-sm border-0" placeholder="0">
</td>
<td class="p-0">
<input type="number" v-model="row.rate" name="product_rate[]" value="" class="form-control form-control-sm border-0" placeholder="0.00">
</td>
<td class="p-0">
<input type="number" v-model="row.amount" name="product_amount[]" readonly="readonly" v-bind:value="row.qty * row.rate" class="form-control form-control-sm border-0" placeholder="0.00">
</td>
</tr>
</template>
<script>
export default {
props: ['row'],
data() {
return {
slabs: [0, 0.25, 3, 5, 12, 18, 28],
}
},
mounted() {}
}
</script>
But the value of v-bind:value="row.qty * row.rate" is not getting update.
This is how i am using this component:
<tbody>
<tr v-for="(row, index) in invoiceRows" v-bind:row="row" v-bind:key="+row" is="invoice-product" v-on:remove="invoiceRows.splice(index, 1)"></tr>
</tbody>
<tfoot class="hidden-print">
<tr>
<td class="p-0" colspan="18">
<i class="ion-ios-plus" style="font-size:18px;"></i> Add Line
</td>
</tr>
</tfoot>
App.js
....
computed: {
invoiceRows() {
return this.$store.state.items
}
},
And this is my vuex that manages all the shared states:
export const store = new Vuex.Store({
state: {
activeType: null,
stateList: [],
companyState: [],
items: [{
id: 0, name: null, hsn: null, qty: 1, rate: null, discount: 0,
taxSlab: 0, cessRate: null, cessAmount: null
}],
billToSupplyState: [],
shipToSupplyState: []
},
actions,
mutations
})
But if i remove v-model="row.amount" from my component then it will work. Can anyone please help and let me know what i have missed?