Show/Hide columns dynamically with a bootstrap-vue component and bootstrap 3 - vue.js

I am currently trying to show/hide dynamically with a bootstrap-vue table (https://bootstrap-vue.js.org/docs/components/table).
So far, I only managed to hide the header but the cells won't disappear, which is a problem because the cells are not in front of the good headers (the numbers should be under days and 'ingredient' should be under item.
Here what's 'worked':
fields: [
{key: "day", label: "Day",sortable: true, thStyle:"display:none"},
{key: "item", label: "Item",sortable: true}
]
I was wondering if it was possible to maybe do that outside of vueJS, and change the properties of the column with CSS directly..
Thanks for the help !
Louis

You could add an additional property to your fields. For example visible and create a computed property that filters out all fields with visible: false.
That way you can simply toggle this property to show/hide your columns.
window.onload = () => {
new Vue({
el: '#app',
computed: {
visibleFields() {
return this.fields.filter(field => field.visible)
}
},
data() {
return {
items: [
{ id: 1, first: 'Mike', last: 'Kristensen', age: 16 },
{ id: 2, first: 'Peter', last: 'Madsen', age: 52 },
{ id: 3, first: 'Mads', last: 'Mikkelsen', age: 76 },
{ id: 4, first: 'Mikkel', last: 'Hansen', age: 34 },
],
fields: [
{ key: 'id', label: 'ID', visible: true },
{ key: 'first', label: 'First Name', visible: true },
{ key: 'last', label: 'Last Name', visible: true },
{ key: 'age', label: 'Age', visible: true },
]
}
}
})
}
<link href="https://unpkg.com/bootstrap#4.4.1/dist/css/bootstrap.min.css" rel="stylesheet"/>
<link href="https://unpkg.com/bootstrap-vue#2.2.2/dist/bootstrap-vue.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.js"></script>
<script src="https://unpkg.com/bootstrap-vue#2.2.2/dist/bootstrap-vue.min.js"></script>
<div id='app'>
<b-checkbox
:disabled="visibleFields.length == 1 && field.visible"
v-for="field in fields"
:key="field.key"
v-model="field.visible"
inline
>
{{ field.label }}
</b-checkbox>
<br /><br />
<b-table :items="items" :fields="visibleFields" bordered>
</b-table>
</div>

Related

Bootstrap Vue - table custom field formatter called twice but not working

I have a table:
<b-table striped hover :items="coursesArray" :fields="fields"></b-table>
The data comes from
coursesArray: [
{
id: "1",
name: "foo",
teacherEmails: [{ 0: "sample1" }, { 1: "sample2" }, { 2: "sample3" }],
teacherIds: ["A1", "A2", "A3"],
},
],
And fields are:
fields: [
{ key: "id", label: "Course ID" },
{ key: "name", label: "Course Name" },
{
key: "teacherEmails",
label: "Teacher Email",
formatter: "teacherEmailTCellFormat",
},
{ key: "teacherIds", label: "Teacher ID" },
],
And this is how it's rendered without the formatter.
So, I added a custom formatter in fields to remove the brackets and curly braces.
The first problem I'm having is that looping over the value to return all the items in teacherEmails does not work.
teacherEmailTCellFormat(value) {
console.log(value)
value.forEach(item => {
return each[0]
}
}
The second problem, that I don't understand why it's happening, is that if I log the value passed to the formatter, it's clear the formatter function is called twice.
Any help or clearance will be appreciated.
You need to return an array, you can do this using Array#map:
new Vue({
el:"#app",
data: () => ({
fields: [
{ key: "id", label: "Course ID" },
{ key: "name", label: "Course Name" },
{ key: "teacherEmails", label: "Teacher Email", formatter: "teacherEmailTCellFormat" },
{ key: "teacherIds", label: "Teacher ID" },
],
coursesArray: [
{
id: "1",
name: "foo",
teacherEmails: [{ 0: "sample1" }, { 1: "sample2" }, { 2: "sample3" }],
teacherIds: ["A1", "A2", "A3"],
}
]
}),
methods: {
teacherEmailTCellFormat(value) {
return value.map((item, i) => item[i]);
}
}
});
<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#latest/dist/bootstrap-vue.css"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.min.js"></script>
<script src="https://unpkg.com/babel-polyfill#latest/dist/polyfill.min.js"></script>
<script src="https://unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.js"></script>
<div id="app">
<b-table striped hover :items="coursesArray" :fields="fields"></b-table>
</div>
Note: I assumed the key represents the index of the item in the array, but you can change the mapping as you want. The main issue was returning the array.

How to use pagination in BootstrapVue

I have created a b-table that stores all the data from the API that has been hit from Swagger UI, but since the data has a lot of characters in string, My questions are how to make the data in each row be hovered on click to show the real data from API that hasn't been truncated? I've tried using v-b-tooltip but it seems doesn't work. If I may, I also wanted to know more about how to make the b-pagination works to load another data as I navigate page further.
Here's my current code:
<template>
<base-header>
<template>
<b-card body>
<b-card-header class="border-0">
<h3 class="mb-0">Stock List</h3>
</b-card-header>
<template>
<div class="text-center">
<b-table responsive dark striped hover:true :items="items" :fields="fields">
<template #cell()="data">
<span v-b-tooltip.hover :title="data.value">
{{ data.value }}
</span>
</template>
</b-table>
</div>
</template>
<div class="overflow-auto">
<b-card-footer class="py-4 d-flex justify-content-end">
<b-pagination
v-model="currentPage"
:total-rows="rows"
:per-page="perPage"
aria-controls="my-table"
></b-pagination>
</b-card-footer>
</div>
</b-card>
</template>
</base-header>
</template>
and then here's the script
<script>
// eslint-disable-next-line no-unused-vars
import { getAllProvinces } from '~/api/delivery'
export default {
// components: {
// },
data() {
return {
perPage: 10,
currentPage: 1,
allStock: 0,
text: '',
rows: 100,
// ubah rows dan perPage biar paginationnya ada value
items: [],
fields: [
{
key: 'id',
sortable: true,
label: 'ID',
class: 'truncate',
},
{
key: 'requestId',
sortable: true,
label: 'Request ID',
class: 'truncate',
},
{
key: 'storeCode',
sortable: true,
label: 'Store Code',
class: 'truncate',
},
{
key: 'branchCode',
sortable: true,
label: 'Branch Code',
class: 'truncate',
},
{
key: 'b2bId',
sortable: true,
label: 'B2B ID',
class: 'truncate',
},
{
key: 'request',
sortable: true,
label: 'Request',
class: 'truncate',
},
{
key: 'response',
sortable: true,
label: 'Response',
class: 'truncate',
},
{
key: 'createDate',
sortable: true,
label: 'Create Date',
class: 'truncate',
},
{
key: 'errorClassification',
sortable: true,
label: 'Error Classification',
class: 'truncate',
},
],
}
},
mounted() {
this.getAllStock()
},
methods: {
getAllStock() {
this.$axios
.get(
'API Link'
)
.then((res) => {
// eslint-disable-next-line no-console
console.log(res.data)
this.items = res.data.stocks
this.allStock = res.data
// eslint-disable-next-line no-console
// console.log('cek res stock:', JSON.stringify(res.data))
})
},
computed: {
rows() {
return this.items.length
},
},
},
}
</script>
<style>
.truncate {
max-width: 200px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
</style>
The documentation is pretty self-explanatory: https://bootstrap-vue.org/docs/components/pagination
But I added several comments on the code below (the template is therefore invalid!)
<template>
<div class="overflow-auto">
<b-pagination
v-model="currentPage" // this is the most important, bind the currentPage state to the pagination component, two-way data binding
:total-rows="rows" // display how much total data there is
:per-page="perPage" // this one will tell how much data per page you want to display
aria-controls="my-table" // this is for a11y
></b-pagination>
<p class="mt-3">Current Page: {{ currentPage }}</p>
<b-table
id="my-table"
:items="items" // where to look for the data
:per-page="perPage"
:current-page="currentPage" // re-use the current value of the pagination component
small
></b-table>
</div>
</template>
<script>
export default {
data() {
return {
perPage: 3,
currentPage: 1,
items: [ // your items, that you may replace with a new array if fetching an API in between each pagination page change
{ id: 1, first_name: 'Fred', last_name: 'Flintstone' },
{ id: 2, first_name: 'Wilma', last_name: 'Flintstone' },
{ id: 3, first_name: 'Barney', last_name: 'Rubble' },
{ id: 4, first_name: 'Betty', last_name: 'Rubble' },
{ id: 5, first_name: 'Pebbles', last_name: 'Flintstone' },
{ id: 6, first_name: 'Bamm Bamm', last_name: 'Rubble' },
{ id: 7, first_name: 'The Great', last_name: 'Gazzoo' },
{ id: 8, first_name: 'Rockhead', last_name: 'Slate' },
{ id: 9, first_name: 'Pearl', last_name: 'Slaghoople' }
]
}
},
computed: {
rows() {
return this.items.length
}
}
}
</script>
Of course, depending of the amount of data, you may want to watch for the currentPage value and make a new API call, fetching the next elements.
This totally depends on the API implementation but it's essentially passing 2 rather than 1 in URL's query params or somewhere into the headers.
As you can see in Github's API here: https://docs.github.com/en/rest/reference/repos#list-repositories-for-the-authenticated-user--parameters
Theirs is awaiting for a page and per_page params, hence the values of our VueJS state that we will send a new API call with.

Display column with condition - Bootstrap-vue - VueJS [duplicate]

I'd like to only show one of my columns if the current user is an admin. I'm not sure how to do this with bootstrapVue. Any ideas?
Here's a snippet based on Troy's comment.
I've added a custom property to the field object called requiresAdmin. This is not part of standard Bootstrap-Vue.
You can use this to filter out all the fields that require's the user to be an admin in a computed property. Based on whether the user is an admin or not. This makes it easy to add and remove fields that require's the user to be an admin.
new Vue({
el: '#app',
computed: {
computedFields() {
// If the user isn't an admin, filter out fields that require auth.
if(!this.isUserAdmin)
return this.fields.filter(field => !field.requiresAdmin);
// If the user IS an admin, return all fields.
else
return this.fields;
}
},
data() {
return {
isUserAdmin: false,
fields: [
{ key: 'first', label: 'First Name' },
{ key: 'last', label: 'Last Name' },
{ key: 'age', label: 'Age' },
{ key: 'sex', label: 'Sex' },
{ key: 'secretActions', label: 'Secret Actions', requiresAdmin: true },
],
items: [
{ first: 'John', last: 'Doe', sex: 'Male', age: 42 },
{ first: 'Jane', last: 'Doe', sex: 'Female', age: 36 },
{ first: 'Rubin', last: 'Kincade', sex: 'Male', age: 73 },
{ first: 'Shirley', last: 'Partridge', sex: 'Female', age: 62 }
]
}
}
})
<link href="https://unpkg.com/bootstrap#4.4.1/dist/css/bootstrap.min.css" rel="stylesheet"/>
<link href="https://unpkg.com/bootstrap-vue#2.3.0/dist/bootstrap-vue.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.js"></script>
<script src="https://unpkg.com/bootstrap-vue#2.3.0/dist/bootstrap-vue.js"></script>
<div id="app" class="p-3">
<b-btn #click="isUserAdmin = !isUserAdmin">
Toggle admin user ({{ isUserAdmin }})
</b-btn>
<br /><br />
<b-table :fields="computedFields" :items="items">
<template v-slot:cell(secretActions)>
<b-btn>Edit User</b-btn>
<b-btn>Delete User</b-btn>
</template>
</b-table>
</div>

Vuetify v-treeview how to get the last selected element?

Vuetify v-treeview how to get the get last selected element?
<v-treeview
v-model="treeModel"
:items="items"
selectable="selectable"
>
by treeModel I have all selected, but how can I get only the last item selected (clicked)?
by only providing the last item's id in the v-model
modified example from https://vuetifyjs.com/en/components/treeview#checkbox-color
option 1 - use v-on/#on update:active
** DOES NOT CURRENTLY WORK FOR VUETIFY v2 **
<div id="app">
<v-app id="inspire">
<v-treeview
v-model="selection"
selectable
selected-color="red"
:items="items"
#update:active="onUpdate"
></v-treeview>
</v-app>
</div>
new Vue({
el: '#app',
vuetify: new Vuetify(),
data: () => ({
selection: [],
items: [
{
id: 1,
name: 'Applications :',
children: [
{ id: 2, name: 'Calendar : app' },
{ id: 3, name: 'Chrome : app' },
{ id: 4, name: 'Webstorm : app' },
],
},
{
id: 5,
name: 'Documents :',
children: [
{ id: 6, name: 'Calendar.doc' },
{ id: 7, name: 'Chrome.doc' },
{ id: 8, name: 'Webstorm.doc' },
],
},
],
}),
methods: {
onUpdate(selection) {
console.log(selection)
}
}
})
the problem is that if you're using vuetify v2.0.0 - v2.0.5 the action does not actually work for a selection but for activatable
## option 2 - use watch
this option, at the moment, is preferred. It uses a watch to trigger the action when the `v-model` changes
```html
<div id="app">
<v-app id="inspire">
<v-treeview
v-model="selection"
:items="items"
selectable
></v-treeview>
</v-app>
</div>
new Vue({
el: '#app',
vuetify: new Vuetify(),
watch:{
selection(newValue) {
this.onUpdate(newValue)
}
},
data: () => ({
selection: [],
items: [
{
id: 1,
name: 'Applications :',
children: [
{ id: 2, name: 'Calendar : app' },
{ id: 3, name: 'Chrome : app' },
{ id: 4, name: 'Webstorm : app' },
],
},
{
id: 5,
name: 'Documents :',
children: [
{ id: 6, name: 'Calendar.doc' },
{ id: 7, name: 'Chrome.doc' },
{ id: 8, name: 'Webstorm.doc' },
],
},
],
}),
methods: {
onUpdate(selection) {
console.log(selection)
}
}
})
If you want to find the last item that was selected, you can use an array diff here
watch:{
selection(newValue, oldVal) {
var arrDiff = myArrDiffDependency.added(newValue, oldValue)
this.onUpdate(arrDiff)
}
},
watch: {
selection() {
if (this.items.length > 1) {
this.items.shift();
}
},
},
Isto resolve

Two-Way Binding in Component Scope

Suppose I'm trying to make a simple questionnaire, where the user answers a list of questions.
new Vue(
{
el: "#app",
data:
{
questions:
[
{
id: 1,
name: "What is your favorite color?",
selectedId: 2,
choices:
[
{ id: 1, name: "red" },
{ id: 2, name: "green" },
{ id: 3, name: "blue" },
]
},
...
]
}
});
How do I go about making a question component with two-way binding. That is, if the user swaps their favorite color from green to red, by clicking on the respective input, the selectedId will automatically update. I'm not very clear on how v-model works within a component. Does it only have access to the components data? Also, I don't understand the difference between props/data.
There are lots of ways you can approach this, here's my attempt:
let id = 0;
Vue.component('question', {
template: '#question',
props: ['question'],
data() {
return {
radioGroup: `question-${id++}`,
};
},
methods: {
onChange(choice) {
this.question.selectedId = choice.id;
},
isChoiceSelected(choice) {
return this.question.selectedId === choice.id;
},
},
});
new Vue({
el: '#app',
data: {
questions: [
{
title: 'What is your favorite color?',
selectedId: null,
choices: [
{ id: 1, text: 'Red' },
{ id: 2, text: 'Green' },
{ id: 3, text: 'Blue' },
],
},
{
title: 'What is your favorite food?',
selectedId: null,
choices: [
{ id: 1, text: 'Beans' },
{ id: 2, text: 'Pizza' },
{ id: 3, text: 'Something else' },
],
},
],
},
});
.question {
margin: 20px 0;
}
<script src="https://rawgit.com/yyx990803/vue/master/dist/vue.js"></script>
<div id="app">
<question v-for="question of questions" :question="question"></question>
</div>
<template id="question">
<div class="question">
<div>{{ question.title }}</div>
<div v-for="choice of question.choices">
<input type="radio" :name="radioGroup" :checked="isChoiceSelected(choice)" #change="onChange(choice)"> {{ choice.text }}
</div>
<div>selectedId: {{ question.selectedId }}</div>
</div>
</template>