How to update the value in particular TextBox in Table row using change event in Vue Js - vue.js

While change the Quantity in the particular TextBox in table row, need to update the calculated Value (Unit Price * Quantity) in another TextBox in the same row.
Template
<template>
<div class="container col-md-12">
<h3>Product List</h3>
<table class="table">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Product Name</th>
<th scope="col">Net Content</th>
<th scope="col">MRP</th>
<th scope="col">Discount</th>
<th scope="col">Unit Price</th>
<th scope="col">Quantity</th>
<th scope="col">Value</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
<template v-for="group in productgroups">
<tr>
<td colspan="9"><b>{{ group.groupName }}</b></td>
</tr>
<tr v-for="(product, index) in filteredProducts(group.id)" v-bind:key="product.id">
<td class="text-center" scope="row">{{ index + 1 }}</td>
<td>{{ product.productName }}</td>
<td class="text-right">{{ product.netContent }} {{ product.uommasters.uomId }}</td>
<td class="text-right">{{ product.mrp }}</td>
<td class="text-right">{{ product.discountAmount }}</td>
<td class="text-right">
<input type="number" class="form-control" v-bind:id="`salePrice${product.id}`" required name="salePrice" v-model="product.salePrice" readonly />
</td>
<td>
<input type="number" class="form-control" v-bind:id="`quantity${product.id}`" required name="quantity"
#change="getValue(product.id, product.salePrice, product.quantity)" v-model="product.quantity" />
</td>
<td>
<input type="number" class="form-control" :id="'productValue'+product.id" required name="productValue" v-model="product.productValue" readonly />
</td>
</tr>
</template>
</tbody>
</table>
</div>
Script
<script>
import moment from 'moment';
import ProductDataService from "../../services/ProductDataService";
import ProductgroupDataService from "../../services/ProductgroupDataService";
export default {
name: "product-index",
data() {
return {
products: [],
productgroups: [],
productTotal: 0,
};
},
methods: {
getValue(productId, price, qty) {
this.productTotal = price * qty;
alert(this.productTotal);
this.product.discountAmount = this.productTotal;
alert(this.product.discountAmount);
},
filteredProducts(groupId) {
var fproducts = this.products.filter(function(product) {
return product.groupCode == groupId;
});
return fproducts;
},
retreiveProducts() {
ProductDataService.getAll()
.then(response => {
this.products = response.data;
})
.catch(e => {
alert(e);
console.log(e);
})
},
retreiveGroups() {
ProductgroupDataService.getAll()
.then(response => {
this.productgroups = response.data;
})
.catch(e => {
console.log(e);
});
},
},
created() {
this.retreiveProducts();
},
mounted() {
this.retreiveGroups();
},
};
I have populated the products from database using API by grouping and need to update the value while change the quantity using onchange event.

Related

Vue.js making object.length value reactive

Trying to display Total records. Students.length works the first time on page load thanks to the created() method. However, calling filteredStudents(), is out of date. What is the easiest way to make this reactive?
<template>
<div class="d-inline-flex flex-row p-4 col-2">
Total record(s): {{ recordCount }}
</div>
<table class="table border table-striped table-hover">
<thead class="bg-secondary">
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Email</th>
</tr>
</thead>
<tbody>
<tr v-for="student in filteredStudents()" :key="student._id">
<td>{{ student.firstName }}</td>
<td>{{ student.lastName }}</td>
<td>{{ student.email }}</td>
</tr>
</tbody>
</table>
</template>
<script>
import MixinCommon from '#/mixins/common.js'
export default {
data() {
return {
searchTerm: '',
Students: [],
studentcount: 0
}
},
created() {
this.Students = this.getSutdentList()
},
computed: {
recordCount() {
return this.Students.length
}
},
mixins: [MixinCommon],
methods: {
filteredStudents() {
return this.searchStudentList(this.searchTerm.toUpperCase(), this.Students)
},
}
}
</script>
I don't know the implementation of the searchStudentsList method, but you could try using the filteredStudents as a computed property, or making a watch property on the searchTerm in order to make the search again:
Using computed:
<template>
<div class="d-inline-flex flex-row p-4 col-2">
Total record(s): {{ recordCount }}
</div>
<table class="table border table-striped table-hover">
<thead class="bg-secondary">
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Email</th>
</tr>
</thead>
<tbody>
<tr v-for="student in filteredStudents" :key="student._id">
<td>{{ student.firstName }}</td>
<td>{{ student.lastName }}</td>
<td>{{ student.email }}</td>
</tr>
</tbody>
</table>
</template>
<script>
import MixinCommon from '#/mixins/common.js'
export default {
data() {
return {
searchTerm: '',
Students: [],
studentcount: 0
}
},
created() {
this.Students = this.getSutdentList()
},
computed: {
recordCount() {
return this.Students.length
},
filteredStudents() {
return this.searchStudentList(this.searchTerm.toUpperCase(), this.Students)
},
},
mixins: [MixinCommon],
}
</script>
Using watch property:
<template>
<div class="d-inline-flex flex-row p-4 col-2">
Total record(s): {{ recordCount }}
</div>
<table class="table border table-striped table-hover">
<thead class="bg-secondary">
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Email</th>
</tr>
</thead>
<tbody>
<tr v-for="student in filteredStudents" :key="student._id">
<td>{{ student.firstName }}</td>
<td>{{ student.lastName }}</td>
<td>{{ student.email }}</td>
</tr>
</tbody>
</table>
</template>
<script>
import MixinCommon from '#/mixins/common.js'
export default {
data() {
return {
searchTerm: '',
Students: [],
filteredStudents: [],
studentcount: 0
}
},
created() {
this.Students = this.getSutdentList()
this.filteredStudents = this.searchStudentList(this.searchTerm.toUpperCase(), this.Students)
},
computed: {
recordCount() {
return this.Students.length
}
},
watch: {
searchTerm(newValue) {
this.filteredStudents = this.searchStudentList(newValue.toUpperCase(), this.Students)
}
}
mixins: [MixinCommon],
}
</script>

