How do I use a primary key in Bootstrap-Table-Vue? But the problem is that sometimes the id fields of the data are 0. I want automatic id assignment.
My Data:
items = [{id:0,name:'Ali'},
{id:1,name:'Veli'},
{id:2,name:'Berkay'},
{id:0,name:'Mehmet'},
]
<b-table
:items="filteredItems"
:fields="filteredFields"
primary-key="id"
/>
In that case, you can iterate the items array and add primaryKey property in each object which will contain the index.
const items = [
{id:0, name:'Ali'},
{id:1, name:'Veli'},
{id:2, name:'Berkay'},
{id:0, name:'Mehmet'}
];
items.forEach((obj, index) => {
obj['primaryKey'] = index
});
console.log(items);
Working demo with b-data-table :
new Vue({
el: '#app',
data: {
items: [
{id:0,name:'Ali'},
{id:1,name:'Veli'},
{id:2,name:'Berkay'},
{id:0,name:'Mehmet'}
],
fields: [
{
key: 'name',
label: 'Name'
}
],
},
mounted() {
this.items.forEach((obj, index) => {
obj['primaryKey'] = index
});
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap-vue#2.0.0-rc.11/dist/bootstrap-vue.common.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.2/css/bootstrap.css"/>
<div id="app">
<b-table class="mt-4" :items="items" :fields="fields" primary-key="primaryKey"/>
</div>
Related
I am currently writing my first full-stack app. I am using bootstrap <b-table> to display content. On row-click, I expand the row to display nested data. Is there a way to iterate over the nested data and display it in nested rows within the parent b-table?
Currently, I can display the data, however it displays in a single row.
component.vue:
<template>
<div id="report-table" class="report-table">
<b-container>
<b-table striped hover sticky-header="100%"
:items="reports"
:fields="fields"
responsive="xl"
#click="clearRowClick"
#row-clicked="reports=>$set(reports, '_showDetails', !reports._showDetails)"
>
<template slot="row-details" slot-scope="row">
<template v-for="(proc, index) in row.item.Processes">
<b-tr :key=index>
<td>{{ proc.Name }}</td>
<td>{{ proc.Id }}</td>
</b-tr>
</template>
</template>
</b-table>
</b-container>
</div>
</template>
example
In the attached image, the bottom row has been clicked. The content is displayed within a single row, but I would like it to be separate rows, so later I can further click on them to display even more nested content.
data example:
{"_id": <id>, "Hostname": <hostname>, "Address": <address>, "Processes": [{"Name": ApplicationHost, ...}, {"Name": svchost, ...}]
If this is not possible, is there some other Bootstrap element that makes more sense to achieve what I want?
To strictly answer your question: no, a BootstrapVue <b-table>'s row-details row can't be expanded into more than one row.
The row-details row has severe limitations:
it's only one row
it's actually only one cell which, through use of colspan is expanded to the full width of the row (which means you can't really use the table columns to align the content of the row-details row).
But... this is web. In web, because it's virtual, virtually anything is possible. When it's not, you're doing-it-wrong™.
What you want is achievable by replacing rows entirely when a row is expanded, using a computed and concatenating the children to their parent row when the parent is in expanded state. Proof of concept:
Vue.config.productionTip = false;
Vue.config.devtools = false;
new Vue({
el: '#app',
data: () => ({
rows: [
{id: '1', name: 'one', expanded: false, children: [
{id: '1.1', name: 'one-one'},
{id: '1.2', name: 'one-two'},
{id: '1.3', name: 'one-three'}
]},
{id: '2', name: 'two', expanded: false, children: [
{id: '2.1', name: 'two-one'},
{id: '2.2', name: 'two-two'},
{id: '2.3', name: 'two-three'}
]}
]
}),
computed: {
renderedRows() {
return [].concat([...this.rows.map(row => row.expanded
? [row].concat(row.children)
: [row]
)]).flat()
}
}
})
tr.parent { cursor: pointer }
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<table>
<tr v-for="row in renderedRows" :key="row.id"
#click="row.children && (row.expanded = !row.expanded)"
:class="{parent: row.children}">
<td>{{row.id}}</td>
<td>{{row.name}}</td>
</tr>
</table>
</div>
The example is rather basic (I haven't added BootstrapVue to it, nor have I used its fancy <b-table>), but it demonstrates the principle. Apply it to <b-table>'s :items.
One could even take it a step further and make it recursive, by moving the expansion logic into a method:
new Vue({
el: '#app',
data: () => ({
fields: ['id', { key: 'expanded', label: ''}, 'name'],
rows: [{
id: '1',
name: 'one',
expanded: false,
children: [
{ id: '1.1', name: 'one-one' },
{ id: '1.2', name: 'one-two' },
{
id: '1.3',
name: 'one-three',
expanded: false,
children: [
{ id: '1.3.1', name: 'one-three-one' },
{ id: '1.3.2', name: 'one-three-two' }
]
}
]
},
{
id: '2',
name: 'two',
expanded: false,
children: [
{ id: '2.1', name: 'two-one' },
{ id: '2.2', name: 'two-two' },
{ id: '2.3', name: 'two-three' }
]
}
]
}),
computed: {
items() {
return [].concat(this.rows.map(row => this.unwrapRow(row))).flat()
}
},
methods: {
unwrapRow(row) {
return row.children && row.expanded
? [row].concat(...row.children.map(child => this.unwrapRow(child)))
: [row]
},
tbodyTrClass(row) {
return { parent: row.children?.length, child: row.id.includes('.') }
}
}
})
.table td:not(:last-child) { width: 80px; }
.table .bi { cursor: pointer }
tr.child {
background-color: #f5f5f5;
font-style: italic;
}
<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" />
<script src="//cdn.jsdelivr.net/npm/vue#2.6.14"></script>
<script src="//unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.min.js"></script>
<script src="//unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue-icons.min.js"></script>
<div id="app">
<b-table :items="items"
:fields="fields"
:tbody-tr-class="tbodyTrClass">
<template #cell(expanded)="{item}">
<b-icon v-if="item.children"
:icon="item.expanded ? 'chevron-up' : 'chevron-down'"
#click="item.expanded = !item.expanded" />
</template>
</b-table>
</div>
One approach (that I've personally used in the past) is simply to put a nested <b-table> inside your child row-details for child data, instead of trying to add them to the outer table.
It's also worth noting that adding child data rows to the outer table could be visually confusing if they don't look distinct enough from their parents.
Example:
new Vue({
el: '#app',
data() {
return {
reports: [{_id: 'ID#1', Hostname: 'Host1', Address: 'Addr1', Processes: [{Name: 'ApplicationHost', Id: '1'}, {Name: 'svchost', Id: '2'}]},
{_id: 'ID#2', Hostname: 'Host2', Address: 'Addr2', Processes: [{Name: 'ApplicationHost', Id: '3'}, {Name: 'svchost', Id: '4'}]},],
fields: ['Hostname', 'Address'],
}
},
});
<!-- Import Vue and Bootstrap-Vue -->
<link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap#4/dist/css/bootstrap.min.css" /><link type="text/css" rel="stylesheet" href="//unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.min.css" /><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-table
bordered
striped
hover
:items="reports"
:fields="fields"
#row-clicked="reports=>$set(reports, '_showDetails', !reports._showDetails)"
>
<!-- <b-table> nested inside 'row-details' slot: -->
<template #row-details="row">
<b-table
bordered
:items="row.item.Processes"
:fields="['Name', 'Id']"
></b-table>
</template>
</b-table>
</div>
I'm new to Vue and i'm dealing with datatables. I'm using Vuetify's datatables to create a component that, on page load, sends a request to my backend, receives some data and shows that data on a datatable.
This is my current code:
<template>
<v-data-table
:headers="headers"
:items="balances"
:items-per-page="5"
class="elevation-1"
></v-data-table>
</template>
<script>
export default {
data() {
return {
search: '',
headers: [
{ text: 'Asset', value: 'symbol' },
{ text: 'Amount', value: 'amount' },
],
balances: [],
}
},
mounted() {
this.fetchData()
},
methods: {
fetchData() {
fetch('MYURL')
.then(response => response.json())
.then(data => {
this.balances = data;
})
}
}
}
</script>
The problem i'm facing now is adding a button to each row in the table, and this button should send a request to my backend with that row's data, so i need to add a button that, when clicked, can fetch the row's data. Is there any way to do that? I tried looking into Vuetify's docs but i didn't found much about a task like this one.
You can add a new column, set value to action for example, and add a slot in the table as follows:
new Vue({
el:"#app",
vuetify: new Vuetify(),
data() {
return {
search: '',
headers: [
{ text: 'Asset', value: 'symbol' },
{ text: 'Amount', value: 'amount' },
{ text: 'Send', value: 'action' }
],
balances: [
{ symbol: "$", amount: 100 },
{ symbol: "$", amount: 200 },
],
}
},
methods: {
sendRequest(rowData) {
console.log(rowData)
}
}
});
<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">
<v-app id="app">
<v-data-table
:headers="headers"
:items="balances"
:items-per-page="5"
class="elevation-1"
>
<template v-slot:item.action="{ item }">
<v-btn #click="sendRequest(item)">
Send
</v-btn>
</template>
</v-data-table>
</v-app>
How can I make the template dynamic? I want field.title.arrayName to be text1.arrayName, text2.arrayName and so on...
Vue.component('box', {
props: ['field'],
template: `
<div>
<p>Total:{{ field.title + '.arrayName.length' }}</p>
</div>
<div>
<card-stack v-for="x in field.title + '.arrayName'" v-bind:course="x"></card-stack>
</div>
`
})
new Vue({
el: 'main',
data: {
fields: [
{ title: 'text1' },
{ title: 'text2' },
],
}
})
And that's my HTML:
<main>
<box v-for="x in fields" v-bind:field="x"></box>
</main>
Also, text1 and text2 are component names:
var text1 = new Vue({
el: '#text1',
data: {
arrayName: [
{ title: 'example' },
],
}
})
note
Please edit my question title as I really don't know how to describe it
using eval() works:
<p>Total:{{ eval(field.title + '.arrayName.length') }}</p>
<card-stack v-for="x in eval(field.title + '.arrayName')" :course="x"></card-stack>
I need to populate a Vuetify select, but there is a problem with it, my Get method returns data, but the vuetify select only show something like this:
The text shows valid data:
[ { "id": 1 }, { "id": 2 } ]
And to populate the Select i follow the documentatioin adding :items="entidades" and :item-text="entidades.id" and :item-value="entidades.id"
<v-select :items="entidades" :item-text="entidades.id" :item-value="entidades.id" single-line auto prepend-icon="group_work" label="Seleccionar Grupo"></v-select>
This is my code form script
`data() {
return(){
entidades: [{
id: ''
}],
}
}`
I already tried to put 0, but it's the same result.
My axios.get method.
axios.get('http://localhost:58209/api/GetEntidades', {
headers:{
"Authorization": "Bearer "+localStorage.getItem('token')
}
})
.then(response => {
console.log(response)
this.entidades = response.data;
})
.catch(error => {
console.log(error.response)
});
Thank you so much
item-text and item-value are the name of the properties each item will display and use as value, respectively. So use item-text="id" item-value="id":
<v-select :items="entidades" item-text="id" item-value="id" single-line auto prepend-icon="group_work" label="Seleccionar Grupo"></v-select>
Demo:
new Vue({
el: '#app',
data () {
return {
entidades: [ { "id": 1 }, { "id": 2 } ]
}
}
})
<link rel='stylesheet' href='https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons'>
<link rel='stylesheet' href='https://unpkg.com/vuetify#1.0.10/dist/vuetify.min.css'>
<script src='https://unpkg.com/vue/dist/vue.js'></script>
<script src='https://unpkg.com/vuetify#1.0.10/dist/vuetify.min.js'></script>
<div id="app">
<v-app>
<v-container>
<v-select :items="entidades" item-text="id" item-value="id" single-line auto prepend-icon="group_work" label="Seleccionar Grupo"></v-select>
</v-container>
</v-app>
</div>
I've an issue to fill data from model into code when I'v more then one template with separate data. What I need is first template renders as many times as many objects in firstFormDetails array and same the second one. There example of my code below:
<div id="app">
<first v-for="item in secondFormDetails" track-by="id"></first>
<second v-for="item in firstFormDetails" track-by="id"></second>
</div>
<template id="template-first">
<div> {{ item.docNumber }}</div>
</template>
<template id="template-second">
<div> {{ item.docNumber }}</div>
</template>
And VueJs module as follows:
Vue.component('first', {
template: '#template-first',
data: function() {
return {
firstFormDetails: [
{docNumber: 7},
{docNumber: 7777},
{docNumber: 10000}
]
}
}
});
Vue.component('second', {
template: '#template-second',
data: function() {
return {
secondFormDetails: [
{docNumber: 1908},
{docNumber: 7777},
{docNumber: 10000}
]
}
}
});
new Vue({
el: '#app'
});
#vbranden is correct, move the v-for into the component
<template id="template-first">
<div v-for="item in firstFormDetails"> {{ item.docNumber }}</div>
</template>
<template id="template-second">
<div v-for="item in secondFormDetails"> {{ item.docNumber }}</div>
</template>
You must define what components you use. Let try to use this:
var first = Vue.component('first', {
template: '#template-first',
data: function() {
return {
firstFormDetails: [
{docNumber: 7},
{docNumber: 7777},
{docNumber: 10000}
]
}
}
});
var second = Vue.component('second', {
template: '#template-second',
data: function() {
return {
secondFormDetails: [
{docNumber: 1908},
{docNumber: 7777},
{docNumber: 10000}
]
}
}
});
new Vue({
el: '#app',
components: {
'first': first,
'second': second
}
});
in addition on #johnnynotsolucky 's answer, you will need a wrapper element out of v-for, since only allow only one element inside it.
<template id="template-first">
<div class="form-details">
<div v-for="item in firstFormDetails"> {{ item.docNumber }}</div>
</div>
</template>
var first = Vue.component('first', {
template: '#template-first',
props: ['item']
});
var second = Vue.component('second', {
template: '#template-second',
props: ['item']
});
new Vue({
el: '#app',
components: {
'first': first,
'second': second
},
data: function () {
return {
firstFormDetails: [
{docNumber: 7},
{docNumber: 7777},
{docNumber: 10000}
],
secondFormDetails: [
{docNumber: 1908},
{docNumber: 7777},
{docNumber: 10000}
]
}
}
});
<div id="app">
<first v-for="item in secondFormDetails" :item="item"></first>
<second v-for="item in firstFormDetails" :item="item"></second>
</div>
<template id="template-first">
<div> {{ item.docNumber }}</div>
</template>
<template id="template-second">
<div> {{ item.docNumber }}</div>
</template>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.3/vue.min.js"></script>
When Vue app is loaded, it looks for component field, if component field is not defined then not component is loaded, if component field is defined, Vue looks for the definition of the component and parse it for syntax checking, once the syntax is correct the component is binded. This happens recursively for nested components.
Registering a component is mandatory