I am developing a node and electron application. I'm using vuetify to generate the table (v-data-table) that loads data from an oracle database.
The data changes according to the value of input, but the table does not update!
When input values change: process.env.ANO_SEM = input.val()
then i call loadData() function
process.env.ANO_SEM is used as parameter for sql query in getEventos() function
My code:
$('#input').keyup(e => {
if (e.keyCode == 13) {
process.env.ANO_SEM = $('#input').val()
loadData()
}
})
// Get data from BD
async function getEventos() {
const sql = await fs
.readFileSync(path.join(process.env.ROOT_PATH, 'src/db/sql/get-evento.sql'))
.toString()
return await db.getData(sql, [process.env.ANO_SEM])
}
async function loadData() {
let data = await getEventos()
console.log(data) // the data aways change, but the table never update
new Vue({
el: '#app',
methods: {
rowClick(idEvento) {
require(path.join(process.env.CTRL_PATH, './evento/evento-ctrl.js'))(
window.$,
idEvento
)
}
},
data: function() {
return {
selectedItem: 'Sample',
pagination: {
sortBy: 'ID'
},
headers: [
{ text: 'ID', value: 'ID', align: 'center', width: '10%' },
{ text: 'Descrição', value: 'DESCRICAO', align: 'left', width: '60%' },
{ text: 'Período', value: 'PERIODO', align: 'left', width: '20%' },
{
text: 'Data Impressão',
value: 'DATA_IMPRESSAO',
align: 'left',
width: '10%'
}
],
eventos: data
}
}
})
}
my html:
<div id="app" class="table-eventos">
<v-app>
<v-data-table
:headers="headers"
:items="eventos"
:rows-per-page-items="[100]"
item-key="name"
class="elevation-1"
>
<!-- :pagination.sync="pagination" -->
<template slot="items" slot-scope="props">
<tr #click="rowClick(props.item.ID)">
<td class="text-xs-left">{{ props.item.ID }}</td>
<td class="text-xs-left">{{ props.item.DESCRICAO }}</td>
<td class="text-xs-left">{{ props.item.PERIODO }}</td>
<td class="text-xs-left">{{ props.item.DATA_IMPRESSAO }}</td>
</tr>
</template>
</v-data-table>
</v-app>
</div>
So basically what happens in your code is you load your vue into #app, this get rendered and your new #app element looks different - but still get linked to your new vue and rendered - but this time vue somehow fails.
long story short - put your render element into a static template that you can rerender:
i made a simple example from your sketch:
Vue.config.productionTip = false;
const mockData = Array.from({length: 100}, (_, i) => [{
DATA_IMPRESSAO: `mock${i}`,
PERIODO: `mock${i}`,
DESCRICAO: `mock${i}`,
ID: `mock${i}`,
}])
let nonReactiveIndex = Math.floor(Math.random()*mockData.length)
setInterval(() =>
new Vue({
template: "#example",
vuetify: new Vuetify(),
mounted() {
nonReactiveIndex = Math.floor(Math.random()*mockData.length)
console.log("mounted", nonReactiveIndex);
},
data() {
return {
headers: [
{ text: "ID", value: "ID", align: "center", width: "10%" },
{ text: "Descrição", value: "DESCRICAO", align: "left", width: "60%" },
{ text: "Período", value: "PERIODO", align: "left", width: "20%" },
{
text: "Data Impressão",
value: "DATA_IMPRESSAO",
align: "left",
width: "10%"
}
],
eventos: mockData[nonReactiveIndex]
};
},
}
).$mount('#app'), 2000)
<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#latest/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/vuetify/dist/vuetify.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify/dist/vuetify.js"></script>
<div id="app" class="table-eventos">
</div>
<template id="example">
<v-app>
<v-data-table
:headers="headers"
:items="eventos"
:rows-per-page-items="[100]"
item-key="name"
class="elevation-1"
>
<!-- :pagination.sync="pagination" -->
<template slot="items" slot-scope="props">
<tr #click="rowClick(props.item.ID)">
<td class="text-xs-left">{{ props.item.ID }}</td>
<td class="text-xs-left">{{ props.item.DESCRICAO }}</td>
<td class="text-xs-left">{{ props.item.PERIODO }}</td>
<td class="text-xs-left">{{ props.item.DATA_IMPRESSAO }}</td>
</tr>
</template>
</v-data-table>
</v-app>
</template>
It is a better design to implement everything into the vue component and let vue handle reactivity:
Vue.config.productionTip = false;
const mockData = Array.from({length: 100}, (_, i) => [{
DATA_IMPRESSAO: `mock${i}`,
PERIODO: `mock${i}`,
DESCRICAO: `mock${i}`,
ID: `mock${i}`,
}])
new Vue({
template: "#example",
vuetify: new Vuetify(),
mounted() {
console.log("mounted", this.reactiveIndex);
},
data() {
return {
reactiveIndex : Math.floor(Math.random()*mockData.length),
headers: [
{ text: "ID", value: "ID", align: "center", width: "10%" },
{ text: "Descrição", value: "DESCRICAO", align: "left", width: "60%" },
{ text: "Período", value: "PERIODO", align: "left", width: "20%" },
{
text: "Data Impressão",
value: "DATA_IMPRESSAO",
align: "left",
width: "10%"
}
],
};
},
computed: {
eventos(){ return mockData[this.reactiveIndex] }
},
methods: {
load(){
this.reactiveIndex = Math.floor(Math.random()*mockData.length)
console.log(this.reactiveIndex)
}
}
}
).$mount('#app')
<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#latest/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/vuetify/dist/vuetify.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify/dist/vuetify.js"></script>
<div id="app" class="table-eventos">
</div>
<template id="example">
<v-app>
<v-btn #click="load">load</v-btn>
<v-data-table
:headers="headers"
:items="eventos"
:rows-per-page-items="[100]"
item-key="name"
class="elevation-1"
>
<!-- :pagination.sync="pagination" -->
<template slot="items" slot-scope="props">
<tr #click="rowClick(props.item.ID)">
<td class="text-xs-left">{{ props.item.ID }}</td>
<td class="text-xs-left">{{ props.item.DESCRICAO }}</td>
<td class="text-xs-left">{{ props.item.PERIODO }}</td>
<td class="text-xs-left">{{ props.item.DATA_IMPRESSAO }}</td>
</tr>
</template>
</v-data-table>
</v-app>
</template>
I took the liberty of setting up the project structure with vue-cli and also added mock data and a text-field, let me know if you have any doubts, I'm pretty sure you'll get the point, clone the public repo, run
npm install
and then
npm run serve
https://github.com/sdpotts93/vue-simple-table
Related
I have a table layout that currently looks like this:
This works well because I can place all my data for one row inside of my item object, and just render the TableRow object as <tr><td>...</td><td>...</td>....
One solution could be to subdivide my item object by column, and return only the corresponding value:
<tbody>
<tr v-for="(item, rowIndex) in store.selectedItems" :key="item.id">
<!-- iterate over each column within each row -->
<td v-for="(col, colIndex) in columns" :key="colIndex">
{{ item[col.field] }}
</td>
</tr>
</tbody>
However, ideally I would like to define the whole column in a single TableCol component instead of having to render the entire table like this. Is this somehow possible?
As per my understanding you want to create a separate shared component for column, So that you can reuse that anywhere by just passing a dynamic props. If Yes, Here you go :
Vue.component('tableCol', {
props: ['column', 'rowitem'],
template: `<td>{{ rowitem[column.field] }}</td>`
});
var app = new Vue({
el: '#app',
data: {
store: {
selectedItems: [{
id: 1,
name: 'Alpha',
age: 21
}, {
id: 2,
name: 'Beta',
age: 25
}, {
id: 3,
name: 'Gamma',
age: 30
}]
},
columns: [{
field: 'name'
}, {
field: 'age'
}]
}
});
table, tr, td {
border: 1px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<table>
<tbody>
<tr v-for="item in store.selectedItems" :key="item.id">
<template>
<table-col v-for="(column, index) in columns" :column="column" :rowitem="item" :key="index"/>
</template>
</tr>
</tbody>
</table>
</div>
I have a v-data-table that groups items. When using a custom header with v-slot like below - how do I enable the built in sort functionality that's on the default header?
is there a way I can trigger the built in sort function with my sortThisColumn function, or any other way? At the moment, my table headings are unclickable.
<v-app class="v-app-custom" v-if="myItems.length > 0">
<div class="section">
<v-data-table
:headers="headers"
:items="myItems"
:items-per-page="30"
hide-default-footer
hide-default-header
item-key="uuid"
group-by="mentor_id"
class="mentor-table"
>
<template v-slot:header="{ props }">
<thead class="v-data-table-header">
<tr>
<th
class="text-start"
v-for="header in props.headers"
:key="header.value"
:style="{ width: `${header.width}px` }"
#click="sortThisColumn(header.value)"
>
<span>{{ header.text }}</span>
<v-icon v-if="header.sortable" color="white" small>{{
sort[header.value] == "desc"
? "fa-chevron-down"
: "fa-chevron-up"
}}</v-icon>
</th>
</tr>
</thead>
</template>
<template
v-slot:group.header="{ group, props, headers }"
sort-icon="fa-chevron-down"
>
Table headers:-
headers: [
{
text: "Status",
value: "status",
width: 82,
sortable: false,
},
{ text: "Type", value: "type", width: 140, sortable: true },
{ text: "Creator", value: "creator", width: 140, sortable: true },
{ text: "Date", value: "due_date", width: 141, sortable: true },
{ text: "Mentor", value: "mentor", width: 141, sortable: true },
],
Check this codesanbox I made: https://codesandbox.io/s/stack-70975751-p9msn?file=/src/components/CustomSorting.vue
You can define a custom sorting function in your headers array like this. I've done this in the past to add a custom sort to one of my date columns.
headers: [
{
text: 'Date',
value: 'date',
align: 'center',
sort: (a, b) => {
var date1 = a.replace(/(\d+)\/(\d+)\/(\d+)/, '$3/$2/$1')
var date2 = b.replace(/(\d+)\/(\d+)\/(\d+)/, '$3/$2/$1')
return date1 < date2 ? -1 : 1
}
}
]
Knowing this, you could do something like this in your case.
data: (vm) => ({
headers: [
{
text: 'Date',
value: 'date',
sort: (a,b) => {
return vm.myCustomSort(a,b)
}
},
]
})
guys I need to display an image into the field of Datatable I've tried everything but nothing works,
at that moment my code is:
<q-table
:data="driverTable.data"
:columns="driverTable.columns"
row-key="objectId"
>
<template v-slot:no-data="{ icon, message, filter }">
<div class="full-width row flex-center q-gutter-sm">
<q-icon size="2em" name="sentiment_dissatisfied" />
<span>{{ message }}</span>
<q-icon size="2em" :name="filter ? 'filter_b_and_w' : icon" />
</div>
</template>
<template v-slot:top="props">
<q-btn flat round dense
#click="props.toggleFullscreen" color="grey-8" class="q-ml-md"
:icon="props.inFullscreen ? 'fullscreen_exit' : 'fullscreen'"
:v-bind="props.inFullscreen"
:title="props.inFullscreen ? 'Minimiser' : 'Plain ecran'"
/>
</template>
</q-table>
and my js code:
data () {
return {
driverTable: {
columns: [
{
name: 'objectId',
field: row => row.objectId,
format: val => ''
},
{
name: 'name',
label: 'Nom complete',
align: 'left',
field: row => row.name,
format: val => `${val}`,
sortable: true,
classes: 'ellipsis'
},
{
name: 'carPermit',
required: true,
label: 'Permit',
field: row => row.carPermit, //
align: 'center',
format: val => `${'<img height="75%" width="75%" src="' + val + '"/>'}`
}
],
data: []
}
}
}
I've tried <img> into field format and bild on it the url source but it gives me just the balise on stiring format
please follow this
<q-table
:data="driverTable.data"
:columns="driverTable.columns"
row-key="objectId"
>
<template v-slot:no-data="{ icon, message, filter }">
<div class="full-width row flex-center q-gutter-sm">
<q-icon size="2em" name="sentiment_dissatisfied" />
<span>{{ message }}</span>
<q-icon size="2em" :name="filter ? 'filter_b_and_w' : icon" />
</div>
</template>
<template v-slot:top="props">
<q-btn flat round dense
#click="props.toggleFullscreen" color="grey-8" class="q-ml-md"
:icon="props.inFullscreen ? 'fullscreen_exit' : 'fullscreen'"
:v-bind="props.inFullscreen"
:title="props.inFullscreen ? 'Minimiser' : 'Plain ecran'"
/>
</template>
<template v-slot:body-cell-carPermit="props">
<q-avatar>
<img :src="props.row.carPermit">
</q-avatar>
</template>
</q-table>
and remove format and field keys from cardPermit
data () {
return {
driverTable: {
columns: [
{
name: 'objectId',
field: row => row.objectId,
format: val => ''
},
{
name: 'name',
label: 'Nom complete',
align: 'left',
field: row => row.name,
format: val => `${val}`,
sortable: true,
classes: 'ellipsis'
},
{
name: 'carPermit',
required: true,
label: 'Permit',
align: 'center',
}
],
data: []
}
}
}
I have the following HTML
<div id="app">
<v-app id="inspire">
<v-treeview :items="items"></v-treeview>
</v-app>
</div>
and the following Javascript:
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: () => ({
items: [
{
id: 1,
name: 'Applications :',
children: [
{ id: 2, name: 'Calendar : app' },
],
},
{
id: 15,
name: 'Downloads :',
children: [
{ id: 16, name: 'October : pdf' },
],
}
],
}),
})
Let's say that I want to make the font-size of the Downloads label bigger or make it bold.
Is this possible with Vuetify and it's v-treeview? If so, how? There does not appear to be a way to control the styling of the labels.
Everything can be controlled in Vuetify. It's easier with some things and harder with others:
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: () => ({
items: [{
id: 1,
name: 'Applications :',
children: [{
id: 2,
name: 'Calendar : app'
}, ],
},
{
id: 15,
name: 'Downloads :',
children: [{
id: 16,
name: 'October : pdf'
}, ],
}
],
}),
})
.large {
font-size: 20px;
font-weight: 700;
}
<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#5.x/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.min.css" rel="stylesheet">
<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>
<div id="app">
<v-app id="inspire">
<v-treeview :items="items">
<template v-slot:label="{ item }">
<span
:class="{ large: !!(item.id === 15) }"
>
{{ item.name }}
</span>
</template>
</v-treeview>
</v-app>
</div>
Look at the API description on Vuetify's component pages, and look for slots - if a component has the slot you look for, then you can work with it quite easily. If not, then you may have to tweak classes or even the components.
v-treeview's documentation: https://vuetifyjs.com/en/components/treeview/
VTreeview has a slot called label. You can "invoke" that slot by:
<v-treeview :items="items">
<template v-slot:label="{ item, leaf, selected, indeterminate, active, open }">
<!-- your code comes here --->
</template>
</v-treeview>
You can pass in item, leaf, selected, indetermintae, active or open props, that you can use in the scope of the <template></template>. In my snippet, we only needed the item, that's why I only passed that down.
My snippet only works, if you have an item with id: 15 - but you can put any logic there: calling methods, computed properties, etc.
For more information you can look in Vue scoped slots and destructuring slot props.
You must to use the slot label. Try something like this:
<v-treeview :items="items">
<template v-slot:label="{ item }">
<span style="font-weight: bold">{{ item.name }}</span>
</template>
</v-treeview>
I made a vue.js bootstrap table for loading some data from local JSON files.
I'm trying to implement show/hide columns via checkboxes.
I think I've solved most of the problem, but the problem is when I hide a column and then press on that same checkbox again (to make column visible again) I lose the order of table (that column becomes last column) and so on.
For example if I hide "Timestamp" column which is first table header in my table and then press to show it again it is no longer on first place, instead it gets created on last place.
https://imgur.com/BaTfgci --> this is how app looks right now
https://codepen.io/frane_caleta/pen/KKPMKrL --> codepen of my code, you won't be able to load it without JSON file though
https://imgur.com/a/23jx0lZ --> JSON data example
First time asking question here, so feel free to ask me if you need some more information to solve the problem :)
<b-form-group label="Hide columns: ">
<b-form-checkbox-group id="checkbox-group-1" v-model="selected" :options="fields" name="flavour-1">
</b-form-checkbox-group>
</b-form-group>
//my table
<b-table id="myTable"
:small="small"
:bordered="bordered"
hover head-variant="dark"
stacked="md"
:items="cptItems"
:fields="selected"
:current-page="currentPage"
:per-page="perPage"
:filter="filter"
:sort-by.sync="sortBy"
:sort-desc.sync="sortDesc"
#filtered="onFiltered"
:tbody-tr-class="rowClass"
v-if="selected.length > 0">
</b-table>
//Javascript file
function initializeVue() {
return new Vue({
el: '#app',
data() {
return {
items: data.logdatas,
selected: [],
fields: [{
text: 'Origin',
value: {
key: 'origin',
label: 'Origin',
sortable: true,
class: 'text-center',
index: 0
}
},
{
text: 'Timestamp',
value: {
key: 'timeStamp',
label: 'Timestamp',
sortable: true,
class: 'text-center',
index: 1
}
},
{
text: 'Level',
value: {
key: 'level',
label: 'Level',
sortable: true,
class: 'text-center',
index: 2
}
}, ...there are 4 more fields here like this...
//my method for creating those checkboxes
created() {
this.selected = this.fields.map(field => field.value);
}
the selected data is your culprit. b-checkbox-group :selection lists items in order of selection.
example2
b-table :fields lists columns in the order of the items.
better make a static fields-list and filter by selection.
// make this data or property
let columnNames = ["one", "two", "three", "infinity", "pi"];
// make this data
let selected = []
//make this computed // can be optimized
let activeFields = columNames.filter(name => selected.includes(name))
// more like this
export default {
data(){
return {
selected: [],
columnNames: ['name1', 'name2']
},
computed(){
activeColumns(){
return this.columnNames.filter(this.selected.includes) || []
}
}
const app = new Vue({
data(){
return {
currentPage: 0,
perPage: 10,
fields: ['age', 'first_name', 'last_name'],
//selected: [],
selected: ['age', 'first_name', 'last_name'],
items: [
{ age: 40, first_name: 'Dickerson', last_name: 'Macdonald' },
{ age: 21, first_name: 'Larsen', last_name: 'Shaw' },
{ age: 89, first_name: 'Geneva', last_name: 'Wilson' },
{ age: 38, first_name: 'Jami', last_name: 'Carney' }
]
}
},
computed: {
activeFields(){
return this.fields.filter(name => this.selected.includes(name))
}
}
}).$mount("#app");
<!-- Add this to <head> -->
<!-- Load required Bootstrap and BootstrapVue CSS -->
<link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap/dist/css/bootstrap.min.css" />
<link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.min.css" />
<!-- Load polyfills to support older browsers -->
<script src="//polyfill.io/v3/polyfill.min.js?features=es2015%2CIntersectionObserver" crossorigin="anonymous"></script>
<!-- Load Vue followed by BootstrapVue -->
<script src="//unpkg.com/vue#latest/dist/vue.min.js"></script>
<script src="//unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.min.js"></script>
<div id="app">
<b-form-group label="Hide columns: ">
<b-form-checkbox-group id="checkbox-group-1" v-model="selected" :options="fields" name="flavour-1">
</b-form-checkbox-group>
</b-form-group>
<b-table id="myTable"
:bordered="true"
hover head-variant="dark"
stacked="md"
:items="items"
:fields="selected"
:current-page="currentPage"
:per-page="perPage"
tbody-tr-class="row-class"
v-if="selected.length > 0">
</b-table>
<b-table id="myTable"
:bordered="true"
hover head-variant="dark"
stacked="md"
:items="items"
:fields="activeFields"
:current-page="currentPage"
:per-page="perPage"
tbody-tr-class="row-class"
v-if="selected.length > 0">
</b-table>
</div>