fix: additional memory leak prevention in Alpine.js watchers and audit events
- Add toastTimeouts array to track and clear pending timeouts - Consolidate duplicate watchers (sortCol, sortAsc, sorted, currentPage) - Add destroy() method to clean up data structures and timeouts - Cache audit events in _cachedAuditEvents instead of computed getter - Clear item data when closing dialogs
This commit is contained in:
parent
3577ee870f
commit
79e6ab63a0
@ -112,50 +112,68 @@ document.addEventListener('alpine:init', () => {
|
||||
this.totalPages = Math.ceil(this.filtered.length / this.pageSize) || 1;
|
||||
},
|
||||
|
||||
// Track timeouts for cleanup
|
||||
toastTimeouts: [],
|
||||
|
||||
init() {
|
||||
this.today = new Date().toISOString().slice(0, 10);
|
||||
|
||||
|
||||
this.filter.date1 = this.today;
|
||||
this.filter.date2 = this.today;
|
||||
|
||||
this.$watch('filterTable', () => {
|
||||
this.currentPage = 1;
|
||||
});
|
||||
|
||||
const defaultPrinter = '<?= $config[session()->get("userrole")]["sampleDialog"]["defaultPrinter"] ?? "lab" ?>';
|
||||
this.selectedPrinter = defaultPrinter || 'lab';
|
||||
|
||||
// Set up watchers to update cached computed properties
|
||||
// Single consolidated watcher for list changes
|
||||
this.$watch('list', () => {
|
||||
this.computeFiltered();
|
||||
this.computeValidatedCount();
|
||||
});
|
||||
this.$watch('filterKey', () => {
|
||||
this.computeFiltered();
|
||||
});
|
||||
|
||||
// Single watcher for filter changes
|
||||
this.$watch('filterKey', () => this.computeFiltered());
|
||||
this.$watch('filterTable', () => {
|
||||
this.currentPage = 1;
|
||||
this.computeFiltered();
|
||||
});
|
||||
|
||||
// Single watcher for computed chain
|
||||
this.$watch('filtered', () => {
|
||||
this.computeSorted();
|
||||
this.computeTotalPages();
|
||||
});
|
||||
this.$watch('sortCol', () => {
|
||||
this.computeSorted();
|
||||
});
|
||||
this.$watch('sortAsc', () => {
|
||||
this.computeSorted();
|
||||
});
|
||||
this.$watch('sorted', () => {
|
||||
this.computePaginated();
|
||||
});
|
||||
this.$watch('currentPage', () => {
|
||||
this.computePaginated();
|
||||
|
||||
// Sort changes trigger pagination update
|
||||
this.$watch('sortCol', () => this.computeSorted());
|
||||
this.$watch('sortAsc', () => this.computeSorted());
|
||||
this.$watch('sorted', () => this.computePaginated());
|
||||
this.$watch('currentPage', () => this.computePaginated());
|
||||
|
||||
// Watch audit data changes to recompute events
|
||||
this.$watch('auditData', () => {
|
||||
this.computeAuditEvents();
|
||||
});
|
||||
|
||||
// Register cleanup on destroy
|
||||
this.$cleanup(() => this.destroy());
|
||||
|
||||
this.fetchList();
|
||||
},
|
||||
|
||||
// Cleanup method to prevent memory leaks
|
||||
destroy() {
|
||||
// Clear all tracked timeouts
|
||||
this.toastTimeouts.forEach(id => clearTimeout(id));
|
||||
this.toastTimeouts = [];
|
||||
|
||||
// Clear large data structures
|
||||
this.list = [];
|
||||
this.filtered = [];
|
||||
this.sorted = [];
|
||||
this.paginated = [];
|
||||
this.auditData = null;
|
||||
this.item = null;
|
||||
this.previewItem = null;
|
||||
},
|
||||
|
||||
fetchList() {
|
||||
this.isLoading = true;
|
||||
@ -215,6 +233,7 @@ document.addEventListener('alpine:init', () => {
|
||||
|
||||
closeSampleDialog() {
|
||||
this.isDialogSampleOpen = false;
|
||||
this.item = null;
|
||||
},
|
||||
|
||||
fetchItem(accessnumber) {
|
||||
@ -282,9 +301,12 @@ document.addEventListener('alpine:init', () => {
|
||||
auditData: null,
|
||||
auditAccessnumber: null,
|
||||
auditTab: 'all',
|
||||
// Cached audit events to prevent memory leak
|
||||
_cachedAuditEvents: [],
|
||||
openAuditDialog(accessnumber) {
|
||||
this.auditAccessnumber = accessnumber;
|
||||
this.auditData = null;
|
||||
this._cachedAuditEvents = [];
|
||||
this.auditTab = 'all';
|
||||
this.isDialogAuditOpen = true;
|
||||
this.fetchAudit(accessnumber);
|
||||
@ -292,6 +314,7 @@ document.addEventListener('alpine:init', () => {
|
||||
closeAuditDialog() {
|
||||
this.isDialogAuditOpen = false;
|
||||
this.auditData = null;
|
||||
this._cachedAuditEvents = [];
|
||||
this.auditAccessnumber = null;
|
||||
},
|
||||
fetchAudit(accessnumber) {
|
||||
@ -300,10 +323,14 @@ document.addEventListener('alpine:init', () => {
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
}).then(res => res.json()).then(data => {
|
||||
this.auditData = data.data;
|
||||
this.computeAuditEvents();
|
||||
});
|
||||
},
|
||||
get getAllAuditEvents() {
|
||||
if (!this.auditData) return [];
|
||||
computeAuditEvents() {
|
||||
if (!this.auditData) {
|
||||
this._cachedAuditEvents = [];
|
||||
return;
|
||||
}
|
||||
let events = [];
|
||||
let id = 0;
|
||||
|
||||
@ -367,15 +394,18 @@ document.addEventListener('alpine:init', () => {
|
||||
});
|
||||
});
|
||||
|
||||
return events.sort((a, b) => {
|
||||
this._cachedAuditEvents = events.sort((a, b) => {
|
||||
if (!a.datetime) return 1;
|
||||
if (!b.datetime) return -1;
|
||||
return new Date(a.datetime) - new Date(b.datetime);
|
||||
});
|
||||
},
|
||||
get getAllAuditEvents() {
|
||||
return this._cachedAuditEvents;
|
||||
},
|
||||
get getFilteredAuditEvents() {
|
||||
if (this.auditTab === 'all') return this.getAllAuditEvents;
|
||||
return this.getAllAuditEvents.filter(e => e.category === this.auditTab);
|
||||
if (this.auditTab === 'all') return this._cachedAuditEvents;
|
||||
return this._cachedAuditEvents.filter(e => e.category === this.auditTab);
|
||||
},
|
||||
|
||||
|
||||
@ -410,9 +440,6 @@ document.addEventListener('alpine:init', () => {
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
selectedPrinter: 'lab',
|
||||
|
||||
printAllLabels(accessnumber) {
|
||||
const printer = this.selectedPrinter || 'lab';
|
||||
fetch(`${BASEURL}/label/all/${accessnumber}/${printer}`, { method: 'GET' })
|
||||
@ -530,7 +557,13 @@ document.addEventListener('alpine:init', () => {
|
||||
toast.className = `alert alert-${type} fixed top-4 right-4 z-50`;
|
||||
toast.innerHTML = `<i class="fa ${type === 'error' ? 'fa-times-circle' : 'fa-check-circle'}"></i> ${message}`;
|
||||
document.body.appendChild(toast);
|
||||
setTimeout(() => toast.remove(), 2000);
|
||||
const timeoutId = setTimeout(() => {
|
||||
toast.remove();
|
||||
// Remove from tracking array
|
||||
const idx = this.toastTimeouts.indexOf(timeoutId);
|
||||
if (idx > -1) this.toastTimeouts.splice(idx, 1);
|
||||
}, 2000);
|
||||
this.toastTimeouts.push(timeoutId);
|
||||
},
|
||||
}));
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user