Related
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.
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>
Does anyone know how to actually display the text value from multiple dependent select fields?
I need option 1 to list a set of parent options. When an Option 1 is selected, it will determine the values available in Option 2 select field.
I then want the text value associated with the selected option to be displayed below the select field.
<template>
<div>
<panel class="main-content">
<div>
<b-form-select v-model="selected" :options="options" class="mb-3" />
<div>Selected: <strong>{{ selected }}</strong></div>
</div>
<div>
<b-form-select v-model="selected" :options="options" class="mb-3" />
<div>Selected: <strong>{{ selected }}</strong></div>
</div>
</panel>
</div>
</template>
<script>
export default {
data () {
return {
selected: null,
options: [
{ value: null, name: 'opt', text: 'Select One' },
{ value: 'option1', name: 'mys', text: 'Carbohydrates' },
{ value: 'option2', name: 'hkong', text: 'Fibre' },
{ value: 'option3', name: 'spore', text: 'Fat' }
]
}
}
}
</script>
Kindly assist since it's something new for me in VueJs.
This is working in VueJS (tested). I was a little off about how the b-form-select element works, but this will give you the results you need.
If you are building the options manually, maybe just make a JSON file or a Component that returns these sets of options and subOptions and import it rather than putting all the options into your template (Cleaner Code).
<script>
export default {
data () {
return {
option1Selected: null,
option2Selected: null,
options: [
{
value: null,
name: 'opt',
text: 'Select One',
},
{
value: 'option1',
name: 'mys',
text:'Carbohydrates',
},
{
value: 'option2',
name: 'hkong',
text: 'Fibre',
},
{
value: 'option3',
name: 'spore',
text: 'Fat',
},
],
subOptions: {
option1: [
{
value: null,
name: 'opt',
text: 'Select One',
},
{
value: 'option1-1',
name: 'o1-1Name',
text: 'Option 1-1 Text',
},
{
value: 'option1-2',
name: 'o1-2Name',
text: 'Option 1-2 Text',
},
{
value: 'option1-3',
name: 'o1-3Name',
text: 'Option 1-3 Text',
},
],
option2: [
{
value: null,
name: 'opt',
text: 'Select One',
},
{
value: 'option2-1',
name: 'o2-1Name',
text: 'Option 2-1 Text',
},
{
value: 'option2-2',
name: 'o2-2Name',
text: 'Option 2-2 Text',
},
{
value: 'option2-3',
name: 'o2-3Name',
text: 'Option 2-3 Text',
},
],
option3: [
{
value: null,
name: 'opt',
text: 'Select One',
},
{
value: 'option3-1',
name: 'o3-1Name',
text: 'Option 3-1 Text',
},
{
value: 'option3-2',
name: 'o3-2Name',
text: 'Option 3-2 Text',
},
{
value: 'option3-3',
name: 'o3-3Name',
text: 'Option 3-3 Text',
},
],
}
}
},
computed: {
option1text () {
for (const key in this.options) {
if (this.options[key].value === this.option1Selected) {
return this.options[key].text;
}
}
},
option2text () {
if (this.option1Selected != null) {
for (const key in this.subOptions[this.option1Selected]) {
if (this.subOptions[this.option1Selected][key].value === this.option2Selected) {
return this.subOptions[this.option1Selected][key].text;
}
}
}
}
},
}
</script>
<template>
<div>
<panel class="main-content">
<div>
<b-form-select v-model="option1Selected" :options="options" class="mb-3" />
<div>Selected: <strong>{{ option1text }}</strong></div>
</div>
<div v-if="option1Selected != null">
<b-form-select v-model="option2Selected" :options="subOptions[option1Selected]" class="mb-3" />
<div>Selected: <strong>{{ option2text }}</strong></div>
</div>
</panel>
</div>
</template>
Note I have updated code to properly reflect exactly what you were asking for in the question: the Text value of the option.
i've been struggling for almost 6 hours with this, but i didn't find a way to accomplish what i need.
the thing is that i need to filter the table of bootstrap vue with a function, but i can't make it work, there is a step that i need, but i can't find it.
here it is a jsfiddle that i grab from the web, that has the simple b-table with property filter.
https://jsfiddle.net/rothariger/oxbrhcqk/1/
now if you check on this example I've replaced the property for a function, but, it just get called in the load, when you change the filter textbox, it doesn't do anything, and here is where i think i'm missing a step.
https://jsfiddle.net/rothariger/oxbrhcqk/3/
new Vue({
el: '#app',
data: {
items: [{
isActive: true,
age: 40,
name: {
first: 'Dickerson',
last: 'Macdonald'
}
}, {
isActive: false,
age: 21,
name: {
first: 'Larsen',
last: 'Shaw'
}
}, {
isActive: false,
age: 9,
state: 'success',
name: {
first: 'Mitzi',
last: 'Navarro'
}
}, {
isActive: false,
age: 89,
name: {
first: 'Geneva',
last: 'Wilson'
}
}, {
isActive: true,
age: 38,
name: {
first: 'Jami',
last: 'Carney'
}
}, {
isActive: false,
age: 27,
name: {
first: 'Essie',
last: 'Dunlap'
}
}, {
isActive: true,
age: 40,
name: {
first: 'Dickerson',
last: 'Macdonald'
}
}, {
isActive: false,
age: 21,
name: {
first: 'Larsen',
last: 'Shaw'
}
}, {
isActive: false,
age: 26,
name: {
first: 'Mitzi',
last: 'Navarro'
}
}, {
isActive: false,
age: 22,
name: {
first: 'Geneva',
last: 'Wilson'
}
}, {
isActive: true,
age: 38,
name: {
first: 'Jami',
last: 'Carney'
}
}, {
isActive: false,
age: 27,
name: {
first: 'Essie',
last: 'Dunlap'
}
}],
fields: {
name: {
label: 'Person Full name',
sortable: true
},
age: {
label: 'Person age',
sortable: true
},
isActive: {
label: 'is Active'
},
actions: {
label: 'Actions'
}
},
currentPage: 1,
perPage: 5,
filter: null
},
methods: {
details(item) {
alert(JSON.stringify(item));
},
filterGrid(val) {
console.log(val);
return true;
}
}
})
any info on what i have to do?
regards.
As Vue Bootstrap introdues:,
The filter prop value can be a string, a RegExp or a function
reference. If a function is provided, the first argument is the
original item record data object. The function should return true if
the record matches your criteria or false if the record is to be
filtered out.
Then look into the source code at Vue Bootstrap Github, you will find Vue Bootstrap only invoke your filter function inside one computed property=computedItems.
So for your case:
filterGrid(val) {
console.log(val);
return true;
}
It does not trigger any reactivity (print then always return true), so it will do nothing (computed property=computedItems will not be re-calculated, so filter function will not be executed either).
so one simple filter function with reactivity will be like:
filterGrid(val){
return !this.filter || JSON.stringify(val).includes(this.filter)
}
Also you can involves other data/prop/computed property, the filter function will be executed also when reactivity is triggered.
The full demo:
new Vue({
el: '#app',
data: {
items: [{
isActive: true,
age: 40,
name: {
first: 'Dickerson',
last: 'Macdonald'
}
}, {
isActive: false,
age: 21,
name: {
first: 'Larsen',
last: 'Shaw'
}
}, {
isActive: false,
age: 9,
state: 'success',
name: {
first: 'Mitzi',
last: 'Navarro'
}
}, {
isActive: false,
age: 89,
name: {
first: 'Geneva',
last: 'Wilson'
}
}, {
isActive: true,
age: 38,
name: {
first: 'Jami',
last: 'Carney'
}
}, {
isActive: false,
age: 27,
name: {
first: 'Essie',
last: 'Dunlap'
}
}, {
isActive: true,
age: 40,
name: {
first: 'Dickerson',
last: 'Macdonald'
}
}, {
isActive: false,
age: 21,
name: {
first: 'Larsen',
last: 'Shaw'
}
}, {
isActive: false,
age: 26,
name: {
first: 'Mitzi',
last: 'Navarro'
}
}, {
isActive: false,
age: 22,
name: {
first: 'Geneva',
last: 'Wilson'
}
}, {
isActive: true,
age: 38,
name: {
first: 'Jami',
last: 'Carney'
}
}, {
isActive: false,
age: 27,
name: {
first: 'Essie',
last: 'Dunlap'
}
}],
fields: {
name: {
label: 'Person Full name',
sortable: true
},
age: {
label: 'Person age',
sortable: true
},
isActive: {
label: 'is Active'
},
actions: {
label: 'Actions'
}
},
currentPage: 1,
perPage: 5,
filter: null
},
methods: {
details(item) {
alert(JSON.stringify(item));
},
filterGrid(val){
return !this.filter || JSON.stringify(val).includes(this.filter)
}
}
})
<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"/>
<!-- Add this after vue.js -->
<script src="https://unpkg.com/vue#2.5.17/dist/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">
<div class="justify-content-centermy-1 row">
<b-form-fieldset horizontal label="Rows per page" class="col-6" :label-size="6">
<b-form-select :options="[{text:5,value:5},{text:10,value:10},{text:15,value:15}]" v-model="perPage">
</b-form-select>
</b-form-fieldset>
<b-form-fieldset horizontal label="Filter" class="col-6" :label-size="2">
<b-form-input v-model="filter" placeholder="Type to Search"></b-form-input>
</b-form-fieldset>
</div>
<!-- Main table element -->
<b-table striped hover :items="items" :fields="fields" :current-page="currentPage" :per-page="perPage" :filter="filterGrid">
<template slot="name" scope="item">
{{item.value.first}} {{item.value.last}}
</template>
<template slot="isActive" scope="item">
{{item.value?'Yes :)':'No :('}}
</template>
<template slot="actions" scope="item">
<b-btn size="sm" #click="details(item.item)">Details</b-btn>
</template>
</b-table>
<div class="justify-content-center row my-1">
<b-pagination size="md" :total-rows="this.items.length" :per-page="perPage" v-model="currentPage" />
</div>
</div>
I'm using vue 2.5 and the library Bootstrap-vue.
I'm interested by the table component of this library: https://bootstrap-vue.js.org/docs/components/table
But I want to encapsulate this component to make my own component with custom configuration which I don't want to repeat. This way I don't have to manage pagination and filtering every time and I can add other features like data export.
So I created a table-helper component (only pagination is handled for now)
<template>
<div>
<b-table striped hover responsive
:items="items" :fields="fields"
:current-page="currentPage" per-page="10">
<slot></slot>
</b-table>
<b-pagination :total-rows="items.length" per-page="10" v-model="currentPage"></b-pagination>
</div>
</template>
<script>
import bTable from 'bootstrap-vue/es/components/table/table'
import bPagination from 'bootstrap-vue/es/components/pagination/pagination'
export default {
name: "table-helper",
props: ['items', 'fields'],
data() {
return {
currentPage: 1,
}
},
components: {
'b-table': bTable,
'b-pagination': bPagination
}
}
</script>
And I want to use my component like this (using the bootstrap-vue slot possibility to reformat columns):
<table-helper :items="users" :fields="fields">
<template slot="fullName" slot-scope="data">
{{data.item.first_name}} {{data.item.last_name}}
</template>
</table-helper>
Obviously it doesn't work (I get the table but not the formatted columns) because the <template slot="fullName" slot-scope="data"> refers to my custom component and not to the b-table component.
So I'd like to know a way to encapsulated a library component which uses slots and slot scopes like this.
Thank you for your help.
The Key points:
Template vs JSX: You may have to use JSX to implement it, especially for slot.
Slot: for your case, the slot of parent component will be the children VNodes of b-table, so reshape the slot to one array and change the context from parent to current (if not scopedSlot will be rendered incorrectly.), then put them into the third parameter of function=h (or you can call createElement). For the details, check Vue Guide: Create Element Arguments.
Anyway, read Best Way To Implement HOC carefully , then you should be able to reach the goal.
Vue.config.productionTip = false
Vue.component('table-helper', {
render (h) {
const slots = Object.keys(this.$slots)
.reduce((arr, key) => arr.concat(this.$slots[key]), [])
.map(vnode => {
vnode.context = this._self
return vnode
})
const self = this
return h('div', [
h('b-table', {
on: self.$listeners,
props: Object.assign(self.$props, {currentPage: self.currentPage}),
scopedSlots: self.$scopedSlots,
attrs: self.$attrs
},slots),
h('b-pagination', {
props: self.$props,
domProps: {
value: self.currentPage
},
on: {
input: function (event) {
self.currentPage = event
}
}
})
])
},
//mixins: [{bTable.props}, {bPagination.props}],
props: ['items', 'fields', 'perPage','totaRows'],
data() {
return {
currentPage: 1,
}
}
})
new Vue({
el: '#app',
data() {
return {
fields: [ 'first_name', 'last_name', 'age', 'fullName' ],
users: [
{ isActive: true, age: 40, first_name: 'Dickerson', last_name: 'Macdonald' },
{ isActive: false, age: 21, first_name: 'Larsen', last_name: 'Shaw' },
{ isActive: false, age: 89, first_name: 'Geneva', last_name: 'Wilson' },
{ isActive: true, age: 38, first_name: 'Jami', last_name: 'Carney' },
{ isActive: true, age: 40, first_name: 'Dickerson', last_name: 'Macdonald' },
{ isActive: false, age: 21, first_name: 'Larsen', last_name: 'Shaw' },
{ isActive: false, age: 89, first_name: 'Geneva', last_name: 'Wilson' },
{ isActive: true, age: 38, first_name: 'Jami', last_name: 'Carney' },
{ isActive: true, age: 40, first_name: 'Dickerson', last_name: 'Macdonald' },
{ isActive: false, age: 21, first_name: 'Larsen', last_name: 'Shaw' },
{ isActive: false, age: 89, first_name: 'Geneva', last_name: 'Wilson' },
{ isActive: true, age: 38, first_name: 'Jami', last_name: 'Carney' }
]
}
}
})
<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.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<script src="//unpkg.com/babel-polyfill#latest/dist/polyfill.min.js"></script>
<script src="https://unpkg.com/bootstrap-vue/dist/bootstrap-vue.js"></script>
<div id="app">
<table-helper :items="users" :fields="fields" :per-page="10" :total-rows="users.length">
<template slot="fullName" slot-scope="data">
{{data.item.first_name}} {{data.item.last_name}}
</template>
</table-helper>
</div>