<v-data-table
:headers="headers"
:items="desserts"
></v-data-table>
I am trying to display in a column of the table not the values from the items object, but a value which I get from a lookup table via reference key.
Specifically, this means: In my items object "desserts", one attribute does not contain the actual data to be displayed, but a foreign key to a lookup table.
I know that with slots you can manipulate certain values to be displayed. But how to do this most efficiently with a lookup table I don't know. Thank you very much for direction.
This is the data table items object:
items: [
{
category_id: 1, # <--- this is a foreign key to a lookup table
name: 'Mousse au Chocolat',
calories: 159,
},
{
category_id: 2, # <--- this is a foreign key to a lookup table
name: 'Ice cream sandwich',
calories: 237,
}
]
My lookup table data are also stored as json and look like this:
categories: [
{
id: 1, # <--- this is what category_id in the items object refers to
name: 'Good Stuff',
},
{
category: 2, # <--- this is what category_id in the items object refers to
name: 'Cold Stuff',
}
]
This is my headers object:
headers: [
{ text: 'Category', value: 'category_id' },
{ text: 'Name', value: 'name' },
{ text: 'Calories', value: 'calories' },
],
You can use template to display any value you want :
<v-data-table
:headers="headers"
:items="desserts"
>
<template #item.category_id={ item }>
<!-- here get your value from your other json -->
{{ this.categories.find(x => x.category === item.category_id)?.name }}
</template>
</v-data-table>
If you want to make it cleaner you can use a function to get your value.
Here : #item.category_id={ item }, category is the header value
Related
I am trying to create a table where the data can be edited directly in the cells. However, I don't understand how to do this.
I am using Vue.js, Vuexy and vue-good-table 2.21.1. The data is contained in the mediaPlanData variable and is reactive. The table is successfully populating with data and can be modified. However, when I change the data in the cells, this variable does not change. I did not find in the documentation vue-good-table how to do this. Please tell me how can I achieve the desired result?
<vue-good-table
:columns="columns"
:rows="mediaPlanData"
:select-options="{
enabled: true,
selectOnCheckboxOnly: true, // only select when checkbox is clicked instead of the row
selectionInfoClass: 'custom-class',
selectionText: 'rows selected',
clearSelectionText: 'clear',
disableSelectInfo: true, // disable the select info panel on top
selectAllByGroup: true, // when used in combination with a grouped table, add a checkbox in the header row to check/uncheck the entire group
}"
#on-selected-rows-change="onRowClick"
>
<template slot="table-row" slot-scope="props">
<span>
<b-form-input v-model="props.row[props.column.field]"
type="text"
></b-form-input>
</span>
</template>
</vue-good-table>
data() {
return {
pageLength: 10,
columns: [
{
label: 'Channel Code',
field: 'channelCode',
},
{
label: 'Product',
field: 'product',
}
],
mediaPlanData: [] //[ {"channelCode": "P230", "product": "Test"}, {"channelCode": "P230", "product": "Test4"}, {"channelCode": "P230", "product": "Test45"}, {"channelCode": "Р230", "product": "Test2"}]
}
}
methods: {
onRowClick(params) {
console.log('onRowClick' + JSON.stringify(params))
this.$toast({
component: ToastificationContent,
props: {
title: `Hello user! You have clicked on row ${params.selectedRows.product}`,
icon: 'UserIcon',
variant: 'success',
},
})
}
}
I have solved this problem.
Added to b-form-input
#change="changeCell(props.row[props.column.field], props.column.field, props.row.originalIndex)"
And adden method
changeCell(changedData, column, row) {
this.mediaPlanData[row][column] = changedData
},
I'm trying to use Bootstrap-vue tables. I'm trying to add a column "chart" where I can show the data in a chart. Something like:
I'm interested in the "graph" type chart (not shown in the image). Is it something that can be done with the Bootstrap-vue tables?
slot:cell
Here is the code from bootstrap-vue.org:
Inside the template you can put your chart element. And use the 'data' to get the information that you want. Like data.item is the entire that for that row
<template>
<div>
<b-table small :fields="fields" :items="items" responsive="sm">
<!-- A virtual column -->
<template v-slot:cell(index)="data">
{{ data.index + 1 }}
</template>
<!-- A custom formatted column -->
<template v-slot:cell(name)="data">
<b class="text-info">{{ data.value.last.toUpperCase() }}</b>, <b>{{ data.value.first }}</b>
</template>
<!-- A virtual composite column -->
<template v-slot:cell(nameage)="data">
{{ data.item.name.first }} is {{ data.item.age }} years old
</template>
<!-- Optional default data cell scoped slot -->
<template v-slot:cell()="data">
<i>{{ data.value }}</i>
</template>
</b-table>
</div>
</template>
<script>
export default {
data() {
return {
fields: [
// A virtual column that doesn't exist in items
'index',
// A column that needs custom formatting
{ key: 'name', label: 'Full Name' },
// A regular column
'age',
// A regular column
'sex',
// A virtual column made up from two fields
{ key: 'nameage', label: 'First name and age' }
],
items: [
{ name: { first: 'John', last: 'Doe' }, sex: 'Male', age: 42 },
{ name: { first: 'Jane', last: 'Doe' }, sex: 'Female', age: 36 },
{ name: { first: 'Rubin', last: 'Kincade' }, sex: 'Male', age: 73 },
{ name: { first: 'Shirley', last: 'Partridge' }, sex: 'Female', age: 62 }
]
}
}
}
</script>
Here is the documentation
https://bootstrap-vue.org/docs/components/table#tables
Go to Custom data rendering
I have an array of objects (users) gotten by an api request. Its structure is something like this:
api response: (5) [{…}, {…}, {…}, {…}, {…}]
Inside this array, the objects are like this:
0: {
user_address_string: ('Street 1')
id: (3)
avatar: (img')
...
user: {
id: 1
first_name: 'Lucas'
last_name: 'Smith'
}
},
1: {...},
2: {...},
....
]
It is just a sample just to show you the structure.
As you can see, among its properties there's another object named user{}.
I need to display only the properties contained in this object.
I would like to keep the structure of the table I was using before I got this new api (which didn't have objects as properties).
<b-table
responsive
v-if="users && users.length > 0"
:fields="fields"
:items="users"
>
The template should be something like this:
<template slot-scope="data">
{{ data.item.user }}
</template>
data.item should be the single user in the users array of objects, and with .user I should be able to access the properties of its object property user. (Going further data.item.user.first_name, etc, to access the single properties in it). What am I missing?
Nothing is rendered on the screen.
No errors in the console though.
In the script I have:
users: [],
fields: [
{ key: 'id', label: 'ID'},
{ key: 'name', label: 'Name' }
]
So, how should I write the template for displaying the nested object’s properties?
Also, the directive v-if="users && users.length > 0" in the b-table should still work, right? It is still an array, but of objects this time. Please correct me if I am wrong.
Thank you
You can specify nested field keys in dotted notation:
export default {
data() {
return {
users: [],
fields: [
{ key: 'id', label: 'ID'},
{ key: 'user.first_name', label: 'First Name' },
{ key: 'user.last_name', label: 'Last Name' }
]
}
}
As #Troy Morehouse suggested, I just needed to redefine the fields definition as
{ key: 'user.first_name', label: 'First name' }
**UPDATE after #artworkjpm comment:
The HTML code should be something like this:
<b-table
v-if="users && users.length > 0 && !isLoading"
id="table-transition-userList"
:key="users.id"
responsive
:tbody-tr-class="userStatus"
:tbody-transition-props="transProps"
:fields="fields"
:items="users"
>
<template
v-slot:cell(fullName)="data"
>
{{ data.item.user.first_name }} {{ data.item.user.last_name }}
</template>
<template
v-slot:cell(buttons)="data"
>
<b-button
v-b-tooltip.hover
title="See plan"
class="btn-plan p2"
variant="primary"
:disabled="!data.item.user.is_active"
#click.prevent="seePlan(data.item), selectUser(data.item)"
>
<span class="svg-container">
<svg-icon icon-class="route" />
</span>
</b-button>
</template>
</b-table>
**Minor change in fields, but the concept is the same:
fields: [
{ key: 'fullName', label: 'User' },
{ key: 'buttons', label: 'Operations' }
],
Hope it helps.
xx
Whereever you get your data, you can extract the property you want and save locally in this case it would look something like this:
data()
return {
users: []
}
methods: {
async getUsersFromApi(){
const { data: {users }} = await axios.get(...)
users.map(user => {
this.users.push(user.user)
}
An easy way to do it is by using a formatter in fields definition:
data() {
return {
fields: [
{
key: "avg_score",
label: this.$t("avgScore"),
sortable: true,
sortByFormatted: true,
formatter: (value, key, item) => item.stats.avg_score?.text
},
],
items: [...your item list]
}
}
And in the template:
<b-table :items="items" :fields="fields"></b-table>
The formatter will print the specified key or value automatically.
Im new in Vue and Vuetify. I use vuetify data table in my project. Everything working fine except the Search options. I use everything as the documentation. But still not get any solution.
As the basic requirements I use these code to adding search
Template
<v-text-field
v-model="search"
append-icon="search"
label="Search"
single-line
hide-details
></v-text-field>
<v-data-table
:headers="headers"
:items="sales"
:search="search"
:rows-per-page-items="rowsPerPageItems"
row
wrap
class="elevation-1"
>
Script
data(){
return{
search: '',
}
}
Here is the complete code
https://github.com/Shakilzaman87/pukucrm/blob/master/src/components/sales/Sales.vue
Vuetify should warn you in the console about this:
[Vuetify] Headers must have a value property that corresponds to a
value in the v-model array in "v-data-table"
So you can fix the search option by adding values:
headers: [
{ text: 'Item Name', value: 'item_name' },
{ text: 'Unit Price', value: 'price' },
{ text: 'Quantity', value: 'quantity' },
{ text: 'Customer', value: 'customer' },
{ text: 'Created', value: 'timestamp' },
{ text: 'Total', value: 'total' },
{ text: 'Action', value: 'item_name' }
]
(data from your repo)
As of date of posting, I cannot find any documentation to use the "custom filter" prop in data tables.
I just want to create a custom filter to filter my data table by headers.
I have a dropdown, and when user click on one of the options for the dropdown, it will filter the list for one specific header.
Example:
Dropdown options:
Food type: fruit, meat, vegetable
Bakchoi (vegetable)
Pork (meat)
Chicken Thigh (meat)
watermelon (fruit)
If I select dropdown as meat, it should only show me pork and chicken thigh.
Looking at the code on Github1, it looks like the customFilter prop is used to overwrite the default method used to determine how the filter prop is applied to the items in the table.
The default customFilter method applies the filter function to each property name of each item object and filters out any items that don't include one property name that passes the filter:
customFilter: {
type: Function,
default: (items, search, filter) => {
search = search.toString().toLowerCase()
return items.filter(i => (
Object.keys(i).some(j => filter(i[j], search))
))
}
},
You might want to overwrite this function if you wanted to prevent any columns from being included in the filter or if there were specific rows that you always wanted to prevent from being filtered out.
You'll notice that the method also depends on the search prop, which must be a string.
All that said, you really don't need to use that prop for what you want to do. You should just make a computed property to filter the items based on your dropdown value and pass that computed property as the items prop.
Here's an example:
new Vue({
el: '#app',
data() {
return {
food: [
{ name: 'Bakchoi', type: 'vegetable', calories: 100 },
{ name: 'Pork', type: 'meat', calories: 200 },
{ name: 'Chicken Thigh', type: 'meat', calories: 300 },
{ name: 'Watermelon', type: 'fruit', calories: 10 },
],
headers: [
{ text: 'Name', align: 'left', value: 'name' },
{ text: 'Food Type', align: 'left', value: 'type' },
{ text: 'Calories', align: 'left', value: 'calories' },
],
foodType: null,
};
},
computed: {
filteredItems() {
return this.food.filter((i) => {
return !this.foodType || (i.type === this.foodType);
})
}
}
})
<script src="https://unpkg.com/vue#2.4.2/dist/vue.js"></script>
<script src="https://unpkg.com/vuetify#0.15.2/dist/vuetify.js"></script>
<link rel="stylesheet" href="https://unpkg.com/vuetify#0.15.2/dist/vuetify.min.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons">
<div id="app">
<v-app>
<v-select
label="Food Type"
:items="['vegetable', 'meat', 'fruit']"
v-model="foodType"
></v-select>
<v-data-table
:headers="headers"
:items="filteredItems"
hide-actions
>
<template slot="items" scope="{ item }">
<td>{{ item.name }}</td>
<td>{{ item.type }}</td>
<td>{{ item.calories }}</td>
</template>
</v-data-table>
</v-app>
</div>
This answer was written when Vuetify was at v0.15.2. The source code for the VDataTable component at that version can be found here.
You can also go the customFilter approach like this, I've restricted the search to the type field.
new Vue({
el: '#app',
data() {
return {
food: [
{ name: 'Bakchoi', type: 'vegetable', calories: 100 },
{ name: 'Pork', type: 'meat', calories: 200 },
{ name: 'Chicken Thigh', type: 'meat', calories: 300 },
{ name: 'Watermelon', type: 'fruit', calories: 10 },
],
headers: [
{ text: 'Name', align: 'left', value: 'name' },
{ text: 'Food Type', align: 'left', value: 'type' },
{ text: 'Calories', align: 'left', value: 'calories' },
],
search: '',
};
},
methods: {
customFilter(items, search, filter) {
search = search.toString().toLowerCase()
return items.filter(row => filter(row["type"], search));
}
}
})
<script src="https://unpkg.com/vue#2.4.2/dist/vue.js"></script>
<script src="https://unpkg.com/vuetify#0.15.2/dist/vuetify.js"></script>
<link rel="stylesheet" href="https://unpkg.com/vuetify#0.15.2/dist/vuetify.min.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons">
<div id="app">
<v-app>
<v-select
label="Food Type"
:items="['vegetable', 'meat', 'fruit']"
v-model="search"
></v-select>
<v-data-table
:headers="headers"
:items="food"
:search="search"
:custom-filter="customFilter"
hide-actions
>
<template slot="items" scope="{ item }">
<td>{{ item.name }}</td>
<td>{{ item.type }}</td>
<td>{{ item.calories }}</td>
</template>
</v-data-table>
</v-app>
</div>
In my case I have 2 different way of filtering which are search bar and drop down. I tried to use custom-filter for both of them, but it doesn't work, so I came up with another approach
<v-text-field v-model="search" label="Label"></v-text-field>
<v-select
v-model="select"
:items="food"
item-text="type"
item-value="type"
:label="Types"
#change="filterFoodUsingDropDown"
>
</v-select>
<v-data-table
:search="search"
:headers="headers"
:items="food"
:custom-filter="filterFoodUsingSearchbar"
>
data() {
return {
food: [
{ name: 'Bakchoi', type: 'vegetable', calories: 100 },
{ name: 'Pork', type: 'meat', calories: 200 },
{ name: 'Chicken Thigh', type: 'meat', calories: 300 },
{ name: 'Watermelon', type: 'fruit', calories: 10 },
],
headers: [
{ text: 'Name', align: 'left', value: 'name' },
{ text: 'Food Type', align: 'left', value: 'type' },
{ text: 'Calories', align: 'left', value: 'calories' },
],
search: '',
select: '',
};
},
methods: {
filterFoodUsingSearchbar(items, search, filter) {
// Condition
}
filterFoodUsingDropDown() {
if (this.select !== '') {
// In this case I use vuex to store the original data of the
food so that all the data is still exist even we filtered it out
this.food = this.$store.state.food.filter((item) => item.type === this.select)
}
}