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

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

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>

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.

Not able to remove a user from a table

I have a nested table where I'm grouping users by the department that they belong to. The issue I'm having is that if I click on the delete button, that specific user that I've deleted isn't being removed from the list and I'm not getting any errors in my console.
Here is my code
<template>
<div>
<div class="row">
<div class="col-lg-12">
<table class="table table-bordered table-sm">
<thead>
<tr>
<th>Name</th>
<th>Job Title</th>
</tr>
</thead>
<tbody v-for="(users, department) in personnal">
<tr>
<td>{{ department }}</td>
</tr>
<tr v-for="user in users">
<td>{{ user.name }}</td>
<td>{{ user.job_title }}</td>
<td>
<div class="btn btn-danger btn-sm" #click="removeUser(user)">
<i class="fa fa-trash"></i>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</template>
export default
{
props: [],
data() {
return {
personnal: {},
}
},
computed: {
},
methods: {
getUsers(){
axios.get(`/api/users`).then(response => {
this.personnal = response.data.users;
});
},
removeUser(user){
axios.delete(`/api/users/${user.id}/delete`).then(response => {
Object.keys(this.personnal).forEach(user => {
this.personnal[user].filter(u => u.id !== response.data.id);
this.personnal[user].slice(response.data);
});
});
}
},
mounted() {
this.getUsers();
}
}
</script>
First of all, you have to pass both department and user in the click event:
<div class="btn btn-danger btn-sm" #click="removeUser(department, user)">
<i class="fa fa-trash"></i>
</div>
Then, in the removeUser method:
removeUser(department, user){
axios.delete(`/api/users/${user.id}/delete`).then(response => {
const index = this.personnal[department].findIndex(u => u.id === user.id);
this.personnal[department].splice(index, 1);
});
}

Not returning value in vue function

I'm using vue and I'm trying to return a user's email. I have 2 separate functions and one gets all the products while the other one
is supposed to get the user emails that is attached to that product.
The issue I'm having is that the emails isn't showing up and I'm getting an undefined as well.
Here is my code
<template>
<div class="card" style="width: 100%;">
<div class="card-body">
<table class="table">
<thead>
<tr>
<th scope="col">Email</th>
<th scope="col">Product</th>
</tr>
</thead>
<tbody>
<tr v-for="product in products">
<td>
{{ getProductUser(product.user_id) }}
</td>
<td>
{{ product.action }}
</td>
<td>
{{ product.options }}
</td>
<td>
{{ product.created_at }}
</td>
</tr>
</tbody>
</table>
</div>
</div>
</template>
<script>
export default {
props:['id', 'user', 'product'],
data(){
return {
products: [],
}
},
methods: {
getProducts(){
axios.get('/api/products/'+this.id+'/product').then(response => {
this.products = response.data.products;
});
},
getProductUser(user){
axios.get('/api/product/'+user).then(response => {
return response.data.email;
});
}
},
mounted() {
this.getProducts();
}
}
</script>
Apart from the fact that product.user_id is undefined as stated in the comment, you have another issue.
getProductUser doesn't return an email. It returns a promise that will provide an email.
Simplest solution would be
<td>
{{ product.userEmail }}
</td>
(which is by the way an equivalent to <td v-text="product.userEmail"/>, vue templates can be much cleaner than standard HTML).
getProducts(){
axios.get('/api/products/'+this.id+'/product').then(response => {
this.products = response.data.products;
this.products.forEach(product => {
axios.get('/api/product/'+user).then(response => {
product.userEmail = response.data.email;
});
})
});
This should work, but keep in mind that executing additional request per product is generally a bad idea, the performance would be much better if /api/products/{id}/product would have userEmail property already initialized.

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>