I have an assignment that uses Vuetify and I want to render a table of "customer" data. My code worked fine with Vuetify 1.5, but now I am supposed to update to the newest version, which is 2.0.
The problem I'm running into is that my data is recognized by the program, but none of it actually shows up in the table. The exact number of rows are rendered to match my number of records, and Vue DevTools shows that my data is there, but The rendered html is just empty elements. I attached screenshots below to illustrate my point a little better:
The project can be found at: https://github.com/apalmesano2/assign3_frontend.git
Here is a snippet of what my Vue template for the list of customers:
CustomerList.vue
<template>
<main>
<br />
<v-container fluid grid-list-md>
<v-layout column align-left>
<blockquote>
Welcome {{validUserName}}!
<footer>
<small>
<em>—Eagle Financial Services, your Midwest Financial Services Partner.</em>
</small>
</footer>
</blockquote>
</v-layout>
<v-layout column align-center>
<v-flex xs6 sm8 md7>
<v-alert
v-if="showMsg === 'new'"
dismissible
:value="true"
type="success"
>New customer has been added.</v-alert>
<v-alert
v-if="showMsg === 'update'"
dismissible
:value="true"
type="success"
>Customer information has been updated.</v-alert>
<v-alert
v-if="showMsg === 'deleted'"
dismissible
:value="true"
type="success"
>Selected Customer has been deleted.</v-alert>
</v-flex>
</v-layout>
<br />
<v-container fluid grid-list-md fill-height>
<v-layout column>
<v-flex md6>
<v-data-table
:headers="headers"
:items="customers"
hide-default-footer
class="elevation-1"
fixed
style="max-height: 300px; overflow-y: auto"
>
<template slot="items" slot-scope="props">
<td>{{ props.item.pk }}</td>
<td>{{ props.item.cust_number }}</td>
<td>{{ props.item.name }}</td>
<td nowrap="true">{{ props.item.address }}</td>
<td nowrap="true">{{ props.item.city }}</td>
<td nowrap="true">{{ props.item.state }}</td>
<td nowrap="true">{{ props.item.zipcode }}</td>
<td nowrap="true">{{ props.item.email }}</td>
<td nowrap="true">{{ props.item.cell_phone }}</td>
<td nowrap="true">
<v-icon #click="updateCustomer(props.item)">edit</v-icon>
</td>
<td nowrap="true">
<v-icon #click="deleteCustomer(props.item)">delete</v-icon>
</td>
</template>
</v-data-table>
</v-flex>
</v-layout>
</v-container>
<v-btn class="blue white--text" #click="addNewCustomer">Add Customer</v-btn>
</v-container>
</main>
</template>
And the script, although I don't think there is an issue here because the data is being fetched just fine according to the Vue DevTools:
<script>
import router from "../router";
import { APIService } from "../http/APIService";
const apiService = new APIService();
export default {
name: "CustomerList",
data: () => ({
customers: [],
validUserName: "Guest",
customerSize: 0,
showMsg: "",
headers: [
{ text: "Record Number", sortable: false, align: "left" },
{ text: "Customer Number", align: "left", sortable: false },
{ text: "Name", sortable: false, align: "left" },
{ text: "Address", sortable: false, align: "left" },
{ text: "City", sortable: false, align: "left" },
{ text: "State", sortable: false, align: "left" },
{ text: "ZipCode", sortable: false, align: "left" },
{ text: "Email", sortable: false, align: "left" },
{ text: "Phone", sortable: false, align: "left" },
{ text: "Update", sortable: false, align: "left" },
{ text: "Delete", sortable: false, align: "left" }
]
}),
mounted() {
this.getCustomers();
this.showMessages();
},
methods: {
showMessages() {
console.log(this.$route.params.msg);
if (this.$route.params.msg) {
this.showMsg = this.$route.params.msg;
}
},
getCustomers() {
apiService
.getCustomerList()
.then(response => {
this.customers = response.data.data;
this.customerSize = this.customers.length;
if (
localStorage.getItem("isAuthenticates") &&
JSON.parse(localStorage.getItem("isAuthenticates")) === true
) {
this.validUserName = JSON.parse(localStorage.getItem("log_user"));
}
})
.catch(error => {
if (error.response.status === 401) {
localStorage.removeItem("isAuthenticates");
localStorage.removeItem("log_user");
localStorage.removeItem("token");
router.push("/auth");
}
});
},
addNewCustomer() {
if (
localStorage.getItem("isAuthenticates") &&
JSON.parse(localStorage.getItem("isAuthenticates")) === true
) {
router.push("/customer-create");
} else {
router.push("/auth");
}
},
updateCustomer(customer) {
router.push("/customer-create/" + customer.pk);
},
deleteCustomer(customer) {
apiService
.deleteCustomer(customer.pk)
.then(response => {
if (response.status === 204) {
alert("Customer deleted");
this.showMsg = "deleted";
this.$router.go();
}
})
.catch(error => {
if (error.response.status === 401) {
localStorage.removeItem("isAuthenticates");
localStorage.removeItem("log_user");
localStorage.removeItem("token");
router.push("/auth");
}
});
}
}
};
</script>
I have a hunch I'm doing something wrong with my v-data-table and the template where I'm passing the props in but I can't figure out what's going wrong or how to fix it because even just hardcoded text in the tags won't render in the table.
To access the page where the screenshots are after cloning the project, you would need to log in with the following credentials:
Username: instructor
Password: instructor1a
Thanks to anyone who can help!
I figured out the new syntax after moving to vuetify v2:
<v-data-table
:headers="headers"
:items="stocks"
hide-default-footer
class="elevation-1"
fixed
style="max-height: 300px; overflow-y: auto"
>
<template v-slot:item="props">
<tr>
<td>{{ props.item.customer }}</td>
<td nowrap="true">{{ props.item.symbol }}</td>
<td nowrap="true">{{ props.item.name }}</td>
<td nowrap="true">{{ props.item.shares }}</td>
<td nowrap="true">{{ props.item.purchase_price }}</td>
<td nowrap="true">{{ props.item.purchase_date }}</td>
<td nowrap="true">
<v-icon #click="updateStock(props.item)">edit</v-icon>
</td>
<td nowrap="true">
<v-icon #click="deleteStock(props.item)">delete</v-icon>
</td>
</tr>
</template>
</v-data-table>
Related
I have a vuetify data table, I've set width for every column and I want to fixed column's width of my table but it has grown up because of their content and it doesn't matter what is the width of them.
how can fixed column's width?
<template>
<v-data-table
v-if="physicianAccountsLocalData"
:headers="headers"
:items="physicianAccountsLocalData"
hide-default-footer
class="data-table"
:items-per-page="-1"
>
<template #[`item.isActive`]="props">
<BaseSwitch
v-model="props.item.isActive"
class="switch-siam-table"
#change="changeModel($event, props)"
/>
</template>
<template #[`item.location`]="props">
<span
class="location"
:class="{'text-disable-state': !props.item.isActive}"
>
{{ props.item.location }}
</span>
</template>
<template #[`item.name`]="props">
<span
class="name"
:class="{'text-disable-state': !props.item.isActive}"
>
{{ props.item.name }}
</span>
</template>
</v-data-table>
</template>
<script>
data() {
return {
headers: [
{ text: 'فعال', value: 'isActive', sortable: false, width: '110px', align: 'right' },
{ text: 'نوع', value: 'location', sortable: false, width: '13%', align: 'start' },
{ text: 'نام', value: 'name', sortable: false, width: '200px' }
],
}
}
</script>
I have a list of orders which I am populating via the orders array. Each of these orders have a functionality where the user can edit these orders. I am trying to generate a vuetify dialog box component when the user clicks on the edit button which will have the default data from that order which the user can choose to edit. So far I have tried this,
<tbody class="items">
<tr v-for="order in orders" :key="order.name">
<td>{{ order.fullname }}</td>
<td>{{ order.address }}</td>
<td>{{ order.phone }}</td>
<!-- <td>{{ order.orderQuantity }}</td> -->
<td>{{ order.quantity }}</td>
<td v-if="order.status == true">
<v-chip small outlined class="ma-2 chip" color="green">delivered</v-chip>
</td>
<td v-if="order.status == false">
<v-chip small outlined class="ma-2 chip" color="red">in progress</v-chip>
</td>
<td>
<!-- <component v-bind:is="component"></component> -->
<!-- <EditOrder></EditOrder> -->
<v-dialog v-model="dialog" persistent max-width="290">
<template #activator="{ on, attrs }">
<v-btn icon color="primary" dark v-bind="attrs" v-on="on"> Open Dialog </v-btn>
</template>
<v-card>
<v-card-title class="headline"> Use Google's location service? </v-card-title>
<v-card-text>{{ phone }}</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="green darken-1" text #click="dialog = false"> Disagree </v-btn>
<v-btn color="green darken-1" text #click="dialog = false"> Agree </v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</td>
</tr>
</tbody>
but this generates n instances of the dialog box before the actual components loads. I also tried using async components but couldn't figure out where I was wrong.
This is what I think should happen;
When the user clicks on the "edit order" button, a dialog box component with the default order details for that particular order must be automatically generated and destroyed.
Please correct me if I am wrong.
Move the dialog part to a separate component
In the parent component, use it once at the beginning with dialog=false to be hidden initially
Add a data attribute orderToEdit which will be a prop to the child component
For each order in the list, add an edit button that would call a method when clicked passing the order
In this method, set orderToEdit to the passed order of the clicked item and set dialog=true to show it with the custom values
const dialogmodel = Vue.component('btn', {
template: '#dialogmodel',
props: { order: Object, value: Boolean },
computed: {
dialog: {
get () { return this.value; },
set (value) { this.$emit('close', value); }
}
}
});
new Vue({
el:"#app",
vuetify: new Vuetify(),
components: { dialogmodel },
data: () => ({
orders: [
{ name:"1", fullname:"fullname1", address:"address1", phone:"phone1", quantity:"quantity1", status:false },
{ name:"2", fullname:"fullname2", address:"address2", phone:"phone2", quantity:"quantity2", status:true }
],
dialog:false,
orderToEdit: {}
}),
methods: {
closeDialog(value) { this.dialog = value; },
editOrder(order) { this.orderToEdit = order; this.dialog = true; }
}
});
<script src="https://cdn.jsdelivr.net/npm/vue#2.x/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.js"></script><link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/#mdi/font#4.x/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.min.css" rel="stylesheet">
<template id="dialogmodel">
<div>
<v-dialog v-model="dialog" max-width="290" persistent>
<v-card>
<v-card-title class="headline">
{{ order.fullname }}
</v-card-title>
<v-card-text> {{ order.address }} </v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn color="green darken-1" text #click="$emit('close')">
Disagree
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</div>
</template>
<v-app id="app">
<div>
<dialogmodel v-model="dialog" :order="orderToEdit" #close="closeDialog" />
</div>
<div>
<table>
<tbody class="items">
<tr v-for="order in orders" :key="order.name">
<td>{{ order.fullname }}</td>
<td>{{ order.address }}</td>
<td>{{ order.phone }}</td>
<td>{{ order.quantity }}</td>
<td v-if="order.status == true">
<v-chip small outlined class="ma-2 chip" color="green">delivered</v-chip>
</td>
<td v-if="order.status == false">
<v-chip small outlined class="ma-2 chip" color="red">in progress</v-chip>
</td>
<td>
<v-btn #click="editOrder(order)">Edit</v-btn>
</td>
</tr>
</tbody>
</table>
</div>
</v-app>
Are you looking to have a central popup being filled with the data of the order you selected to edit? In that case you can simply have a single component for the edit popup (instead of adding them to your v-for).
You can do it like this:
When clicking on 'edit', set a data property 'orderIndexToEdit' to the index of the order in the v-for
have a single dialog box that becomes visible when 'orderIndexToEdit' is truethy using v-if or v-show
Create a computed property that returns the correct order from the orders array, based on the index stored in the 'orderIndexToEdit' variable.
Use that computed property to populate the dialog box.
Let me know whether this helps!
I have a problem with an array that is undefined while in Vuejs devTool I see it full. Here is what I did with quasar:
When i click on my update button i want to display a q-card with input of my row. So i have make a request with axios and put response in array. But i have 'undefined' when i do a console.log() of my array.
<template>
<div>
<div class="q-pa-sm q-gutter-sm">
<q-dialog v-model="show_dialog" v-bind:programmeEdit="programmeEdit">
<q-card>
<q-card-section>
<div class="text-h6">Add new item!</div>
</q-card-section>
<q-card-section>
<div class="row">
<q-input v-model="programmeEdit.prg_libelle" label="prg_libelle"></q-input>
<q-input label="id"></q-input>
<q-input label="nom promotteur"></q-input>
<q-input label="date commercialistion"></q-input>
<q-input label="stock initial"></q-input>
<q-input label="nombre tranche"></q-input>
</div>
</q-card-section>
<q-card-actions align="right">
<q-btn flat label="OK" color="primary" v-close-popup #click="" ></q-btn>
</q-card-actions>
</q-card>
</q-dialog>
</div>
<q-table title="Programme" :filter="filter" :data="programme.data" :columns="columns" row-key="name">
<template v-slot:top-right>
<q-input borderless dense debounce="300" v-model="filter" placeholder="Search">
<template v-slot:append>
<q-icon name="search" />
</template>
</q-input>
</template>
<template v-slot:body="props">
<q-tr :props="props">
<q-td key="prg_libelle" :props="props">{{ props.row.prg_libelle }}</q-td>
<q-td key="id" :props="props">{{ props.row.id }}</q-td>
<q-td key="nom_promotteur" :props="props">{{ props.row.act_libelle }}</q-td>
<q-td key="id_promotteur" :props="props">{{ props.row.id_promotteur }}</q-td>
<q-td key="date_commercialisation" :props="props">{{ props.row.tra_date_commercialisation }}</q-td>
<q-td key="stock_initial" :props="props">{{ props.row.tra_stock_initial }}</q-td>
<q-td key="nombre_tranche" :props="props">{{ props.row.nombre_tranche }}</q-td>
<q-td key="actions" :props="props">
<q-btn color="blue" label="Update" #click="getProgramme(props.row.id)" size=sm no-caps></q-btn>
<q-btn color="red" label="Delete" #click="deleteItem(props.row)" size=sm no-caps></q-btn>
</q-td>
</q-tr>
</template>
</q-table>
</div>
</template>
Vue.use(VueAxios, axios)
export default {
methods: {
},
data () {
return {
programmetoEdit:'',
show_dialog:false,
filter: '',
programme:{},
programmeEdit:{},
columns: [
{
name: 'prg_libelle',required: true, label: 'prg_libelle',align: 'left', field: 'prg_libelle' ,format: val => `${val}`, sortable: true },
{ name: 'id', align: 'center', label: 'id', field: 'id', sortable: true },
{ name: 'nom_promotteur', align: 'center',label: 'nom_promotteur', field: 'act_libelle', },
{ name: 'id_promotteur', align: 'center',label: 'id_promotteur', field: 'id_promotteur', },
{ name: 'date_commercialisation', align: 'center',label: 'date_commercialisation', field: 'tra_date_commercialisation', },
{ name: 'stock_initial', align: 'center',label: 'stock_initial', field: 'tra_stock_initial', },
{ name: 'nombre_tranche', align: 'center',label: 'nombre_tranche', field: 'nombre_tranche', },
{ name: "actions", label: "Actions", field: "actions"},
]
}
},
created() {
axios.get("http://localhost:80/api/programme")
.then(response =>this.programme=response)
.catch(error=>console.log(error))
},
methods: {
getProgramme($id){
axios.get("http://localhost:80/api/programme/"+$id)
.then(response => this.programmeEdit=response.data);
this.show_dialog= !this.show_dialog;
this.programmeEdit.id=this.programmetoEdit;
}
},
}
I get an undefined when I want to retrieve the query according to the id. I'm blocking and I don't know why.
thanks a lot :)
If you want to log it from your code (like in the .then promise, for example) you should do:
console.log(this.programmetoEdit);
Moreover, you are doing assignment in parallel of the job Axios is doing. Not after, as I guess you are expecting it to do.
You should drop all your stuff in the .then promise if you want it to happen after the this.programmeEdit=response.data part.
Like so (this will show the dialog WHILE loading the data/waiting for the API to respond):
getProgramme ($id) {
axios.get("http://localhost:80/api/programme/" + $id)
.then(response => {
this.programmeEdit=response.data;
this.programmeEdit.id=this.programmetoEdit;
});
this.show_dialog= !this.show_dialog;
}
Or like so (this will show the dialog AFTER loading the data/waiting for the API to respond):
getProgramme ($id) {
axios.get("http://localhost:80/api/programme/" + $id)
.then(response => {
this.programmeEdit=response.data;
this.programmeEdit.id=this.programmetoEdit;
this.show_dialog= !this.show_dialog;
});
}
i want to show a image in vuetify data table row. i tried this with below code.
<v-data-table :headers="headers" :items="desserts" :search="search" class="elevation-1">
<template slot="items" slot-scope="props">
<td>
<img :src="'/assets/img/' + props.item.name" style="width: 50px; height: 50px" />
</td>
<td class="text-xs-right">{{ props.item.calories }}</td>
<td class="text-xs-right">{{ props.item.fat }}</td>
<td class="text-xs-right">{{ props.item.carbs }}</td>
<td class="text-xs-right">{{ props.item.protein }}</td>
<td class="text-xs-right">{{ props.item.iron }}</td>
</template>
<template v-slot:item.action="{ item }">
<v-icon small class="mr-2" #click="editItem(item)">edit</v-icon>
<v-icon small #click="deleteItem(item)">delete</v-icon>
</template>
</v-data-table>
but this code not works properly. only shows data with v-data-table loop and template slot not working. my script like this.
search: "",
headers: [
{
text: "Images",
align: "left",
sortable: false,
value: "name"
},
{ text: "Calories", value: "calories" },
{ text: "Fat (g)", value: "fat" },
{ text: "Carbs (g)", value: "carbs" },
{ text: "Protein (g)", value: "protein" },
{ text: "Iron (%)", value: "iron" },
{ text: "Actions", value: "action", sortable: false }
],
desserts: [
{
value: false,
name: "notfound.png",
calories: 159,
fat: 6.0,
carbs: 24,
protein: 4.0,
iron: "1%"
}],
how can i show my image. this method only shows image text name only.
Presuming you use the last version of vuetify, the slots are working a little bit different, and you can define a slot only for the column you want to customize. (order is defined by the headers)
<div id="app">
<v-data-table :headers="headers" :items="desserts" class="elevation-1">
<template v-slot:item.name="{ item }">
<img :src="'/assets/img/' + item.name" style="width: 50px; height: 50px" />
</template>
<template v-slot:item.action="{ item }">
<v-icon small class="mr-2" #click="editItem(item)">edit</v-icon>
<v-icon small #click="deleteItem(item)">delete</v-icon>
</template>
</v-data-table>
</div>
I encountered some troubles creating an accordion table. I created an Accordion component and a Table component. While independant from each other it works perfectly fine but I can't get any table appear into accordion.
//js part of accordion component
import Table from "../Table/index.vue"
export default {
name: 'accordion',
components: { Table },
mounted () {
},
data(){
return {
items: [
{ id: "mc", title: "Medical Checkup", text: this.components.Table },
{ id: "ac", title: "Application connected", text:
this.components.Table },
{ id: "p", title: "Programs", text: this.components.Table },
{ id: "pl", title: "Pain list", text: this.components.Table }
]
}
}
}
//html part of accordion component
<div>
<div ref="list" class="list">
<transition-group class="flip-list" name="flip-list" ref="tg1"
tag="div">
<div v-for="(item,index) in items" :key="item.id" class="item">
<slot name="item" :class="{active:item.isOpen}" :item="item"
:index="index">
<v-expansion-panel id="exp1">
<v-expansion-panel-content>
<div slot="header" class="drop-target handle2">
<span>{{item.title}}</span>
</div>
<v-card>
<v-card-text>
<div slot="item">{{Table}}</div>
</v-card-text>
</v-card>
</v-expansion-panel-content>
</v-expansion-panel>
</slot>
</div>
</transition-group>
</div>
</div>
So, the point is : how can I make it so that a datatable appears into accordion ? Like when you click aon one of the titles and it appears instead of some text ?
Thanks a lot
Resolved!
In case of (if people encounter the same problem as I had), here is the code (html / js).
//html goes here
<div id="app">
<v-app id="inspire">
<v-data-table :headers="mainHeaders"
:items="mainItems"
item-key="title"
hide-actions
expand
class="elevation-1">
<template slot="items" scope="props">
<tr #click="props.expanded = !props.expanded">
<td class="text-xs">{{ props.item.title }}</td>
</tr>
</template>
<template slot="expand" scope="props">
<v-data-table :headers="subHeaders"
:items="subItems"
item-key="pain"
hide-actions
class="elevation-10">
<template slot="items" scope="props">
<td class="text-xs">{{ props.item.pain }}</td>
<td class="text-xs">{{ props.item.type }}</td>
</template>
</v-data-table>
</template>
</v-data-table> </v-app>
</div>
//js goes here
export default {
name: 'accordion-table',
mounted () {
},
data () {
return {
mainHeaders: [
{ text: 'Medical informations', value: 'title' }
],
mainItems: [
{ title: 'Medical Checkup' },
{ title: 'Application connected' },
{ title: 'Programs' },
{ title: 'Pain List' }
],
subHeaders: [
{ text: 'Pain', value: 'pain' },
{ text: 'Type', value: 'type' }
],
subItems: [
{ pain: 'Knee', type: '1' },
{ pain: 'Ankle', type: '2' },
{ pain: 'Back', type: '3' },
{ pain: 'Neck', type: '4' }
]
}
}
}
(I used random values)
Hope it helps some of you