How to click user and view the user data? - vue.js

I am doing a project with VueJS and I need to the following:
Use an API & fetch a list of users in the home page.
When clicking on a user's button, I need to redirect to another component & output that user's details in that component (only the details of the user that I clicked).
Here is the table displaying the users info
<v-data-table hide-actions flat :headers="headers" :items="doctors">
<template v-slot:items="props">
<td>{{ props.index + 1 }}</td>
<td>{{ props.item.profile.full_name }}</td>
<td>{{ props.item.email }}</td>
<td>{{ props.item.username }}</td>
<td>{{ props.item.updated_at }}</td>
<td>
<v-btn outline small color="indigo" #click="view(props.item)">
<i class="fa fa-eye"></i> make payment
</v-btn>
</td>
</template>
<template v-slot:no-results>
<h6 class="grey--text">No data available</h6>
</template>
</v-data-table>
the view function is in the methods
view() {
window.open(`/finance/pay-doctors/${item.id}`, "_blank");
},
I have created a dynamic route
{ path: '/finance/pay-doctors/:id', component: DoctorDetails}
Am unable to create the DoctorDetails and show data

I recommend that you try with a router push initially.
Hence please use
<script>
export default {
methods: {
view(id) {
this.$router.push({ path: `/finance/pay-doctors/${item.id}` })
}
}
}
</script>
Make sure that your router is properly set + that you have the proper state thanks to your Vue devtools.
Also, be sure to have something to fetch the user's data on DoctorDetails.

Related

Every button getting loaded on clicking single button in Nuxt vuetify

When I am clicking on View Button every button is getting spinned. Here is the code which I used to view payload. Though I am getting correct data after clicking button, button I want to know reasons why every button is getting loaded though I have clicked on only one View.
Here are the screenshots
Before Click
After Click
<v-data-table
:headers="headers"
:items="tasks"
class="elevation-1"
:loading="loading"
hide-actions
>
<template v-slot:items="props">
<td>{{ props.item.reason }}</td>
<td>{{ props.item.requestedBy.adminName }}</td>
<td>{{ new Date(props.item.createdAt).toLocaleString("en") }}</td>
<td class="layout px-0">
<v-btn
small
:loading="downloadingPayload"
#click="getPayload(props.item._id)"
>
View
</v-btn>
</td>
</template>
</v-data-table>
It is because you have the same data for all the elements.
:loading="downloadingPayload"
i guess you have something like this in your method
getPayload (item) { this.downloadingPayload = true }
need to have a downloadingPayload for each item and do something like
getPayload (item) {
item.downloadingPayload = true
}
and in the template
:loading="props.item.downloadingPayload"

Vuetify Data Tables: Fetch data when expanding a row

