How to use "Vue-infinite-loading" in nuxt without loading data by axios? - vue.js

I am working on a Nuxt app. For one of my dynamic pages I want to show data with infinite scrolling. For that purpose I decided to use Vue-infinite-loading. I read this article and also skimmed the documentation of Vue-infinite-loading. In both of them they were using axios module to load data step by step to show in the page when the scroll reaches to specific point in that page. But in my page the data is already present in the page according to page-id with the help of $strapi module and Nuxt fetch hook. The whole code of my page is like below:
<template>
<v-container fluid>
<v-row>
<v-col cols="10" class="mx-auto">
<p v-if="$fetchState.pending">
در حال بارگذاری اطلاعات
</p>
<p v-else-if="$fetchState.error">
مشکلی در نمایش اطلاعات به وجود آمده است.
</p>
<div v-else>
<h1 v-html="this.fourType[this.nameRoute]['title']">
</h1>
<section>
<BaseCard
v-for="item in listShow"
:key="item.id"
:imgId = "item.id"
:sizeCard = "270"
>
<template v-slot:tozih>
{{ item.tozih }}
</template>
<template v-slot:aout>
{{ item.author }}
</template>
</BaseCard>
</section>
<infinite-loading
spinner="spiral"
#infinite="infiniteScroll"
>
</infinite-loading>
</div>
</v-col>
</v-row>
</v-container>
</template>
<script>
export default {
data() {
return {
listBooks: [],
fourType:{
short: {
title: "در این قسمت می‌توانید کتابها را به ترتیب از کوچک (کمترین تعداد صفحه) به بزرگ (بیشترین تعداد صفحه) مشاهده نمایید:",
request: 1
},
programming: {
title: "در این قسمت می‌توانید کتب مرتبط با برنامه‌نویسی (دارای برچسب برنامه‌نویسی) را به ترتیب به روزرسانی (از جدیدترین به قدیمی‌ترین) مشاهده نمایید:",
request: 2
},
new: {
title: "در این قسمت می‌توانید کتب موجود در سایت را به ترتیب به روز رسانی (از جدید به قدیم) مشاهده نمایید:",
request: 3
},
random: {
title: "در این قسمت می‌توانید به صورت تصادفی به مشاهده تمامی کتب موجود در سایت بپردازید:",
request: 4
}
},
nameRoute: this.$route.params.type
}
}, // end of data
computed: {
listShow: function() {
return [ this.listBooks[0], this.listBooks[1] ]
}
}, // end of computed
methods: {
infiniteScroll($state) {
if (this.listBooks.length > 1) {
this.listBooks.forEach((item) => this.listShow.push(item))
$state.loaded()
}
else {
$state.complete()
}
}
}, // end of methods
async fetch() {
switch (this.nameRoute) {
case "short":
this.listBooks = await this.$strapi.$books.find("_sort=pageQuantity:ASC");
break;
case "programming":
this.listBooks = await this.$strapi.$books.find({ 'tags.name': ['برنامه نویسی'], _sort: 'updated_at:DESC' });
break;
case "new":
this.listBooks = await this.$strapi.$books.find("_sort=updated_at:DESC");
break;
default:
this.listBooks = this.$store.getters["books/randomBook"](this.$store.state.books.list2.length);
break;
}
}
}
</script>
<style scoped>
</style>
In the code I get the data in fetch hook (that is different according to page id) and then put it in listBooks Vue data. What I want to do is that show for example 2 data in listBooks first and when the scroll reached to the end of the second data (second card here), I show the rest of data step by step with infinite scrolling method. So I used a Computed data called listShow and used it in v-for. Then I put the code I thought that maybe it might work inside infiniteScroll method. But obviously that does not work, because I just guess that. If anyone know that how change that code to work and show data in infinite scrolling please help me to solve this issue.

