I’m using Alpine.js to render a list of videos from an API. After making the API call, the response data populates my videos array successfully, but the page doesn’t update to show the new data.
Here’s a breakdown of what’s happening:
I’m making an Axios API request and pushing the resulting videos to the videos array.
I’ve confirmed via console log that videos is populated with the correct data.
Despite this, the template does not render the updated array.
Thank you!
<template x-for="video in videos" :key="video.id">
<!--video content-->
<div>Test Video</div>
</template>
const app = {
triggerElement: null,
page: 1,
lastPage: 24,
itemsPerPage: 24,
observer: null,
isObserverPolyfilled: false,
videos: [],
debug: true,
loaded: false,
init: function () {
window.addEventListener('urlChange', () => {
app.getItems();
});
app.triggerElement = document.querySelector('#infinite-scroll-trigger');
document.addEventListener('DOMContentLoaded', function () {
// app.getItems();
app.loaded = true;
});
app.infiniteScroll();
(app.debug) ? console.log('init' ) : '';
},
infiniteScroll: function () {
(app.debug) ? console.log('infiniteScroll' ) : '';
// Check if browser can use IntersectionObserver which is waaaay more performant
if (!('IntersectionObserver' in window) ||
!('IntersectionObserverEntry' in window) ||
!('isIntersecting' in window.IntersectionObserverEntry.prototype) ||
!('intersectionRatio' in window.IntersectionObserverEntry.prototype)) {
// Loading polyfill since IntersectionObserver is not available
this.isObserverPolyfilled = true
// Storing function in window so we can wipe it when reached last page
window.alpineInfiniteScroll = {
scrollFunc() {
var position = app.triggerElement.getBoundingClientRect()
if (position.top < window.innerHeight && position.bottom >= 0) {
if (app.loaded) {
(app.debug) ? console.log('getItems 1' ) : '';
app.getItems();
}
}
}
}
window.addEventListener('scroll', window.alpineInfiniteScroll.scrollFunc)
} else {
// We can access IntersectionObserver
this.observer = new IntersectionObserver(function (entries) {
if (entries[0].isIntersecting === true) {
if (app.loaded) {
(app.debug) ? console.log('getItems 2' ) : '';
app.getItems();
}
}
}, {threshold: [0]})
this.observer.observe(this.triggerElement)
}
},
getItems: function () {
// TODO: Do fetch here for the content and concat it to populated items
// TODO: Set last page from API call - ceil it
let currentURL = new URL(window.location.href);
currentURL.hostname = 'api.' + currentURL.hostname;
axios.post(currentURL, {
page: this.page,
perPage: this.itemsPerPage,
})
.then(response => {
const data = response.data;
// this.lastPage = data.total_pages;
app.videos = data.data.videos;
// this.page++;
(app.debug) ? console.log(response) : '';
})
.catch(error => {
console.error('Error loading videos:', error);
(app.debug) ? console.log(error) : '';
this.loading = false;
});
// Next page
this.page++
// We have shown the last page - clean up
if (this.lastPage && this.page > this.lastPage) {
if (this.isObserverPolyfilled) {
window.removeEventListener('scroll', window.alpineInfiniteScroll.scrollFunc)
(app.debug) ? console.log('alpineInfiniteScroll') : '';
} else if (this.observer && this.triggerElement) {
try {
this.observer.unobserve(this.triggerElement);
} catch (e) {
console.error('Failed to unobserve element:', e);
}
}
if (this.triggerElement && this.triggerElement.parentNode) {
this.triggerElement.parentNode.removeChild(this.triggerElement);
this.triggerElement = null; // Prevent further access to the removed element
}
}
}
};
Using this.$nextTick() after updating videos to ensure Alpine.js updates.
Manually triggering Alpine.js reactivity.
Directly manipulating the DOM as a temporary workaround (not ideal).
None of these approaches resolved the issue. I’d appreciate any insight into why Alpine.js might not be re-rendering the updated data or any alternative solutions.