How can i link b-table(bootstrap-vue) elements? - vue.js

...
<b-table
class="table table-bordered"
id="my-table"
striped hover
:items="items"
:per-page="perPage"
...
></b-table>
items: [
{ id : 1, name : "Taylor", status: "passive" },
{ id : 2, name : "Tom", status: "passive" },
{ id : 3, name : "Arthur", status: "passive" },
...
İ want name or more details when i click it will transfer to new page. How can i do that ?

You can use slots to customize your b-table. I prepared a code snippet to show you how you can add a detail button in your table and redirect the user to a new page after clicking on it.
new Vue({
el: '#app',
data() {
return {
fields: ['id', 'name', 'status', 'details'],
items: [{
id: 1,
name: "Taylor",
status: "passive"
},
{
id: 2,
name: "Tom",
status: "passive"
},
{
id: 3,
name: "Arthur",
status: "passive"
},
]
}
},
methods: {
},
mounted() {},
})
<link type="text/css" rel="stylesheet" href="https://unpkg.com/bootstrap#4.5.3/dist/css/bootstrap.min.css" />
<link type="text/css" rel="stylesheet" href="https://unpkg.com/bootstrap-vue#2.21.2/dist/bootstrap-vue.css" />
<script src="https://unpkg.com/vue#2.6.12/dist/vue.min.js"></script>
<script src="https://unpkg.com/bootstrap-vue#2.22.0/dist/bootstrap-vue.min.js"></script>
<div id="app">
<b-table bordered id="my-table" striped hover :items="items" :fields="fields">
<template #cell(details)="row">
<b-link :to="`/items/${row.item.id}`">Details</b-link>
</template>
</b-table>
</div>

Related

Hide vue paginate button if element doesn't exist

I'm trying to hide a vue paginate button if the item doesn't exist in my array. My code:
<b-pagination
:key="currentQuestionPage"
v-model="currentQuestionPage"
:total-rows="submissionFiles.length"
:per-page="perPage"
align="center"
first-number
last-number
limit="20"
#input="changePage()"
>
<template v-slot:page="{ page, active }">
{{ submissionFiles[page - 1][currentStudentPage - 1] && submissionFiles[page - 1][currentStudentPage - 1].order }}
</template>
</b-pagination>
However, instead of the button not rendering (what I'm hoping for), I'm getting a "blank" button:
Is there any way to prevent the button from rendering at all if it has empty content?
I don't think you show enough code to get a particularly useful answer here, but my guess would be:
You need to first create a computed property which is an array only of the items you want.
That way you end up with something more like this:
new Vue({
el: '#app',
data() {
return {
perPage: 3,
currentPage: 1,
items: [
{ id: 1, test: true },
{ id: 2, test: false },
{ id: 3, test: true },
{ id: 4, test: false },
{ id: 5, test: true },
{ id: 6, test: true },
{ id: 7, test: false },
{ id: 8, test: true },
{ id: 9, test: false },
]
}
},
computed: {
// Here we compute the actual items we want:
usedItems(){
return this.items.filter(i => i.test);
},
rows() {
// Now we remove the previous "rows" var and use the computed property instead
// return this.items.length
return this.usedItems.length
}
}
})
<link type="text/css" rel="stylesheet" href="https://unpkg.com/bootstrap#4.5.3/dist/css/bootstrap.min.css" />
<script src="https://unpkg.com/vue#2.6.12/dist/vue.min.js"></script>
<script src="https://unpkg.com/bootstrap-vue#2.21.2/dist/bootstrap-vue.min.js"></script>
<div id="app">
<div class="overflow-auto">
<b-pagination
v-model="currentPage"
:total-rows="rows"
:per-page="perPage"
aria-controls="my-table"
></b-pagination>
<p class="mt-3">Current Page: {{ currentPage }}</p>
<b-table
id="my-table"
<!-- Update items to show only the filtered selection: -->
:items="usedItems"
:per-page="perPage"
:current-page="currentPage"
small
></b-table>
</div>
</div>

How to customize the text on empty cell value using Bootstrap-Vue?

What I want to achieve is put "-" on every empty cell value. Like this (see the "Name" row):
How do I do that? Thanks
You can use add a formatter if you use the fields prop.
Here you can create a method which either returns your name if there is one, or - if there's none.
new Vue({
el: '#app',
data() {
return {
fields: [
{ key: "name", formatter: 'formatName' },
{ key: "age" }
],
items: [
{ age: 40, name: 'Dickerson Macdonald' },
{ age: 21, name: '' },
{ age: 89, name: 'Geneva Wilson' },
{ age: 38, name: 'Jami Carney'}
]
}
},
methods: {
formatName(value) {
return value || '-'
}
}
})
<link type="text/css" rel="stylesheet" href="https://unpkg.com/bootstrap/dist/css/bootstrap.min.css" />
<link type="text/css" rel="stylesheet" href="https://unpkg.com/bootstrap-vue#2.21.2/dist/bootstrap-vue.min.css" />
<script src="https://unpkg.com/vue#2.6.2/dist/vue.min.js"></script>
<script src="https://unpkg.com/bootstrap-vue#2.21.2/dist/bootstrap-vue.min.js"></script>
<div id="app">
<b-table :items="items" :fields="fields"></b-table>
</div>
Alternatively, you can use slots to create the logic in your template, or maybe render something else if there's no name. But if it's simply displaying a -, I'd stick with a formatter.
new Vue({
el: '#app',
data() {
return {
fields: [
{ key: "name" },
{ key: "age" }
],
items: [
{ age: 40, name: 'Dickerson Macdonald' },
{ age: 21, name: '' },
{ age: 89, name: 'Geneva Wilson' },
{ age: 38, name: 'Jami Carney'}
]
}
}
})
<link type="text/css" rel="stylesheet" href="https://unpkg.com/bootstrap/dist/css/bootstrap.min.css" />
<link type="text/css" rel="stylesheet" href="https://unpkg.com/bootstrap-vue#2.21.2/dist/bootstrap-vue.min.css" />
<script src="https://unpkg.com/vue#2.6.2/dist/vue.min.js"></script>
<script src="https://unpkg.com/bootstrap-vue#2.21.2/dist/bootstrap-vue.min.js"></script>
<div id="app">
<b-table :items="items" :fields="fields">
<template #cell(name)="{ value }">
{{ value || '-' }}
</template>
</b-table>
</div>

Split row-details into multiple row in bootstrap-vue

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>

Vue JS Roles & Permission in a table grid using checkboxes

I would like to check permissions of a role using checkbox in vue. For example if an admin can create, view, delete a checkbox will be selected, if not checkbox will not be selected. So far i have the table created with proper roles and permissions that below to each role but don't know how to check checkbox based on permission in role.
new Vue({
el: '#app',
vuetify: new Vuetify(),
data() {
return {
permissions: [
{
id: 1,
name: "view posts"
},
{
id: 2,
name: "create posts"
},
{
id: 3,
"name": "edit posts"
},
{
id: 4,
name: "delete posts"
}
],
roles: [
{
id: 1,
name: "admin",
description: "Full admin access to all the areas of the blog.",
permissions: [
{
id: 1,
name: "view posts",
},
{
id: 2,
name: "create posts",
},
{
id: 3,
name: "edit posts",
},
{
id: 4,
name: "delete posts",
}
]
},
{
id: 2,
name: "executive",
description: "Limited access to critical areas of the blog",
permissions: [
{
id: 1,
name: "view posts",
},
{
id: 2,
name: "create posts",
}
]
},
{
id: 3,
name: "user",
description: "Basic view access to some areas of the blog",
permissions: [
{
id: 1,
name: "view posts",
}
]
}
]
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons" rel="stylesheet">
<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">
<script src="https://cdn.jsdelivr.net/npm/vue#2.x/dist/vue.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.min.js"></script>
<div id="app">
<template>
<div>
<div id='permissionsTable'>
<v-row class='headerRow'>
<v-col cols='3'>Permissions</v-col>
<v-col v-for="role in roles" v-bind:key="role.id">{{role.name}}</v-col>
</v-row>
<v-row v-for="permission in permissions" v-bind:key="permission.id" class="bodyRow">
<v-col cols='3'>{{permission.name}}</v-col>
<v-col v-for="(role, index) in roles" v-bind:key="role.name">
{{roles[index].permissions}}
<v-checkbox :input-value="roles[index].permissions.includes(permission.id)" #change="onItemClick($event,role.name,permission.id)">
</v-checkbox>
</v-col>
</v-row>
</div>
</div>
</template>
</div>
Let me know if this help.
new Vue({
el: "#app",
vuetify: new Vuetify(),
methods: {
onItemClick(e, role, permissionId) {
const index = this.roles.findIndex((r) => r.name === role);
const found=this.roles[index].permissions.find(perm=> perm.id === permissionId)
if (
found
) {
this.roles[index].permissions.splice(
this.roles[index].permissions.findIndex(perm=>perm.id===permissionId),
1
);
} else {
this.roles[index].permissions.push(this._permissions[permissionId]);
}
}
},
computed: {
_permissions() {
return this.permissions.reduce((acc, curr) => {
const id = curr.id;
acc[id] = curr;
return acc;
}, {});
},
_roles() {
return this.roles.reduce((acc, curr) => {
const id = curr.id;
acc[id] = curr;
return acc;
}, {});
}
},
data() {
return {
permissions: [
{
id: 1,
name: "view posts"
},
{
id: 2,
name: "create posts"
},
{
id: 3,
name: "edit posts"
},
{
id: 4,
name: "delete posts"
}
],
roles: [
{
id: 1,
name: "admin",
description: "Full admin access to all the areas of the blog.",
permissions: [
{
id: 1,
name: "view posts"
},
{
id: 2,
name: "create posts"
},
{
id: 3,
name: "edit posts"
},
{
id: 4,
name: "delete posts"
}
]
},
{
id: 2,
name: "executive",
description: "Limited access to critical areas of the blog",
permissions: [
{
id: 1,
name: "view posts"
},
{
id: 2,
name: "create posts"
}
]
},
{
id: 3,
name: "user",
description: "Basic view access to some areas of the blog",
permissions: [
{
id: 1,
name: "view posts"
}
]
}
]
};
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons" rel="stylesheet">
<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">
<script src="https://cdn.jsdelivr.net/npm/vue#2.x/dist/vue.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.min.js"></script>
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons" rel="stylesheet">
<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">
<script src="https://cdn.jsdelivr.net/npm/vue#2.x/dist/vue.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#2.x/dist/vuetify.min.js"></script>
<div id="app">
<template>
<div>
<div id='permissionsTable'>
<v-row class='headerRow'>
<v-col cols='3'>Permissions</v-col>
<v-col v-for="role in roles" v-bind:key="role.id">{{role.name}}</v-col>
</v-row>
<v-row v-for="permission in permissions" v-bind:key="permission.id" class="bodyRow">
<v-col cols='3'>{{permission.name}}</v-col>
<v-col v-for="(role, index) in roles" v-bind:key="role.name">
{{roles[index].permissions}}
<v-checkbox :input-value="_roles[role.id].permissions.find(perm=>perm.id===permission.id)" #change="onItemClick($event,role.name,permission.id)">
</v-checkbox>
</v-col>
</v-row>
</div>
</div>
</template>
</div>
v-checkbox has a v-model prop that you can use to pass Boolean and select or deselect it accordingly ! try adding that Boolean in your roles array and use it inside the v-for. you can also check your condition inside v-for on v-model like :v-model="role.permissions.includes(1)" but it's up to you to use the one that suit your case best.

How to load Array Data into Vuetify Select Input

I'm trying to show "locations" in a vuetify select component, but my current code renders "[object Object]" instead of Location 1, Location 2, etc.
My select component:
<v-select
:items="locations"
v-model="location"
label="Choose Location"
bottom
autocomplete
></v-select>
Locations:
locations () {
return this.$store.getters.getLocationsForEvent(this.event.id)
}
Vuex Getter:
getLocationsForEvent: (state) => (id) => {
return state.loadedLocations.filter(function (location) {
return location.eventId === id;
});
}
Here is a screenshot of what the location data looks like:
Thanks!
For custom objects you have to specify the item-text. The item-text is what each option will display.
From your screenshot, for instance, title is a possible property:
<v-select
:items="locations"
v-model="location"
label="Choose Location"
item-text="title"
bottom
autocomplete
></v-select>
Demos below.
Without item-text:
new Vue({
el: '#app',
data () {
return {
location: null,
locations: [
{ id: "1111", manager: 'Alice', title: 'Water Cart 1' },
{ id: "2222", manager: 'Bob', title: 'Water Cart 2' },
{ id: "3333", manager: 'Neysa', title: 'Water Cart 3' }
]
}
}
})
<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="locations"
v-model="location"
label="Choose Location"
bottom
autocomplete
>
</v-select>
</v-container>
</v-app>
</div>
With item-text:
new Vue({
el: '#app',
data () {
return {
location: null,
locations: [
{ id: "1111", manager: 'Alice', title: 'Water Cart 1' },
{ id: "2222", manager: 'Bob', title: 'Water Cart 2' },
{ id: "3333", manager: 'Neysa', title: 'Water Cart 3' }
]
}
}
})
<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="locations"
v-model="location"
label="Choose Location"
item-text="title"
bottom
autocomplete
>
</v-select>
</v-container>
</v-app>
</div>
implemeneted a watch to have a low level Array of objects
watch: {
groupInfo: function(groupInfo) {
if (groupInfo.teams !== undefined) {
var newArray = [];
for (var key in groupInfo.teams) {
var obj = groupInfo.teams[key];
newArray.push(obj);
}
console.log("wagwan" newArray)
this.teams = newArray;
}
}
},