Getting bootstrap vue pagination to play with REST api - vue.js

Trying to get Bootstrap Vue to play with a REST api that returns data for one page and the total number of records (based on this):
<template>
</div>
<b-pagination
v-on:change="onPageChange"
:total-rows="totalRows"
:per-page="perPage"
v-model="currentPage" />
<b-table responsive striped hover show-empty
stacked="md"
:items="items"
:fields="fields"
:current-page="currentPage"
:per-page="perPage"
:filter="filter"
:sort-by.sync="sortBy"
:sort-desc.sync="sortDesc"
:sort-direction="sortDirection"
#filtered="onFiltered">
</b-table>
</div>
</template>
<script>
...
export default {
name: 'TableList',
data() {
return {
module: null,
title: 'Table',
items: [],
fields: [],
errors: [],
currentPage: 1,
perPage: 15,
totalRows: 0,
pageOptions: [ 5, 10, 15 ],
sortBy: null,
sortDesc: false,
sortDirection: 'asc',
filter: null,
}
},
created() {
...
this.fetch();
},
methods: {
fetch() {
var me = this;
var requestParams = {
page: this.currentPage,
per_page: this.perPage
};
if(this.sortBy) {
requestParams = Object.assign({ sort_by: this.sortBy }, requestParams);
}
Rest('GET', '/table/' + this.table, requestParams, this.$root.user.token)
.then(response => {
me.items = response.data[1]
me.totalRows = response.data[0].total_entries
})
.catch(error => {
this.errors.push('Error: ' + error.response.status + ': ' + error.response.statusText)
})
.finally(() => {
//alert('turn off loader!');
});
}
}
</script>
This Vue works if I fetch the entire table. However, when I use the REST api to return one page at a time, the number of pages is calculated to be 1, and the forward and end links are inactive. Thus, I am unable to trigger a request for e.g. page 2.
The REST api correctly returns the total number of rows in the table, and the number of rows requested, but Bootstrap Vue does not appear to be watching/reacting to changes to this.totalRows.
What have I missed?

You need to set the per-page prop to 0 on the b-table component to disable the local pagination and allow b-pagination to handle the data. Here's an example:
new Vue({
el: '#app',
data() {
return {
items: [],
fields: [{
key: 'postId',
label: 'Post ID'
},
{
key: 'id',
label: 'ID'
},
{
key: 'name',
label: 'Name'
},
{
key: 'email',
label: 'Email'
},
{
key: 'body',
label: 'Body'
}
],
currentPage: 0,
perPage: 10,
totalItems: 0
}
},
mounted() {
this.fetchData().catch(error => {
console.error(error)
})
},
methods: {
async fetchData() {
this.items = await fetch(`https://jsonplaceholder.typicode.com/comments?_page=${this.currentPage}&_limit=${this.perPage}`)
.then(res => {
this.totalItems = parseInt(res.headers.get('x-total-count'), 10)
return res.json()
})
.then(items => items)
}
},
watch: {
currentPage: {
handler: function(value) {
this.fetchData().catch(error => {
console.error(error)
})
}
}
}
})
<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.22/vue.js"></script>
<script src="//unpkg.com/babel-polyfill#latest/dist/polyfill.min.js"></script>
<script src="//unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.js"></script>
<div id="app">
<b-table show-empty :items="items" :fields="fields" :current-page="currentPage" :per-page="0"></b-table>
<b-pagination size="md" :total-rows="totalItems" v-model="currentPage" :per-page="perPage"></b-pagination>
</div>

You can also disabled local pagination in the table, so that your items provider becomes responsible for controlling the pagination.

Related

How do you select a single array item from a data object and pass it to another component?

I have some data that I get from axios and pass to a Bootstrap table. In my computed properties where I declare the nameOfPerson field, I have made a click event, so that when a user clicks on the name, a modal opens. This modal also contains the data shown in the table.
However, I would like to change it so that when you click on the name of a person, ONLY the data for THAT single person gets passed to the modal. So instead of passing a prop containing data of ALL users the modal, I just want the data related to the name that I actually click on.
How would I accomplish this?
The parent:
<template>
<b-container>
<b-card class="mt-4">
<b-table
:items="dataItems"
:fields="fields"
:per-page="[5, 10]"
sort-desc
primary-key="id"
/>
</b-card>
<data-modal ref="dataModal" :selected-name="dataItems"/>
</b-container>
</template>
<script>
import {axiosComponent} from '#/axios/services';
import DataModal from '#/components/DataModal';
export default {
components: {
DataModal
},
data() {
return {
dataItems: null,
};
},
computed: {
fields() {
return [
{
key: 'nameOfperson',
label: 'name',
sortable: true
click: () => this.$refs.dataModal.show(),
},
{
key: 'ageOfPerson',
label: 'Age',
sortable: true
},
]
},
},
methods: {
load(){
axiosComponent.getData().then(result => {
this.dataItems = result.data
})
}
},
created() {
this.load()
}
};
</script>
The child (modal)
<template>
<b-modal v-model="showModal">
<div v-for="log in selectedName">
{{ log }}
</div>
</b-modal>
</template>
<script>
export default {
props: {
selectedName: Array
},
data() {
return {
showModal: false,
};
},
methods: {
show(){
this.showModal = true
}
}
};
</script>
You can use #row-selected method, take a look at following demo:
Vue.component('child', {
template: `
<b-modal v-model="showModal">
<div v-for="log in selectedName">
{{ log }}
</div>
</b-modal>
`,
props: {
selectedName: Array,
},
data() {
return {
showModal: false,
};
},
methods: {
show(){
this.showModal = true
}
}
})
new Vue({
el: "#demo",
data() {
return {
dataItems: null,
selected: null,
};
},
computed: {
fields() {
return [
{
key: 'nameOfperson',
label: 'name',
sortable: true,
},
{
key: 'ageOfPerson',
label: 'Age',
sortable: true
},
]
},
},
methods: {
load(){
// axiosComponent.getData().then(result => {
this.dataItems = [{id: 1, nameOfperson: 'aaa', ageOfPerson: 5}, {id: 2, nameOfperson: 'bbb', ageOfPerson: 25}, {id: 3, nameOfperson: 'ccc', ageOfPerson: 35}, {id: 4, nameOfperson: 'ddd', ageOfPerson: 45}]
// })
},
onRowSelected(items) {
this.selected = items
this.$refs.dataModal.show()
},
},
created() {
this.load()
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<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.min.css" />
<script src="https://unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue.min.js"></script>
<script src="https://unpkg.com/bootstrap-vue#latest/dist/bootstrap-vue-icons.min.js"></script>
<div id="demo">
<b-container>
<b-card class="mt-4">
<b-table
:items="dataItems"
:fields="fields"
:per-page="5"
sort-desc
primary-key="id"
selectable
:select-mode="'single'"
#row-selected="onRowSelected"
/>
</b-card>
<child ref="dataModal" :selected-name="selected"></child>
</b-container>
</div>

Default props value are not selected in vue3 options api

I created a select2 wrapper in vue3 with options API everything working fine but the problem is that when getting values from calling API it's not selected the default value in the select2 option. but when I created a static array of objects it does. I don't know why it's working when it comes from the API
Parent Component
Here you can I passed the static options array in options props and my selected value is 2 and it's selected in my Select2 component, but when passed formattedCompanies it's not which is the same format as the static options array then why is not selected any reason here..?
<template>
<Form #submitted="store()" :processing="submitting">
<div class="row">
<div class="col-lg-6">
<div class="form-group">
<label>Company Name</label>
<Select2
:options="options"
v-model="selected"
placeholder="Select Company"
/>
<ValidationError :errors="errors" error-key="name" />
</div>
</div>
</div>
</Form>
</template>
<script>
import Form from "#/components/Common/Form";
import Select2 from "#/components/Common/Select2";
export default {
components: {
Select2,
Form
},
data() {
return {
selected : 2,
companies : [],
options: [ // static array
{ id: 1, text: 'hello' },
{ id: 2, text: 'hello2' },
{ id: 3, text: 'hello3' },
{ id: 4, text: 'hello4' },
{ id: 5, text: 'hello5' },
],
}
},
mounted() {
this.getAllMedicineCompanies()
},
computed:{
formattedCompanies() {
let arr = [];
this.companies.forEach(item => {
arr.push({id: item.id, text: item.name})
});
return arr;
}
},
methods: {
getAllMedicineCompanies(){
axios.get('/api/get-data?provider=companies')
.then(({ data }) => {
this.companies = data
})
},
}
}
</script>
Select2 Component
Here is what my select2 component look like, did I do anything wrong here, please anybody help me
<template>
<select class="form-control">
<slot/>
</select>
</template>
<script>
export default {
name: "Select2",
props: {
options: {
type: [Array, Object],
required: true
},
modelValue: [String, Number],
placeholder: {
type: String,
default: "Search"
},
allowClear: {
type: Boolean,
default: true
},
},
mounted() {
const vm = this;
$(this.$el)
.select2({ // init select2
data: this.options,
placeholder: this.placeholder,
allowClear: this.allowClear
})
.val(this.modelValue)
.trigger("change")
.on("change", function () { // emit event on change.
vm.$emit("update:modelValue", this.value);
});
},
watch: {
modelValue(value) { // update value
$(this.$el)
.val(value)
.trigger("change");
},
options(options) { // update options
$(this.$el)
.empty()
.select2({data: options});
},
},
destroyed() {
$(this.$el)
.off()
.select2("destroy");
}
}
</script>
Probably when this Select2 mounted there is no companies. It is empty array after that it will make API call and it it populates options field and clear all options.
Make:
companies : null,
Change it to
<Select2
v-if="formattedCompanies"
:options="formattedCompanies"
v-model="selected"
placeholder="Select Company"
/>
It should be like this:
<template>
<Form #submitted="store()" :processing="submitting">
<div class="row">
<div class="col-lg-6">
<div class="form-group">
<label>Company Name</label>
<Select2
v-if="formattedCompanies"
:options="formattedCompanies"
v-model="selected"
placeholder="Select Company"
/>
<ValidationError :errors="errors" error-key="name" />
</div>
</div>
</div>
</Form>
</template>
<script>
import Form from "#/components/Common/Form";
import Select2 from "#/components/Common/Select2";
export default {
components: {
Select2,
Form
},
data() {
return {
selected : 2,
companies : null,
options: [ // static array
{ id: 1, text: 'hello' },
{ id: 2, text: 'hello2' },
{ id: 3, text: 'hello3' },
{ id: 4, text: 'hello4' },
{ id: 5, text: 'hello5' },
],
}
},
mounted() {
this.getAllMedicineCompanies()
},
computed:{
formattedCompanies() {
let arr = [];
this.companies.forEach(item => {
arr.push({id: item.id, text: item.name})
});
return arr;
}
},
methods: {
getAllMedicineCompanies(){
axios.get('/api/get-data?provider=companies')
.then(({ data }) => {
this.companies = data
})
},
}
}
</script>
The problem was that my parent component and Select2 component mounted at the same time that's why my computed value is not initialized so the selected value is not selected in the option,
problem solved by setTimeOut function in mounted like this
Select2 Component
<script>
mounted() {
const vm = this;
setTimeout(() => {
$(this.$el)
.select2({ // init select2
data: this.options,
placeholder: this.placeholder,
allowClear: this.allowClear
})
.val(this.modelValue)
.trigger("change")
.on("change", function () { // emit event on change.
vm.$emit("update:modelValue", this.value);
});
}, 500)
},
</script>

How to call a vue.js function on page load with vue3.0 and antd2.1.2

my vue project with vue3.0.0 and antd 2.1.2
there is a function "getScriptList", it can be executed automatically when placed in Component "mounted" with vue2.0
and I wonder how can it be automatically executed when loading the page with vue3.0?
I placed it in the component "onMounted" ,but it's useless.
this function "getScriptlist" can be executed mannul by
"
<a-button type="primary" #click="getScriptlist()">
ExecuteScriptList
"
my vue page test.vue:
<template>
<a-table
:row-selection="{ selectedRowKeys: selectedRowKeys, onChange: onSelectChange }"
:columns="columns"
:data-source="dataSource"
bordered
>
<template #operation="{ record }">
<div>
<span >
<a #click="save(record.key)">Delete</a>
<a #click="edit(record.key)">Edit</a>
</span>
</div>
</template>
</a-table>
</template>
<script>
import { defineComponent, reactive, ref,computed,toRefs,onMounted } from 'vue';
import axios from 'axios';
const columns = [
{
title: 'name',
dataIndex: 'name',
},
{
title: 'timestamp',
dataIndex: 'timestamp',
},
{
title: 'location',
dataIndex: 'location',
},
{
title: 'operation',
dataIndex: 'operation',
slots: {
customRender: 'operation',
},
},
];
export default defineComponent({
setup() {
const dataSource = [];
......
function getScriptlist(){
axios.post(
"/api/script/getPid",
qs.stringify({
pid: 5,
}),
{
headers: { "Content-Type": "application/x-www-form-urlencoded" },
})
.then(response=>{
for( var i=0;i<response.data.length;i++)
{
dataSource.push(response.data[i])
}
})
.catch(function(error){
console.log(error);
});
}
onMounted(() =>
{
getScriptlist();
})
return {
getScriptlist,
dataSource,
columns,
.......
.......
};
},
});
</script>
<style>
</style>
Your component life cycle looks fine, but the dataSource should be defined as ref in order to be reactive:
const dataSource = ref([]);
then replace
for( var i=0;i<response.data.length;i++)
{
dataSource.push(response.data[i])
}
with
dataSource.value=response.data

how can I update progress bar when json data change using vue.js without refresh

I try to make a boostrap dashboard page for tracing status of my python aplication running on server.
On server side, python app update data.json file when reach certain status.
On client side, vue.js handle content creation.
I have a problem when I try to update progress bar, because i need to refresh page so that progress appears.
Any suggestion how can I make live progress bar in my view without refresh?
index.html
<div class="item" v-for="item in order">>
<div class="progress">
<div class="progress-bar bg-warning" role="progressbar" aria-valuenow="50" aria-valuemin="0" aria-valuemax="100" :style="{ width: item.completion + '%' }">
</div>
</div>
</div>
app.js
window.addEventListener('load', () => {
window.vue = new Vue({
el: '#app',
name: 'Order',
data: {
isLoading: true,
order: [],
},
created() {
fetch('./data.json')
.then((res) => { return res.json() })
.then((res) => {
this.isLoading = false;
this.order = res.order;
})
}
})
});
data.json
{
"order": [
{
"customer": "Mr. Smith",
"price": "60",
"status": "Pending",
"orders": "Something",
"completion": 40,
"isAvailable": true,
"isEligible": true
}
]
}
edit: I solve my issue with adding watcher to app.js
watch: {
order() {
this.updateorder();
}
},
methods: {
updateorder() {
fetch('./data.json?_timestamp=' + Date.now())
.then((res) => { return res.json() })
.then((res) => {
this.order = res.order;
})
Does it help?
let i = 0;
const emulateRequest = () => Promise.resolve({
"order": [
{
"customer": "Mr. Smith",
"price": "60",
"status": "Pending",
"orders": "Something",
"completion": i++,
"isAvailable": true,
"isEligible": true
}
]
});
new Vue({
el: '#app',
data: () => ({
isLoading: true,
order: [],
}),
created() {
this.load();
},
methods: {
load() {
//fetch('./data.json')
// .then((res) => { return res.json() })
emulateRequest()
.then((res) => {
this.isLoading = false;
this.order = res.order;
if (this.order.some(({ completion }) => completion !== 100)) {
setTimeout(() => {
this.load();
}, 1000);
}
})
},
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<div id="app">
<div class="item" v-for="item in order">
<div class="progress">
<div class="progress-bar bg-warning" role="progressbar" :aria-valuenow="item.completion" aria-valuemin="0" aria-valuemax="100" :style="{ width: `${item.completion}%` }">
</div>
</div>
</div>
</div>

apply jquery plugin on vue json data

I'm using a vue component to fetch and show data as below:
export default {
data() {
return {
posts: [],
post: {
id: '',
title: '',
rating: '',
ver: ''
},
pagination: {},
}
},
created() {
this.fetchRecentPosts();
},
methods: {
fetchRecentPosts() {
fetch('api/recentposts')
.then(res => res.json())
.then(res => {
this.posts = res.data;
})
}
},
}
I'm also using rateYo plugin to show posts rating.
without using vue.js i'm able to turn my div attribute into star rating by :
$(".rateYo").each(function () {
var rating = $(this).data("rating");
$(this).rateYo({
rating: rating,
starWidth: "13px",
spacing: "3px",
ratedFill: "#ffb300",
normalFill: "#d3d8e4",
readOnly: true
});
});
So, How should I apply it work on evey div inside a v-for loop using vue.js ?
Thanks
var app = new Vue({
el: '#app',
data() {
return {
posts: [
{
id: 1,
title: 'Post One',
rating: 2.5
},
{
id: 2,
title: 'Post Two',
rating: 4.1
}
],
post: {
id: '',
title: '',
rating: '',
ver: ''
},
pagination: {},
}
},
mounted() {
$('.rateYo').each(function() {
$(this).rateYo({
rating: $(this).data('rating'),
starWidth: "13px",
spacing: "3px",
ratedFill: "#ffb300",
normalFill: "#d3d8e4",
readOnly: true
});
});
}
})
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/rateYo/2.3.2/jquery.rateyo.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/rateYo/2.3.2/jquery.rateyo.min.css">
<div id="app">
<div v-for="(post,index) in posts" :key="index">
<h3>{{ post.title }}</h3>
<div class="rateYo" :data-rating="post.rating"></div>
</div>
</div>