Usually, an infinite loader is used to have a small subset of data that you then expand for performance reasons: not having to load 100 elements at once but 10, then 10 more etc...
If you already have the data locally at once and like the behavior of it, without any "pagination" needed from your Strapi backend, you can always watch for the #infinite event and increase the size of your initial array of elements with the next one.
Saying that if you display 10 of them, want to scroll down and show 10 more: display the first 10, then when the infinite event is triggered, append 10 more items to your initial array and so on.
My previous answer may help understand it a bit more.
PS: beware of some reactivity issues that you may face one day when dealing with arrays.

I finally with the help of kissu answer and with the inspiration of the code of the article mentioned in my question found the solution. Here I will post the code of my whole Nuxt page to show the answer for using other developers:
<template>
<v-container fluid>
<v-row>
<v-col cols="10" class="mx-auto">
<p v-if="$fetchState.pending">
در حال بارگذاری اطلاعات
</p>
<p v-else-if="$fetchState.error">
مشکلی در نمایش اطلاعات به وجود آمده است.
</p>
<div v-else>
<h1 v-html="this.fourType[this.nameRoute]['title']">
</h1>
<section>
<BaseCard
v-for="item in list2"
:key="item.id"
:imgId = "item.id"
:sizeCard = "270"
>
<template v-slot:tozih>
{{ item.tozih }}
</template>
<template v-slot:aout>
{{ item.author }}
</template>
</BaseCard>
</section>
<infinite-loading
spinner="spiral"
#infinite="infiniteScroll"
>
</infinite-loading>
</div>
</v-col>
</v-row>
</v-container>
</template>
<script>
export default {
data() {
return {
listBooks: [],
page: 0,
list2: [],
fourType:{
short: {
title: "در این قسمت می‌توانید کتابها را به ترتیب از کوچک (کمترین تعداد صفحه) به بزرگ (بیشترین تعداد صفحه) مشاهده نمایید:",
request: 1
},
programming: {
title: "در این قسمت می‌توانید کتب مرتبط با برنامه‌نویسی (دارای برچسب برنامه‌نویسی) را به ترتیب به روزرسانی (از جدیدترین به قدیمی‌ترین) مشاهده نمایید:",
request: 2
},
new: {
title: "در این قسمت می‌توانید کتب موجود در سایت را به ترتیب به روز رسانی (از جدید به قدیم) مشاهده نمایید:",
request: 3
},
random: {
title: "در این قسمت می‌توانید به صورت تصادفی به مشاهده تمامی کتب موجود در سایت بپردازید:",
request: 4
}
},
nameRoute: this.$route.params.type
}
}, // end of data
methods: {
infiniteScroll($state) {
setTimeout(() => {
let emptyArr = [];
for (let index = this.page*10; index < this.page*10+10; index++) {
if (this.listBooks[index]) {
emptyArr.push(this.listBooks[index])
}
}
if (emptyArr.length > 0) {
emptyArr.forEach(
(item) => this.list2.push(item)
)
$state.loaded();
} else {
$state.complete()
}
this.page++
}, 500)
}
}, // end of methods
async fetch() {
switch (this.nameRoute) {
case "short":
this.listBooks = await this.$strapi.$books.find("_sort=pageQuantity:ASC");
break;
case "programming":
this.listBooks = await this.$strapi.$books.find({ 'tags.name': ['برنامه نویسی'], _sort: 'updated_at:DESC' });
break;
case "new":
this.listBooks = await this.$strapi.$books.find("_sort=updated_at:DESC");
break;
default:
this.listBooks = this.$store.getters["books/randomBook"](this.$store.state.books.list2.length);
break;
}
}
}
</script>
<style scoped>
</style>
the two important changes were:
1- Using an empty array called list2 in my Vue data instead of Vue computed data.
2- Using a variable called emptyArr in my infiniteScroll method that holds only for example 10 items of the original data (Vue listBooks data) and is showing them with scrolling the page each time that 10 data passed from the user view.

Related

GoogleMap Set longitude and latitude after request