I have a Vuetify data table with expandable rows. Each row correlates with a customer's order which consists of samples they want to be tested.
Currently, I'm retrieving all the orders with all the samples but it takes too long to load all the information.
So instead of retrieving all the samples for every order, when I expand a row I want to be able to perform an API call to retrieve the samples that should be displayed in the expanded section for that particular order.
I've researched all I can and have come to a dead-end. Here's where I'm at currently:
<v-data-table
:headers="orders_headers"
:items="orders"
show-expand
:single-expand="true"
:expanded.sync="expanded"
>
<!-- Expand Buttons -->
<template v-slot:item.data-table-expand="{ item, isExpanded, expand }">
<v-btn #click="expand(true)" v-if="!isExpanded">Expand</v-btn>
<v-btn #click="expand(false)" v-if="isExpanded">close</v-btn>
</template>
<!-- Expanded Data -->
<template v-slot:expanded-item="{ headers, item }">
<td :colspan="headers.length">
<table v-for="(sample, index) in item.samples" :key="index">
<tr>
<th>Sample Acc</th>
<td>{{ sample.sample_accession }}</td>
</tr>
<tr>
<th>Sample Status</th>
<td>{{ sample.sample_status }}</td>
</tr>
</table>
</td>
</template>
</v-data-table>
I think I may have figured it while writing this
As I was typing this out, I realized what I possibly need to do. I need to add a method call to the expand button, then in that method set the results to expandedSamples and replace item.samples with it.
In the meantime, if anyone has any better solution I'd love to hear. Otherwise, I'll post my solution in case anyone else is trying to attempt something similar to this.
Bonus
Anyone know if there's a way to tap into the expand event without replacing the default icons/functionality or a way to include the original icons/functionality when using v-slot:item.data-table-expand?
Currently when I'm using v-slot:item.data-table-expand, I have to add the buttons back in and I lose the chevrons and the animations.
For the benefit of future readers facing the same issue, use the #item-expanded event of the data-table to lazy load the item details (or child data) on demand. Hook the item-expanded event to a method (e.g. loadDetails) that loads the data, and then merges the response into the original items array.
Here's an example...
<v-data-table
:headers="headers"
:items="items"
show-expand
single-expand
item-key="name"
:search="search"
#item-expanded="loadDetails"
>
<template v-slot:expanded-item="{ headers, item }">
<td :colspan="headers.length">
<table v-for="(sample, index) in items.samples" :key="index">
<tr>
<th>Sample Acc</th>
<td>{{ sample.sample_accession }}</td>
</tr>
</table>
</td>
</template>
</v-data-table>
methods: {
loadDetails({item}) {
axios.get('http.../' + item.id)
.then(response => {
item.samples = response.data[0]
})
}
}
https://codeply.com/p/d5XibmqjUh
Here's the basic solution to my issue.
<v-data-table
:headers="orders_headers"
:items="orders"
show-expand
:single-expand="true"
:expanded.sync="expanded"
>
<!-- Expand Buttons -->
<template v-slot:item.data-table-expand="{ item, isExpanded, expand }">
<v-btn #click="expand(true); getSamples(item.id)" v-if="!isExpanded">Expand</v-btn>
<v-btn #click="expand(false)" v-if="isExpanded">close</v-btn>
</template>
<!-- Expanded Data -->
<template v-slot:expanded-item="{ headers, item }">
<td :colspan="headers.length">
<table v-for="(sample, index) in expandedOrderDetails" :key="index">
<tr>
<th>Sample Acc</th>
<td>{{ sample.sample_accession }}</td>
</tr>
<tr>
<th>Sample Status</th>
<td>{{ sample.sample_status }}</td>
</tr>
</table>
</td>
</template>
</v-data-table>
<script>
methods: {
getSamples(orderId) {
// Perform API call
this.expandedOrderDetails = response.data;
},
}
</script>
After many hours of development, our project leader has decided he wants each row to correlate to each sample instead of orders though. So expanding is no longer necessary.

Getting Current ID on Quasar Table

Good Morning,
I am trying to do and edit feature in a Quasar table.
It works as expected, but when I have more than one entry in the table it defaults to the last entry.
The value of the row is added to the component using props, my problem is getting the current row.
So my question is how do it get the correct row when clicking the button?
Code Download
You can get row data using props.row.
Example -
<q-table
title="Treats"
:data="data"
:columns="columns"
row-key="name"
>
<template v-slot:body-cell-name="props">
<q-td :props="props">
<div>
<q-badge color="purple" :label="props.value"></q-badge>
<q-btn icon="edit" dense flat size="sm" #click="EditData(props.row)"></q-btn>
</div>
</q-td>
</template>
</q-table>
methods:{
EditData(row){
alert("hi")
console.log(row);
console.log(this.data.indexOf(row))
}
}
Now you have a row and indexof particular row. You can use splice or replace the element on particular index.
Codepen - https://codepen.io/Pratik__007/pen/LYVjqXb
Check the console.
**you can use index to get the row number
<tr v-for="(strockorder,index) in strockorders" :key="index">
<td class="text-left">{{ strockorder.strockNo}}</td>
<td class="text-center">{{ strockorder.item}}</td>
<td class="text-center">{{ strockorder.orderDate}} </td>
<td class="text-center">{{strockorder.deliveryDate }}</td>
**

Vue + Vuetify performance