In vuejs, quoting variables in HTML element attribute doesn't work

Here is the code:
<tr v-for="(item, index) in detail" :key="item.name" class="[[ item.name ]]">
<td>[[ index + 1 ]]</td>
<td>[[ item.name ]]</td>
The rendered HTML looks like this:
<tr class="[[ item.name ]]">
<td>1</td>
<td>Job</td>
</tr>
<tr class="[[ item.name ]]">
<td>2</td>
<td>Jesse</td>
</tr>
<tr class="[[ item.name ]]">
<td>3</td>
<td>Wazert</td>
</tr>
The class="[[ item.name ]]" just don't change. What I expect is:
<tr class="Job">
<td>1</td>
<td>Job</td>
</tr>
<tr class="Jesse">
<td>2</td>
<td>Jesse</td>
</tr>
<tr class="Wazert">
<td>3</td>
<td>Wazert</td>
</tr>
How should I fix it?
First thing Square bracket not worked in vue.js you need to use interpolation for binding the data dynamically.
So you need to use like For Example
HTML
<table border="1">
<tr v-for="(item, index) in detail" :key="item.name" :class="item.name">
<td>{{ index + 1 }}</td>
<td>{{ item.name }}</td>
</tr>
</table>
JS
data: function () {
return {
detail: [{ name: "Job" }, { name: "Jesse" }, { name: "Wazert" }],
};
},
Here you can play with code
You need to use class-binding and interpolate the data:
new Vue({
el: "#app",
data: () => ({
detail: [ { name: "Job" }, { name: "Jesse" }, { name: "Wazert" } ]
})
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<table>
<tr v-for="(item, index) in detail" :key="item.name" :class="item.name">
<td>{{index + 1}}</td>
<td>{{item.name}}</td>
</tr>
</table>
</div>

Can't add new item using v-model in Vue JS

I am learning Vue.
Now, I am trying to add data with the price and finally, it calculates total price:
Here is the HTML
<div id="app">
<form #submit.prevent="addItem">
<table border="1" cellpadding="10" width="300">
<tr>
<td colspan="2"><strong>Add New Item</strong></td>
</tr>
<tr>
<td>
<input type="text" name="" v-model="newItem" placeholder="Item Name">
</td>
<td>
<input type="number" name="" v-model="newItemPrice" placeholder="Item Price">
</td>
</tr>
</table>
</form>
<br>
<table border="1" cellpadding="10" width="400">
<tr>
<th>Item Name</th>
<th>Item Price</th>
</tr>
<tr v-for="(item, index) in items" :key="index">
<td>{{ item.name }}</td>
<td><input type="number" name="" v-model="item.price"></td>
<td><button #click="removeItem(index)">X</button></td>
</tr>
<tr>
<td>Total</td>
<td><strong>{{ total }}</strong></td>
</tr>
</table>
</div>
Here is the Vue Instance:
new Vue({
el : '#app',
data : {
items: [
{ name: 'Rice', price : 12.60 },
{ name: 'Oil', price : 22.00 },
{ name: 'Mango', price : 32.50 },
{ name: 'Orange', price : 42.00 },
],
newItem : '',
newItemPrice : '',
},
computed: {
total() {
var total = 0;
this.items.forEach( item => {
total += parseFloat( item.price );
})
return total;
}
},
methods: {
addItem() {
this.items.push({
name: this.newItem,
price: 0
});
},
removeItem( index ) {
this.items.splice( index, 1 )
}
}
});
You can see it's by default showing item name and price. I want to add new item using the v-model called newItem But It's not adding the new item to the table
BUT
If I remove the Item Price column I mean this line:
<td>
<input type="number" name="" v-model="newItemPrice" placeholder="Item Price">
</td>
then it's adding the new item perfectly :(
can you tell me what's wrong here?
See two issues with the fiddle:
There is no way to submit the form data
When pushing the price field was not added to the object
After fixing both of them it works well in this fiddle.
This happens because of browser implementation. As mentioned in W3C Specs:
When there is only one single-line text input field in a form, the user agent should accept Enter in that field as a request to submit the form.
But in case of multiple elements, the enter keypress does not trigger the form submit and thus you get this behaviour.
To resolve this you can simply use #keyup.enter.prevent="addItem" to listen to the enter keypress on each input and call the addItem() function like:
new Vue({
el: '#app',
data: {
items: [{name:"Rice",price:12.6},{name:"Oil",price:22},{name:"Mango",price:32.5},{name:"Orange",price:42}],
newItem: '',
newItemPrice: null,
},
computed: {
total() {
var total = 0;
this.items.forEach(item => {
total += parseFloat(item.price);
})
return total;
}
},
methods: {
addItem() {
this.items.push({
name: this.newItem,
price: 0
});
},
removeItem(index) {
this.items.splice(index, 1)
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" rel="stylesheet">
<div id="app">
<form #submit.prevent="addItem">
<table border="1" cellpadding="10" width="300">
<tr>
<td colspan="2"><strong>Add New Item</strong></td>
</tr>
<tr>
<td>
<input type="text" name="" v-model="newItem" placeholder="Item Name"
#keyup.enter.prevent="addItem">
</td>
<td>
<input type="number" name="" v-model="newItemPrice" placeholder="Item Price"
#keyup.enter.prevent="addItem">
</td>
</tr>
</table>
</form>
<br>
<table border="1" cellpadding="10" width="400">
<tr>
<th>Item Name</th>
<th>Item Price</th>
</tr>
<tr v-for="(item, index) in items" :key="index">
<td>{{ item.name }}</td>
<td><input type="number" name="" v-model="item.price"></td>
<td><button #click="removeItem(index)">X</button></td>
</tr>
<tr>
<td>Total</td>
<td><strong>{{ total }}</strong></td>
</tr>
</table>
</div>
You should put a new line in your form, my suggestion is to put just above the close form tag </form>:
<input type="submit" value="add">
Another fix to do is in your methods addItem()
addItem() {
this.items.push({
name: this.newItem,
price: this.newItemPrice
});
}
Where it is the number 0 you should provide the this.newItemPrice to it work properly.

How to extract single record ( id ) from JSON response and display in DOM using VueJS?

I have created a List view to see records from a JSON API response. Now I want user to click on a icon eg. '>' to see only that record on the full page ( Detail View ). I am not sure which vue directive I need to use and how to display in the DOM.
I tried this http://localhost:8000/Patients/1/?format=json but then directive v-for is not working
<template>
<div id="app" class="container">
<p v-if="loading">Loading...</p>
<div v-else>
<h3 class="heading" style="text-align:left">Patients List</h3>
<input id="lens" v-model= "search" placeholder ="Search here">
<br></br>
</div>
<table class="table table-bordered">
<thead>
<tr>
<th scope="col">ID</th>
<th scope="col">Name</th>
<th scope="col">Mobile</th>
<th scope="col">Email</th>
</tr>
</thead>
<tbody>
<tr v-for="patient in filteredPatients" v-bind:key="patient">
<td>{{ patient.id }}</td>
<td>{{ patient.first_name + " " + patient.last_name }}</td>
<td>{{ patient.mobile }}</td>
<td>{{ patient.email }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</template>
<script>
import axios from "axios";
export default {
name: 'app',
data () {
return {
loading: false,
patients: '',
search: '',
}
},
mounted () {
this.loading = true;
axios
.get('http://localhost:8000/Patients/?format=json')
.then(response => (this.patients = response.data))
.catch(error => console.log(error))
.finally(() => this.loading = false)
},
computed: {
filteredPatients() {
return this.patients.filter(patient => {
return `${patient.first_name} ${patient.last_name} ${patient.email} ${patient.mobile} ${patient.id}`.includes(this.search);
})
}
From the filteredPatients()method you are only returning three field i.e first_name,last_name and email but from your loop you are expecting to get five fields i.e id,first_name,last_name,email and mobile
How about you get your data directly from the patients value in the data section but you will first have to change it to an array.
After that have an onclick listener on the table rows and pass the specific product as an argument to the listener method.This way you will have the selected/clicked record.From here you can pass the record to the other page as props.
So the final code would look somehow like this:
<div id="app" class="container">
<p v-if="loading">Loading...</p>
<div v-else>
<h3 class="heading" style="text-align:left">Patients List</h3>
<input id="lens" v-model= "search" placeholder ="Search here">
<br></br>
</div>
<table class="table table-bordered">
<thead>
<tr>
<th scope="col">ID</th>
<th scope="col">Name</th>
<th scope="col">Mobile</th>
<th scope="col">Email</th>
</tr>
</thead>
<tbody>
<tr v-for="(patient,id )in patients" :key="id" #click="getOneRecord(patient)">
<td>{{ patient.id }}</td>
<td>{{ patient.first_name + " " + patient.last_name }}</td>
<td>{{ patient.mobile }}</td>
<td>{{ patient.email }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</template>
<script>
import axios from "axios";
export default {
name: 'app',
data () {
return {
loading: false,
patients: {},
search: '',
}
},
mounted () {
this.loading = true;
axios
.get('http://localhost:8000/Patients/?format=json')
.then(response => (this.patients = response.data))
.catch(error => console.log(error))
.finally(() => this.loading = false)
},
computed: {
filteredPatients() {
return this.patients.filter(patient => {
return `${patient.first_name} ${patient.last_name} ${patient.email} ${patient.mobile} ${patient.id}`.includes(this.search);
})
},
methods:{
getOneRecord(record):{
}
}

Vue.js 2 filter is not working with data table

Attempting to filter data by the name of the client. Tried many options with no luck. currently i have the list of clients broken out to a separate component with intention to use vuex as the project becomes larger. So with that being said i have currently placed the logic for filtering inside my client info component where as the input for the search is in the clients list component. see below
this is the clients info component
<template>
<tbody class="client-info">
<tr v-for="(client, index) in filterClient" :key="index">
<td>{{ index }}</td>
<td>{{ client.name }}</td>
<td>{{ client.type }}</td>
<td>{{ client.email }}</td>
<td>{{ client.phone }}</td>
<td><router-link v-bind:to="'/client/'+client.id"><i class="far fa-eye"></i></router-link></td>
</tr>
</tbody>
</template>
<script>
export default {
name: 'client-info',
props: {
clients: {
type: Array,
default: () => []
}
},
data() {
return {
search: ''
}
},
created() {
this.$store.dispatch('retrieveClients')
},
computed: {
filterClient () {
return this.clients.filter( client => {
return !this.searchClient || client.name.toLowerCase().includes(this.searchClient.toLowerCase()) > -1
})
}
}
}
</script>
this is the clients list component
<template>
<div>
<!-- this is the head of the table list -->
<table class="table table-bordered table table-light table-striped table-hover">
<thead class="thead-primary">
<tr>
<th scope="col">#</th>
<th scope="col">Name</th>
<th scope="col">Type</th>
<th scope="col">Email</th>
<th scope="col">Phone</th>
<th scope="col">Profile</th>
</tr>
</thead>
<!-- the clients data is imported from client info file -->
<client-info :clients="allClients"></client-info>
</table>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import ClientInfo from '#/components/clientslistexperience/ClientInfo'
export default {
name: 'ClientsList',
components: {
ClientInfo
},
data() {
return {
search: null
}
},
computed: {
...mapGetters(['allClients']),
}
}
</script>
i am aware that the data for the search is placed in both components at the moment, just trying different things out. Also that right now it is not being set up to use vuex for the logic and state. If I am completely off track please let me know!
Table tag requires thead, tbody or tr . it removes other tag , so put table tag inside your component.
<template>
<div>
<client-info :clients="allClients"></client-info>
</div>
</template>
and put table tag along with all inner tag
<template>
<table class="table table-bordered table table-light table-striped table-hover">
<thead class="thead-primary">
<tr>
<th scope="col">#</th>
<th scope="col">Name</th>
<th scope="col">Type</th>
<th scope="col">Email</th>
<th scope="col">Phone</th>
<th scope="col">Profile</th>
</tr>
</thead>
<tbody class="client-info">
<tr v-for="(client, index) in filterClient" :key="index">
<td>{{ index }}</td>
<td>{{ client.name }}</td>
<td>{{ client.type }}</td>
<td>{{ client.email }}</td>
<td>{{ client.phone }}</td>
<td><router-link v-bind:to="'/client/'+client.id"><i class="far fa-eye"></i></router-link></td>
</tr>
</tbody>
</template>