I Developer this Template and put its code below. When I type a phrase, Google offers me an address, and when I click on it, the map zooms automatically and displays the marker, as in the picture below:
In the image below, the fields (longitude and latitude) are automatically set only when the map is clicked, but I need the values ​​of the fields (longitude) when I search for the address at the same time (as I explained above) and latitude) are updated and the user does not need to necessarily click on the map.
I just started working with vuejs. Please guide me by editing my code. Thanks
<template>
<div no-body class="mb-1 w-100">
<div class="body-bg shadow-none" id="test2">
<div
:class="{
'is-invalid':
$parent.form.errors.has('auto_complete_map') ||
$parent.form.errors.has('location'),
}"
class="d-flex flex-row justify-content-center border border-1 rounded border-dark"
>
<h4 block v-b-toggle.accordion-2 variant="info">Location</h4>
</div>
</div>
<b-collapse
class="navbar-fixed-top"
id="accordion-2"
accordion="my-accordion"
>
<div class="d-flex flex-row gap-2 justify-content-between mt-2">
<div class="col-sm-6">
<label for="latitude" class="form-label"
>{{ $t("latitude")
}}<span class="small text-danger m-1">*</span></label
>
<input
id="latitude"
:value="latitude"
class="form-control bg-transparent"
name="latitude"
placeholder=""
spellcheck="false"
data-ms-editor="true"
:disabled="true"
/>
<has-error :form="form" field="latitude" />
</div>
<div class="col-sm-6">
<label for="longitude" class="form-label">{{
$t("longitude")
}}</label>
<input
id="longitude"
class="form-control bg-transparent"
:value="longitude"
name="longitude"
placeholder=""
spellcheck="false"
data-ms-editor="true"
:disabled="true"
/>
<has-error :form="form" field="longitude" />
</div>
</div>
<label class="form-label mt-2"
>{{ $t("location") }}<span class="small text-danger">*</span></label
>
<GmapAutocomplete
id="auto_complete_map"
ref="location"
v-validate="'required|min:5'"
class="form-control"
name="location"
:value="location"
:placeholder="$t('enter a location')"
aria-required="true"
:disabled="$parent.canEditProperty()"
#place_changed="setPlace"
#keydown.enter.prevent
/>
<has-error :form="form" field="auto_complete_map" />
<GmapMap
ref="map"
class="my-3"
:center="center"
:zoom="zoom"
style="width: 100%; height: 300px"
#click="clickMap"
>
<GmapMarker
v-for="(m, index) in markers"
:key="index"
:position="m.position"
#click="center = m.position"
/>
</GmapMap>
<!-- <div class="d-flex flex-row justify-content-end m-2">
<b-button class="rounded-lg px-4 text-lg" block v-b-toggle.accordion-3 variant="info">next</b-button>
</div> -->
<div class="d-flex flex-row justify-content-end gap-3 m-2 mt-3">
<b-button
class="rounded-lg px-4 col-sm-2 text-white text-lg border border-dark"
block
v-b-toggle.accordion-1
variant="dark"
>previous</b-button
>
<b-button
class="rounded-lg px-6 col-sm-2 text-lg border border-dark"
block
v-b-toggle.accordion-3
variant="light"
>Next Step</b-button
>
</div>
</b-collapse>
</div>
</template>
<script>
import Autocomplete from "vue2-google-maps/dist/components/autocomplete";
export default {
name: "GoogleMap",
props: {
location: { type: String, default: "" },
latitude: { type: String, default: "" },
longitude: { type: String, default: "" },
},
data() {
return {
center: {
lat: 45.508,
lng: -73.587,
},
currentPlace: null,
markers: [],
places: [],
zoom: 12,
};
},
mounted() {
this.geolocate();
},
methods: {
setPlace(place) {
this.markers.splice(0, this.markers.length);
this.places.splice(0, this.places.length);
this.currentPlace = place;
this.updateLocation(place.formatted_address);
this.addMarker();
},
addMarker() {
if (this.currentPlace) {
const marker = {
lat: this.currentPlace.geometry.location.lat(),
lng: this.currentPlace.geometry.location.lng(),
};
this.markers.push({ position: marker });
this.places.push(this.currentPlace);
this.center = marker;
this.zoom = 17;
this.currentPlace = null;
}
},
clickMap(location) {
const marker = location.latLng;
// this.markers.clear()
this.markers.splice(0, this.markers.length);
this.places.splice(0, this.places.length);
this.markers.push({ position: marker });
// this.places.push(this.currentPlace)
// this.center = marker
// this.zoom = 17
this.currentPlace = null;
const geocoder = new google.maps.Geocoder();
geocoder
.geocode({ location: location.latLng })
.then((response) => {
if (response.results[0]) {
this.updateLocation(response.results[0].formatted_address);
this.getLoc(location.latLng);
} else {
window.alert("No results found");
}
})
.catch((e) => window.alert("Geocoder failed due to: " + e));
},
geolocate: function () {
const oldLocation = this.location;
if (oldLocation.length > 0) {
const _this = this;
const geocoder = new google.maps.Geocoder();
geocoder.geocode({ address: oldLocation }, function (results, status) {
if (status == google.maps.GeocoderStatus.OK) {
// const lat = results[0].geometry.location.lat()
// const lng = results[0].geometry.location.lng()
// const placeName = results[0].address_components[0].long_name
if (results.length > 0) {
_this.setPlace(results[0]);
}
}
});
} else {
navigator.geolocation.getCurrentPosition((position) => {
this.center = {
lat: position.coords.latitude,
lng: position.coords.longitude,
};
});
}
},
updateLocation: function (newLocation) {
this.location = newLocation;
this.$emit("eventname", newLocation);
},
getLoc: function (location) {
// this.location = this.markers
this.longitude = location.lng();
this.latitude = location.lat();
this.$emit("getlog", location);
},
},
};
</script>
the passed in variable place in setPlace(place) has properties .geometry.location.lag() and .geometry.location.lng(). These are your latitude and longitude values for the entered address. Use these to set the latitude and longitude variables used by your <input> elements
setPlace(place) {
this.markers.splice(0, this.markers.length);
this.places.splice(0, this.places.length);
this.currentPlace = place;
this.updateLocation(place.formatted_address);
this.addMarker();
this.latitude = place.geometry.location.lat();
this.longitude = place.geometry.location.lng();
},
I do also want to point out that you've created latitude and longitude as props, which aren't meant to be mutated. If you don't have a reason to have them as props put them as data variables instead