So I have a Vuetify data table with a lot of items, I need to display 50/100/150 items.
The problem, when I have 50 items it takes about 2 to 3 seconds to load the entire table. My request takes about 500ms to load and vue takes 3 seconds to render. When I have 150 items it takes about 10 seconds.
Is it possible to reduce render time to at least 5 seconds with 150 items?
I'm using this table: https://vuetifyjs.com/en/components/data-tables#data-tables
My table code:
<template>
<v-data-table
:headers="headers"
:items="desserts"
:items-per-page="5"
class="elevation-1"
:items-per-page="50"
:footer-props="rowsPerPageItems"
>
<TableRow :row="item" v-for="item in clientList" :key="item.id">
</v-data-table>
</template>
// my clientList is async response from server
import { mapGetters } from "vuex";
export default {
data(){
return {
rowsPerPageItems: [50, 100, 150]
}
},
computed: {
...mapGetters["clientList"] // this comes from vuex, I don't think it's relevant. It returns about 400 client info
}
}
<!-- My component TableRow -->
<template>
<tr>
<td>
<v-checkbox v-model="row.checked" color="primary"></v-checkbox>
</td>
<td>{{ row.data }}</td>
<td>{{ row.name }}</td>
<td>
<!-- This is a jQuery plugin, I need it to keep track of every key pressed -->
<Select2
:id="row.id.toString()"
:settings="{ minimumInputLength: 3, ajax: ajaxData, placeholder: labels.descriptionList[row.description] }"
#select="handleSelect2($event)"
/>
</td>
<td v-if="this.is_expense">
<v-select
:items="labels.categoryName"
:placeholder="labels.categoryList[row.category]"
#input="updateCashflow"
v-model="row.category"
dense
></v-select>
</td>
<td v-else></td>
<td>{{ row.amount }}</td>
</tr>
</template>
Remove that component and read v-data-table documentation.
You shouldn't add v-checkbox to make it rows selectable, use show-select prop instead with v-model on v-data-table
Use item.<name> slot to add custom components into cells
In my case, I really need components inside my table so I used
<v-select v-if="mountedComponent" :items="myList"/>
<span v-else>Some default value</span>
When page mounted I set mounted to true
export default {
name: "Test",
data(){
return {
mountedComponent: false,
myList:["item 1", "item 2"]
}
},
mounted(){
this.mountedComponent= true;
}
}
This solved my performance issue, going from 10 ~ 15 seconds delay to 100ms.

How do I properly update Vuex state from my Vuetify data-table

I am using Vue, Vuex, and Vuetify to display courses in data-table and want in-line editing as a feature. Please see relevant component code below.
#Courses.vue
<template>
<v-data-table
:headers="headers"
:items="courses"
:search="search"
:loading="loading"
no-data-text="No Courses Available"
>
<template slot="items" slot-scope="props">
<tr>
<td>
<v-edit-dialog
:return-value.sync="props.item.title"
lazy
>
{{ props.item.title }}
<v-text-field
slot="input"
label="Enter New Course Title"
v-model="props.item.title"
single-line
counter
#keyup.enter="onUpdateCourse({ id: props.item.id, title: props.item.title})"
></v-text-field>
</v-edit-dialog>
</td>
<td>{{ props.item.category }}</td>
<td>{{ props.item.startDate | date }}</td>
<td>{{ props.item.endDate | date }}</td>
<td>{{ props.item.location }}</td>
</tr>
</template>
</v-data-table>
</template>
<script>
export default {
data() {
return {
headers: [** table headings **],
};
},
computed: {
courses() {
return this.$store.getters.courses;
},
},
methods: {
onUpdateCourse(itemToUpdate) {
debugger;
this.$store.dispatch('updateCourse', itemToUpdate);
},
},
};
</script>
It works; but I take issue with the fact that this approach alters the Vuex state directly: the only reason to dispatch(‘updateCourse’, itemToUpdate) is to update the db (firestore in this case). I would have thought it preferable to update the $store.state solely via the Vuex actions/mutations rather than have it syncing directly here.
So, my first question is: Should I take issue with this, and if not why not?
Because it was bugging me, I added a local copy of the Vuex courses array to the computed section:
localCopy() {
return this.courses.map(x => Object.assign({}, x));
},
and built the data-table using :items="localCopy". This allows me to update the courses from within the Vuex actions but the data table doesn’t update until I click somewhere else in the app.
So, my second question is: If this is the correct approach, how do I keep the reactivity of the component?
Thanks for any help.
(PS – When cutting and pasting code, or editing in the text box, it seems that some of the double quotes " are unfortunately getting converted to fancy quotes “. It is doubly unfortunate that I am dyslexic and although I have done my best to hunt them down, I literally can’t see them most of the time. Please don’t hate on me)
To not assign changes to props.item.title, do:
Remove the .sync in <v-edit-dialog :return-value="props.item.title".
Replace v-model with :value in <v-text-field :value="props.item.title".
As .sync has an implicit #return-value:update="props.item.title = $event" and v-model has an implicit #input="props.item.title = $event (roughly), the above alone (removing .sync and replacing v-model with :value) would stop title from being directly modified.
To make it being modified via dispatch also add an #input listener that calls the dispatch: #input="onUpdateCourse({ id: props.item.id, title: props.item.title})".
Finally, here's how your code should look like:
<td>
<v-edit-dialog
:return-value="props.item.title"
lazy
>
{{ props.item.title }}
<v-text-field
slot="input"
label="Enter New Course Title"
:value="props.item.title"
#input="onUpdateCourse({ id: props.item.id, title: props.item.title})"
single-line
counter
#keyup.enter="onUpdateCourse({ id: props.item.id, title: props.item.title})"
></v-text-field>
</v-edit-dialog>
</td>