@servicemind.tis/tis-smart-table-viewer 2.3.5 → 2.3.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -89,14 +89,12 @@ class ApiDataSource {
|
|
|
89
89
|
this.loadingSubject.next(true);
|
|
90
90
|
this.apiSubs = this.apiService.getList(url, (pageIndex + 1), pageSize, search, { filter }, { sortFilter }).pipe(catchError(() => of([])), finalize(() => this.loadingSubject.next(false))).subscribe(r => {
|
|
91
91
|
console.log(`DataSource: Url: ${url}, Reply:`, r);
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
this.apiSubject.next(r?.data);
|
|
99
|
-
this.extraDataSubject.next(r?.extraData);
|
|
92
|
+
// ✅ FIX: Ensure we always emit an array (even if empty) to prevent undefined issues
|
|
93
|
+
const data = r?.data || [];
|
|
94
|
+
const total = (Array.isArray(data) && data.length > 0) ? (r?.total || data.length) : 0;
|
|
95
|
+
this.totalDataLength.next(total);
|
|
96
|
+
this.apiSubject.next(data);
|
|
97
|
+
this.extraDataSubject.next(r?.extraData || null);
|
|
100
98
|
});
|
|
101
99
|
}
|
|
102
100
|
loadWithCancellation(url, pageIndex, pageSize, search, filter, sortFilter, cancelSubject) {
|
|
@@ -111,14 +109,12 @@ class ApiDataSource {
|
|
|
111
109
|
}
|
|
112
110
|
this.apiSubs = apiCall$.subscribe(r => {
|
|
113
111
|
console.log(`DataSource: Url: ${url}, Reply:`, r);
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
this.apiSubject.next(r?.data);
|
|
121
|
-
this.extraDataSubject.next(r?.extraData);
|
|
112
|
+
// ✅ FIX: Ensure we always emit an array (even if empty) to prevent undefined issues
|
|
113
|
+
const data = r?.data || [];
|
|
114
|
+
const total = (Array.isArray(data) && data.length > 0) ? (r?.total || data.length) : 0;
|
|
115
|
+
this.totalDataLength.next(total);
|
|
116
|
+
this.apiSubject.next(data);
|
|
117
|
+
this.extraDataSubject.next(r?.extraData || null);
|
|
122
118
|
});
|
|
123
119
|
}
|
|
124
120
|
}
|
|
@@ -2096,12 +2092,18 @@ class TisSmartTableViewerComponent {
|
|
|
2096
2092
|
}
|
|
2097
2093
|
if (changes['loadDataApiBaseUrl']) {
|
|
2098
2094
|
if (changes['loadDataApiBaseUrl'].currentValue) {
|
|
2095
|
+
// Clean up existing subscriptions
|
|
2099
2096
|
if (this.loadingSubscription) {
|
|
2100
2097
|
this.loadingSubscription.unsubscribe();
|
|
2101
2098
|
}
|
|
2102
2099
|
if (this.dataLengthSubscription) {
|
|
2103
2100
|
this.dataLengthSubscription.unsubscribe();
|
|
2104
2101
|
}
|
|
2102
|
+
// ✅ FIX: Disconnect previous dataSource to prevent memory leaks
|
|
2103
|
+
if (this.dataSource) {
|
|
2104
|
+
this.dataSource.disconnect({});
|
|
2105
|
+
}
|
|
2106
|
+
// Create new ApiDataSource
|
|
2105
2107
|
this.dataSource = new ApiDataSource(this.apiService);
|
|
2106
2108
|
this.loadingSubscription = this.dataSource.loading$.subscribe(loading => {
|
|
2107
2109
|
if (!loading) {
|
|
@@ -2109,11 +2111,14 @@ class TisSmartTableViewerComponent {
|
|
|
2109
2111
|
this._paginator.pageIndex = this.pageIndex;
|
|
2110
2112
|
this._paginator.pageSize = this.pageSize;
|
|
2111
2113
|
}
|
|
2114
|
+
// ✅ FIX: Always clear caches when new data arrives (even if empty)
|
|
2112
2115
|
// Clear background cache when new data arrives
|
|
2113
2116
|
this.clearRowBackgroundCache();
|
|
2114
2117
|
// Pre-compute all row backgrounds for optimal performance
|
|
2115
2118
|
this.computeAllRowBackgrounds();
|
|
2119
|
+
// ✅ FIX: Ensure selection state is updated even when data changes from empty to populated
|
|
2116
2120
|
this.checkAllRowsSelected();
|
|
2121
|
+
// ✅ FIX: Force change detection by emitting events
|
|
2117
2122
|
this.onDataLoaded.emit(true);
|
|
2118
2123
|
this.onSetExtraData.emit(this.dataSource.extraDataSubject.value);
|
|
2119
2124
|
// if (this.selectedRowIds && this.selectedRowIds.length) {
|
|
@@ -2136,8 +2141,12 @@ class TisSmartTableViewerComponent {
|
|
|
2136
2141
|
if (this.filterFormGroupSubscription) {
|
|
2137
2142
|
this.filterFormGroupSubscription.unsubscribe();
|
|
2138
2143
|
}
|
|
2144
|
+
// ✅ FIX: Use custom comparator for distinctUntilChanged to properly detect form value changes
|
|
2139
2145
|
this.filterFormGroupSubscription = this.filterFormGroup.valueChanges
|
|
2140
|
-
.pipe(takeUntil(this._onDestroy), distinctUntilChanged(
|
|
2146
|
+
.pipe(takeUntil(this._onDestroy), distinctUntilChanged((prev, curr) => {
|
|
2147
|
+
// Custom comparator: deep compare form values
|
|
2148
|
+
return JSON.stringify(prev) === JSON.stringify(curr);
|
|
2149
|
+
})).subscribe(val => {
|
|
2141
2150
|
this.filterHasNonEmptyValue = ValidationHelper.hasNonEmptyValue(val);
|
|
2142
2151
|
});
|
|
2143
2152
|
}
|
|
@@ -2283,11 +2292,21 @@ class TisSmartTableViewerComponent {
|
|
|
2283
2292
|
}
|
|
2284
2293
|
// Compute all row backgrounds when data changes (runs once per data load)
|
|
2285
2294
|
computeAllRowBackgrounds() {
|
|
2286
|
-
|
|
2295
|
+
// ✅ FIX: Always clear the cache first to ensure fresh computation
|
|
2296
|
+
this.computedRowBackgrounds.clear();
|
|
2297
|
+
// ✅ FIX: Safely check if we have data and a background function
|
|
2298
|
+
if (!this.dataSource?.apiSubject?.value ||
|
|
2299
|
+
!Array.isArray(this.dataSource.apiSubject.value) ||
|
|
2300
|
+
!this.rowsConfig.backgroundApplyFunction) {
|
|
2301
|
+
return;
|
|
2302
|
+
}
|
|
2303
|
+
// ✅ FIX: Only compute backgrounds if we have actual data
|
|
2304
|
+
if (this.dataSource.apiSubject.value.length === 0) {
|
|
2287
2305
|
return;
|
|
2288
2306
|
}
|
|
2289
|
-
this.computedRowBackgrounds.clear();
|
|
2290
2307
|
this.dataSource.apiSubject.value.forEach((row) => {
|
|
2308
|
+
if (!row)
|
|
2309
|
+
return; // Skip null/undefined rows
|
|
2291
2310
|
const rowId = row?.id || row?.[this.selectedRowKey] || JSON.stringify(row);
|
|
2292
2311
|
try {
|
|
2293
2312
|
const background = this.rowsConfig.backgroundApplyFunction(row);
|
|
@@ -2376,6 +2395,8 @@ class TisSmartTableViewerComponent {
|
|
|
2376
2395
|
this.isAllExpanded = false;
|
|
2377
2396
|
// Clear expansion state when loading new data to avoid stale expansion state
|
|
2378
2397
|
CollectionHelper.clearSet(this.expandedRowIds);
|
|
2398
|
+
// ✅ FIX: Clear row background cache before loading new data to prevent stale computed backgrounds
|
|
2399
|
+
this.clearRowBackgroundCache();
|
|
2379
2400
|
const filterFormData = this.filterFormGroup?.value;
|
|
2380
2401
|
this.filterHasNonEmptyValue = ValidationHelper.hasFormData(filterFormData);
|
|
2381
2402
|
// Build query string using helper
|
|
@@ -2436,6 +2457,10 @@ class TisSmartTableViewerComponent {
|
|
|
2436
2457
|
UrlHelper.safeNavigate(this.router, url);
|
|
2437
2458
|
}
|
|
2438
2459
|
toggleSelection(status, row) {
|
|
2460
|
+
// ✅ Add null guards to prevent crashes
|
|
2461
|
+
if (!row || !ValidationHelper.hasRowKey(row, this.selectedRowKey)) {
|
|
2462
|
+
return;
|
|
2463
|
+
}
|
|
2439
2464
|
if (this.onlySingleSelection && status.checked) {
|
|
2440
2465
|
this.selection.clear();
|
|
2441
2466
|
}
|
|
@@ -2445,16 +2470,29 @@ class TisSmartTableViewerComponent {
|
|
|
2445
2470
|
this.checkAllRowsSelected();
|
|
2446
2471
|
}
|
|
2447
2472
|
checkAllRowsSelected() {
|
|
2473
|
+
// ✅ Add comprehensive null guards to prevent crashes
|
|
2474
|
+
if (!this.dataSource?.apiSubject?.value || !Array.isArray(this.dataSource.apiSubject.value)) {
|
|
2475
|
+
this.isAllRowsSelected = false;
|
|
2476
|
+
this.allRowsSelectedChange.emit(this.isAllRowsSelected);
|
|
2477
|
+
return;
|
|
2478
|
+
}
|
|
2448
2479
|
this.isAllRowsSelected = this.selection.selected.length === this.dataSource.apiSubject.value.length;
|
|
2449
2480
|
this.allRowsSelectedChange.emit(this.isAllRowsSelected);
|
|
2450
2481
|
}
|
|
2451
2482
|
toggleAllRows() {
|
|
2483
|
+
// ✅ Add comprehensive null guards
|
|
2484
|
+
if (!this.dataSource?.apiSubject?.value || !Array.isArray(this.dataSource.apiSubject.value)) {
|
|
2485
|
+
return;
|
|
2486
|
+
}
|
|
2452
2487
|
if (this.isAllRowsSelected) {
|
|
2453
2488
|
this.selection.clear();
|
|
2454
2489
|
}
|
|
2455
2490
|
else {
|
|
2456
2491
|
this.dataSource.apiSubject.value.forEach(row => {
|
|
2457
|
-
|
|
2492
|
+
// ✅ Validate each row before selecting
|
|
2493
|
+
if (row && ValidationHelper.hasRowKey(row, this.selectedRowKey)) {
|
|
2494
|
+
this.selection.select(row);
|
|
2495
|
+
}
|
|
2458
2496
|
});
|
|
2459
2497
|
}
|
|
2460
2498
|
this.selectedRows = this.selection.selected;
|
|
@@ -2465,8 +2503,13 @@ class TisSmartTableViewerComponent {
|
|
|
2465
2503
|
$event.stopPropagation();
|
|
2466
2504
|
}
|
|
2467
2505
|
isChecked(row) {
|
|
2468
|
-
|
|
2469
|
-
|
|
2506
|
+
// ✅ Add comprehensive null guards
|
|
2507
|
+
if (!row ||
|
|
2508
|
+
!ValidationHelper.hasRowKey(row, this.selectedRowKey) ||
|
|
2509
|
+
!this.selectedIds) {
|
|
2510
|
+
return false;
|
|
2511
|
+
}
|
|
2512
|
+
return this.selectedIds.has(row[this.selectedRowKey]);
|
|
2470
2513
|
}
|
|
2471
2514
|
getQueryParams(url) {
|
|
2472
2515
|
return QueryParamsHelper.parseQueryParams(url);
|
|
@@ -2525,6 +2568,10 @@ class TisSmartTableViewerComponent {
|
|
|
2525
2568
|
return this.computedRowBackgrounds.get(rowId) || null;
|
|
2526
2569
|
}
|
|
2527
2570
|
drop(event) {
|
|
2571
|
+
// ✅ Add null guards to prevent crashes
|
|
2572
|
+
if (!this.dataSource?.apiSubject?.value || !Array.isArray(this.dataSource.apiSubject.value)) {
|
|
2573
|
+
return;
|
|
2574
|
+
}
|
|
2528
2575
|
// Ignore if the item was dropped at the same index
|
|
2529
2576
|
if (event.previousIndex === event.currentIndex) {
|
|
2530
2577
|
return;
|
|
@@ -2548,14 +2595,28 @@ class TisSmartTableViewerComponent {
|
|
|
2548
2595
|
UrlHelper.handleSecondaryButtonClick(this.router, config);
|
|
2549
2596
|
}
|
|
2550
2597
|
setSelectedRows() {
|
|
2598
|
+
// ✅ Add null guards to prevent crashes
|
|
2599
|
+
if (!this.dataSource?.apiSubject?.value ||
|
|
2600
|
+
!Array.isArray(this.dataSource.apiSubject.value) ||
|
|
2601
|
+
!this.selectedRowIds) {
|
|
2602
|
+
this.selection.clear();
|
|
2603
|
+
this.selectedRows = [];
|
|
2604
|
+
this.selectedRowsChange.emit(this.selectedRows);
|
|
2605
|
+
return;
|
|
2606
|
+
}
|
|
2551
2607
|
this.selection.clear();
|
|
2608
|
+
// ✅ FIX: Convert array to Set for O(1) lookup instead of O(n) indexOf
|
|
2609
|
+
const selectedIdsSet = new Set(this.selectedRowIds);
|
|
2552
2610
|
this.dataSource.apiSubject.value.forEach(row => {
|
|
2553
|
-
if (
|
|
2611
|
+
if (row &&
|
|
2612
|
+
ValidationHelper.hasRowKey(row, this.selectedRowKey) &&
|
|
2613
|
+
selectedIdsSet.has(row[this.selectedRowKey])) {
|
|
2554
2614
|
this.selection.select(row);
|
|
2555
2615
|
}
|
|
2556
2616
|
});
|
|
2557
2617
|
this.selectedRows = this.selection.selected;
|
|
2558
2618
|
this.selectedRowsChange.emit(this.selectedRows);
|
|
2619
|
+
this.checkAllRowsSelected();
|
|
2559
2620
|
}
|
|
2560
2621
|
resetSelectedRows() {
|
|
2561
2622
|
this.isAllRowsSelected = false;
|