HTTP post returns 400 (Bad Request)

I am trying to save data using Axios API from my front end which is built using the Quasar framework. When I am sending the API request it is responding 400 (Bad Request). SO far what have tried is given in below. I am new to this Framework. So if anyone can help me with this and suggest any good tutorial for learning Quasar then it will be much more helpful for me.
<template>
<q-page>
<div class="row q-col-gutter-sm q-ma-xs q-mr-sm">
<div class="col-lg-4 col-md-4 col-sm-12 col-xs-12">
<q-card
flat
bordered
class
>
<q-card-section>
<div class="text-h6">Add Style Status</div>
</q-card-section>
<q-separator inset></q-separator>
<q-card-section>
<q-form>
<q-list>
<!-- <q-item>
<q-item-section>
<q-input
dense
outlined
v-model="styleStatus.styleStatusId"
label="Style Status Id"
/>
</q-item-section>
</q-item> -->
<q-item>
<q-item-section>
<q-input
dense
outlined
v-model="styleStatus.statusName"
label="Style Status Name"
/>
</q-item-section>
</q-item>
<q-card-actions
align="right"
class="text-teal"
>
<q-btn
:label="styleStatus.styleStatusId==='' ? 'Save' : 'Update' "
type="submit"
color="positive"
v-close-popup
:disabled="!styleStatus.statusName"
icon-right="save"
#click="addNewStyleStatus()"
/>
</q-card-actions>
</q-list>
</q-form>
</q-card-section>
</q-card>
</div>
</div>
</q-page>
</template>
<script>
export default {
data () {
return {
filter: '',
mode: 'list',
styleStatus: {
styleStatusId: '',
statusName: ''
},
pagination: {
rowsPerPage: 10
},
model: null,
data: this.data
}
},
methods: {
addNewStyleStatus () {
if (this.styleStatus.styleStatusId !== '') {
this.updateData()
} else {
this.saveData()
}
},
saveData () {
this.$axios.post('http://192.168.0.11:8020/api/StyleStatuses', this.styleStatus)
.then(res => {
console.log(res)
this.$q.notify({
color: 'positive',
position: 'top-right',
message: 'Record Inserted Successfully',
icon: 'check_circle'
})
this.getAllStyleStatus()
})
.catch(() => {
this.$q.notify({
color: 'negative',
position: 'top-right',
message: 'Oooops!! something was wrong.',
icon: 'report_problem'
})
})
},
updateData () {
this.$axios.put('http://192.168.0.11:8020/api/StyleStatuses/', this.styleStatus)
.then(res => {
this.$q.notify({
color: 'positive',
position: 'top-right',
message: 'Record Updated Successfully',
icon: 'check_circle'
})
this.styleStatus = {}
this.getAllStyleStatus()
})
.catch(() => {
this.$q.notify({
color: 'negative',
position: 'top-right',
message: 'Oooops!! something was wrong.',
icon: 'report_problem'
})
})
},
editData (x) {
this.styleStatus = x.row
}
}
}
</script>
<style scoped></style>

