Vue.js making object.length value reactive - vue.js

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>

Related

How to update the value in particular TextBox in Table row using change event in 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.

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>

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):{
}
}

Data is not showing up in html

I am running into a issue with showing the data from my variable, itemListPrice. I have checked in the console and the data is populated into the itemListPrice, but its not showing up in my html, am I loading it all wrong?
Here is the markup
<div id="app2">
<div id="TableContainer" style="width:798px !important">
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<td><label>Catalog Name</label></td>
</tr>
</thead>
<tbody>
<tr>
<td>{{ currentCatalogName }}</td>
</tr>
</tbody>
</table>
<table class="table">
<thead>
<tr>
<td><label>Item Name</label></td>
</tr>
</thead>
<tbody>
<tr>
<td> {{ itemPriceList.Name }}</td>
</tr>
</tbody>
</table>
<table class="table">
<thead>
<tr>
<td colspan="2"><label>Item List</label></td>
</tr>
</thead>
<tbody>
<tr>
<td width="575px">${{ itemPriceList.ItemListPrice }}</td>
<td>${{ itemPriceList.ItemListPrice }}</td>
</tr>
</tbody>
</table>
<table class="table">
<thead>
<tr>
<td colspan="3"><label>Item Features</label></td>
<td></td>
</tr>
</thead>
<tbody>
<template v-for="item in itemPriceList.ItemFeatures">
<tr v-if="item.FeatureQuantity != 0">
<td width="250px">{{ item.FeatureName }}</td>
<td>{{ item.FeatureQuantity }}</td>
</tr>
</template>
</tbody>
</table>
<table class="table">
<thead>
<tr>
<td><label>Item IAM</label></td>
</tr>
</thead>
<tbody>
<tr>
<td>{{ itemPriceList.ItemIAM }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
and here is my code
new Vue({
el: '#app2',
beforeCreate: function () {
StartBusyIndicator("#ItemPriceListWindow");
},
created: function () {
this.GetItemDetails();
},
mounted: function () {
StopBusyIndicator("#ItemPriceListWindow");
},
data: {
itemPriceList: [],
orderItems: currentOrderItems,
currentCatalogName: currentCatalog,
priceList: null
},
methods: {
GetItemDetails: function () {
TryCatch(function() {
let result = GetItemPriceDetails();
this.itemPriceList = result;
priceList = result;
});
},
GetOrderItems: function () {
},
OptionPriceSplitter: function (optionprice) {
return TryCatch(function() {
let sentenceSplit = optionprice.split('& ');
let comp = '';
for (let i = 0; i < sentenceSplit.length; i++) {
comp += sentenceSplit[i] + '\n';
}
return sentenceSplit;
});
},
GlobalListPriceCalculation: function (globalgroupid) {
return TryCatch(function() {
let listPrice = 0.00;
let priceList = this.itemPriceList;
let result = priceList.GlobalListPrices.filter(function(item) {
if (item.GlobalGroupID == globalgroupid) {
listPrice = listPrice + item.Price;
}
});
if (listPrice == 0) {
listPrice = "";
} else {
listPrice = "$" + listPrice.toFixed(2);
}
return listPrice;
});
}
}
});
assuming TryCatch(cb) is something along the lines of
function TryCatch(cb){ let x = null; try { x = cb() } catch(e) {} return x; }
you loose 2 important things:
this (you could bind it via cb.call(this))
utterly usefull error messages
furher points:
I have checked in the console
checkout the excellent browser plugin for ff and chrome vue-devtools
itemPriceList = []
you initalize itemPriceList as array, use it as array item in itemPriceList, but also use it as object {{ itemPriceList.Name }} - what shall it be ?

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>