Using nuxt.js google recaptcha module

There is a problem that is wasting too much time. I installed the Nuxt js recaptcha module. but the information given in the documentation is insufficient. I haven't used recaptcha before. How exactly should I use it.
<template>
<div class="mx-auto mt-5" style="width: 500px; max-width:90%">
<div class="mx-auto mt-5" style="width: 230px;">
<img
src="#/assets/media/images/site/logo.png"
style="width: 110px"
/>.com'a Üye Olun
</div>
<div class="bg-white p-4 mt-2" style="border-radius:20px">
<b-form #submit.prevent="onSubmit" #reset="onReset" v-if="show">
<b-form-group id="input-group-2" label-for="input-2">
<b-form-input
id="input-2"
class="form-control form-control-lg"
v-model="form.userFullName"
placeholder="İsim soyisim"
required
></b-form-input>
</b-form-group>
<b-form-group id="input-group-2" label-for="input-2">
<b-form-input
id="input-5"
class="form-control form-control-lg"
v-model="form.userName"
placeholder="Kullanıcı adı"
required
></b-form-input>
</b-form-group>
<b-form-row>
<b-col>
<b-form-input
id="input-1"
v-model="form.userEmail"
type="email"
class="form-control form-control-lg"
placeholder="E-mail adresiniz"
required
></b-form-input>
</b-col>
<b-col>
<b-form-input
id="input-3"
v-model="form.userPassword"
class="form-control form-control-lg"
placeholder="Şifreniz"
required
></b-form-input>
</b-col>
</b-form-row>
<b-form-group
id="input-group-4"
class="mt-3"
v-slot="{ ariaDescribedby }"
>
<b-form-checkbox-group
v-model="form.checked"
id="checkboxes-4"
:aria-describedby="ariaDescribedby"
>
<b-form-checkbox class="text-dark" value="1"
>Beni Hatırla</b-form-checkbox
>
</b-form-checkbox-group>
</b-form-group>
<b-button
:disabled="isClickSubmit"
type="submit"
class="btn btn-dark btn-lg btn-block"
variant="primary"
>
<b-spinner v-if="isClickSubmit" small style="margin-bottom:3px" type="grow"></b-spinner>
Kaydol</b-button
>
</b-form>
</div>
</div>
</template>
import axios from "axios";
export default {
layout: "default",
data() {
return {
isClickSubmit: false,
form: {
userEmail: "",
userFullName: "",
userName: "",
userPassword: null
},
show: true
};
},
methods: {
async mounted() {
try {
const bune = await this.$recaptcha.init();
console.log(bune);
} catch (e) {
console.log(e);
}
},
async onSubmit(event) {
this.isClickSubmit = true;
this.onReset();
try {
console.log(this.$recaptcha);
const token = await this.$recaptcha.execute("login");
console.log("ReCaptcha token:", token);
// await this.$recaptcha.reset()
const form = this.form;
const sonuc = await axios.post("http://localhost:3000/api/users", {
form
});
this.isClickSubmit = false
} catch (error) {
console.log("Login error:", error);
}
// console.log(JSON.stringify(this.form));
},
onReset() {
this.form.userEmail = "";
this.form.userFullName = "";
this.form.userName = "";
this.form.userPassword = null
}
}
};
nuxt.config.js:
env: {
GOOGLE_SECRET: '...' },
privateRuntimeConfig: {
secretKey: process.env.GOOGLE_SECRET },
modules: [
[
"#nuxtjs/recaptcha",
{
siteKey:process.env.GOOGLE_SECRET ,
version: 3,
} ]
],
You don't seem to have the recaptcha element in your template.
<!-- Add this where you want the captcha, regardless of version -->
<recaptcha #error="onError" #success="onSuccess" #expired="onExpired" />
<script>
export default {
data() {
return {
isClickSubmit: false,
form: {
userEmail: "",
userFullName: "",
userName: "",
userPassword: null,
token: null
},
show: true
};
},
methods: {
onSuccess(token) {
this.form.token = token;
},
onExpired() {
this.$recaptcha.reset();
},
onError(error) {
console.error(error);
}
}
}
Before you make your request, you'll need to send some things to Google. You'll make this call before serving any requests. This function is from a project of mine.
// Backend code
function Recaptcha(token, ip, callback) {
axios.post(`https://www.google.com/recaptcha/api/siteverify?secret=${SECRET_KEY}&response=${token}`,
{
remoteip: ip,
},
{
headers: {
'Content-Type':
'application/x-www-form-urlencoded; charset=utf-8',
},
},
)
.then(callback);
}
Example usage of Recaptcha function:
Hopefully this helps you understand it a bit better.

Cannot get computed property (array)

Trying to get a 'displayImages' array as a computed property. Using a default 'selected' property = 0.
this.selected changes accordingly on mouseover and click events.
When trying to get the computed 'displayImages' it says:
"this.variations[this.selected] is undefined."
I'm using an api to get my product data and images.
<template>
<div id="product-page">
<v-card width="100%" class="product-card">
<div class="image-carousel">
<v-carousel height="100%" continuos hide-delimiters>
<v-carousel-item
v-for="(image, i) in displayImages"
:key="i"
:src="image"
>
</v-carousel-item>
</v-carousel>
</div>
<div class="details">
<h2>{{ this.title }}<br />Price: ${{ this.price }}</h2>
<p>{{ this.details }}</p>
<ul style="list-style: none; padding: 0">
<li
style="border: 1px solid red; width: auto"
v-for="(color, index) in variations"
:key="index"
#mouseover="updateProduct(index)"
#click="updateProduct(index)"
>
{{ color.color }}
</li>
</ul>
<div class="buttons">
<v-btn outlined rounded
>ADD TO CART<v-icon right>mdi-cart-plus</v-icon></v-btn
>
<router-link to="/shop">
<v-btn text outlined rounded> BACK TO SHOP</v-btn>
</router-link>
</div>
</div>
</v-card>
</div>
</template>
<script>
export default {
name: "Product",
props: ["APIurl"],
data: () => ({
title: "",
details: "",
price: "",
variations: [],
selected: 0,
}),
created() {
fetch(this.APIurl + "/products/" + this.$route.params.id)
.then((response) => response.json())
.then((data) => {
//console.log(data);
this.title = data.title;
this.details = data.details.toLowerCase();
this.price = data.price;
data.variations.forEach((element) => {
let imagesArray = element.photos.map(
(image) => this.APIurl + image.url
);
this.variations.push({
color: element.title,
images: imagesArray,
qty: element.qty,
productId: element.productId,
});
});
});
},
computed: {
displayImages() {
return this.variations[this.selected].images;
},
},
methods: {
updateProduct: function (index) {
this.selected = index;
console.log(index);
}
},
};
</script>
To properly expand on my comment, the reason why you are running into an error is because when the computed is being accessed in the template, this.variations is an empty array. It is only being populated asynchronously, so chances are, it is empty when VueJS attempts to use it when rendering the virtual DOM.
For that reason, accessing an item within it by index (given as this.selected) will return undefined. Therefore, attempting to access a property called images in the undefined object will return an error.
To fix this problem, all you need is to introduce a guard clause in your computed as such:
computed: {
displayImages() {
const variation = this.variations[this.selected];
// GUARD: If variation is falsy, return empty array
if (!variation) {
return [];
}
return variation.images;
},
}
Bonus tip: if you one day would consider using TypeScript, you can even simplify it as such... but that's a discussion for another day ;) for now, optional chaining and the nullish coalescing operator is only supported by bleeding edge versions of evergreen browsers.
computed: {
displayImages() {
return this.variations[this.selected]?.images ?? [];
},
}
For avoid this kind of error, you must to use the safe navigation property.
Remember, it's useful just when the app is loading.
Try something like that:
<script>
export default {
name: 'Product',
computed: {
displayImages() {
if (this.variations[this.selected]) {
return this.variations[this.selected].images;
}
return [];
},
},
};
</script>

Vuejs compute property doesn't get updated properly

I'm very new to Vuejs, I'm following their documentation which is very helpful. However, I find it difficult to understand how compute properties actually are triggered.
I'm using ag-grid for my project and I would like to update the total number of rows to my custom page size drop-down list.
The following is my code:
<template>
<div id="ag-grid-demo">
<vx-card>
<!-- TABLE ACTION ROW -->
<div class="flex flex-wrap justify-between items-center">
<!-- ITEMS PER PAGE -->
<div class="mb-4 md:mb-0 mr-4 ag-grid-table-actions-left"></div>
<!-- TABLE ACTION COL-2: SEARCH & EXPORT AS CSV -->
<div class="flex flex-wrap items-center justify-between ag-grid-table-actions-right">
<vs-button class="mb-4 md:mb-0" #click="gridApi.exportDataAsCsv()">Export as CSV</vs-button>
</div>
</div>
<ag-grid-vue
ref="agGridTable"
:gridOptions="gridOptions"
class="ag-theme-material w-100 my-4 ag-grid-table"
:columnDefs="columnDefs"
:defaultColDef="defaultColDef"
:rowModelType="rowModelType"
#grid-ready="onGridReady"
rowSelection="multiple"
colResizeDefault="shift"
:animateRows="true"
:pagination="true"
:paginationPageSize="paginationPageSize"
:cacheBlockSize="cacheBlockSize"
:enableRtl="$vs.rtl"
:modules="modules"
></ag-grid-vue>
<div class="flex flex-wrap justify-between items-center">
<!-- CUSTOM PAGESIZE DROP-DWON -->
<div class="mb-4 md:mb-0 mr-4 ag-grid-table-actions-left">
<vs-dropdown vs-trigger-click class="cursor-pointer">
<div class="p-4 border border-solid d-theme-border-grey-light rounded-full d-theme-dark-bg cursor-pointer flex items-center justify-between font-medium">
<span class="mr-2"
>{{ currentPage * paginationPageSize - (paginationPageSize - 1) }} - {{ recordCount - currentPage * paginationPageSize > 0 ? currentPage * paginationPageSize : recordCount }} of {{ recordCount }}</span>
<feather-icon icon="ChevronDownIcon" svgClasses="h-4 w-4" />
</div>
<vs-dropdown-menu>
<vs-dropdown-item #click="gridApi.paginationSetPageSize(10)">
<span>10</span>
</vs-dropdown-item>
<vs-dropdown-item #click="gridApi.paginationSetPageSize(50)">
<span>50</span>
</vs-dropdown-item>
<vs-dropdown-item #click="gridApi.paginationSetPageSize(100)">
<span>100</span>
</vs-dropdown-item>
<vs-dropdown-item #click="gridApi.paginationSetPageSize(150)">
<span>150</span>
</vs-dropdown-item>
</vs-dropdown-menu>
</vs-dropdown>
</div>
<!-- CUSTOM TABLE PAGINATION -->
<div class="flex flex-wrap items-center justify-between ag-grid-table-actions-right">
<vs-pagination :total="totalPages" :max="maxPageNumbers" v-model="currentPage" />
</div>
</div>
</vx-card>
</div>
</template>
<script>
import { AgGridVue } from "ag-grid-vue";
import { ServerSideRowModelModule } from "#ag-grid-enterprise/server-side-row-model";
import { MenuModule } from "#ag-grid-enterprise/menu";
import { ColumnsToolPanelModule } from "#ag-grid-enterprise/column-tool-panel";
import CompanyServices from "../../../_services/company.service";
import "#/assets/scss/vuexy/extraComponents/agGridStyleOverride.scss";
export default {
components: {
AgGridVue
},
data() {
return {
gridOptions: {},
maxPageNumbers: 7,
gridApi: null,
defaultColDef: {
sortable: true,
editable: false,
resizable: true,
suppressMenu: false
},
columnDefs: [
{ headerName: "Id", field: "id", filter: false },
{
headerName: "Company Name",
field: "companyName",
filter: true,
checkboxSelection: true,
headerCheckboxSelectionFilteredOnly: true
}
],
rowModelType: "serverSide",
modules: [ServerSideRowModelModule, MenuModule, ColumnsToolPanelModule],
cacheBlockSize: 10,
};
},
computed: {
paginationPageSize() {
if (this.gridApi) return this.gridApi.paginationGetPageSize();
else return 10;
},
totalPages() {
if (this.gridApi) return this.gridApi.paginationGetTotalPages();
else return 0;
},
currentPage: {
get() {
if (this.gridApi) return this.gridApi.paginationGetCurrentPage() + 1;
else return 1;
},
set(val) {
this.gridApi.paginationGoToPage(val - 1);
}
},
recordCount: function() {
if (window.recordCount === undefined) return 0;
else return window.recordCount;
}
},
methods: {
onGridReady: function(params) {
var datasource = new ServerSideDatasource();
params.api.setServerSideDatasource(datasource);
}
},
mounted() {
this.gridApi = this.gridOptions.api;
this.gridColumnApi = this.gridOptions.columnApi;
}
};
window.ServerSideDatasource = function ServerSideDatasource(server) {
return {
getRows: function(params) {
CompanyServices.list({
startRow: params.request.startRow,
endRow: params.request.endRow,
SortColumn: "CompanyName",
SortOrder: "asc"
})
.then(response => {
window.recordCount = response.data.total;
params.successCallback(response.data.rows, response.data.total);
})
.catch(error => {
params.failCallback();
});
}
};
};
</script>
My issue is that computed property 'recordCount' does not get updated as 'window.recordCount' being changed. 'recordCount' always shows value as zero.
Could someone please shed a light here?
Thanks in advance!
There seems to be a couple of issues here, mainly that you are setting data and functions outside of the vue instance.
First of all, you should not be setting any data on window, Vue has plenty of ways to handle your data and makes it all reactive for you.
Start by moving your window.ServerSideDataSource function into the vue instance.
In Vue, functions are put under "methods", so after your onGridReady function, add a ,
and then put:
serverSideDatasource() {
return {
getRows: functions(params) {..}
}
}
Notice that I put serverSideDatasource with a small starting s, as camelCase is widely considered best practice when naming variables and functions.
Next, your window.recordCount should be put into Vue's data. Just add it after cacheBlockSize and set it like so:
recordCount: 0
Now that all your data and methods (functions) are within Vue, you can reach them by using "this".
So replace "window." with "this." all the places you use it.
Now, since your recordCount starts as 0, and is only changed when you call getRows, there is no need for a computed called recordCount, so delete that.
Notice: Having both a property in data and computed called recordCount would conflict, so even if you kept it, you would have to rename the computed.
Do tell me if this fixes it, or how far it gets you :)