@acorex/cdk 20.2.0-next.13 → 20.2.0-next.15
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.
package/common/index.d.ts
CHANGED
|
@@ -996,8 +996,8 @@ declare abstract class MXSelectionValueComponent<T = unknown> extends MXValueCom
|
|
|
996
996
|
*/
|
|
997
997
|
private createCacheKey;
|
|
998
998
|
/**
|
|
999
|
-
|
|
1000
|
-
|
|
999
|
+
* Normalizes currently selected items and updates the data service
|
|
1000
|
+
*/
|
|
1001
1001
|
private _normalizeSelectedItems;
|
|
1002
1002
|
/**
|
|
1003
1003
|
* Unselects the specified items from the selection
|
|
@@ -1026,6 +1026,12 @@ declare abstract class MXSelectionValueComponent<T = unknown> extends MXValueCom
|
|
|
1026
1026
|
* @returns True if the item is disabled
|
|
1027
1027
|
*/
|
|
1028
1028
|
isItemDisabled(item: T): boolean;
|
|
1029
|
+
/**
|
|
1030
|
+
* Checks if an item is currently loading
|
|
1031
|
+
* @param item Item to check
|
|
1032
|
+
* @returns True if the item is loading
|
|
1033
|
+
*/
|
|
1034
|
+
isItemLoading(item: T): boolean;
|
|
1029
1035
|
/**
|
|
1030
1036
|
* Gets the display text for an item using template or text field
|
|
1031
1037
|
* @param item Item to get display text for
|
|
@@ -1046,6 +1052,25 @@ declare abstract class MXSelectionValueComponent<T = unknown> extends MXValueCom
|
|
|
1046
1052
|
* Clears only the cache while preserving selected items
|
|
1047
1053
|
*/
|
|
1048
1054
|
protected softClearSelectionCache(): void;
|
|
1055
|
+
/**
|
|
1056
|
+
* Clears the debouncing cache for specific items to ensure fresh data is displayed
|
|
1057
|
+
*/
|
|
1058
|
+
protected clearItemDebouncingCache(item: T): void;
|
|
1059
|
+
/**
|
|
1060
|
+
* Forces a refresh of the display when items are loaded asynchronously
|
|
1061
|
+
* This should be called when the data source is updated
|
|
1062
|
+
*/
|
|
1063
|
+
protected refreshDisplay(): void;
|
|
1064
|
+
/**
|
|
1065
|
+
* Refreshes the display for all selected items to ensure consistency
|
|
1066
|
+
* This is useful when the data source has been updated
|
|
1067
|
+
*/
|
|
1068
|
+
protected refreshSelectedItemsDisplay(): void;
|
|
1069
|
+
/**
|
|
1070
|
+
* Forces a refresh of all selected items by clearing their debouncing cache
|
|
1071
|
+
* This ensures fresh data is displayed when the data source is updated
|
|
1072
|
+
*/
|
|
1073
|
+
protected forceRefreshSelectedItems(): void;
|
|
1049
1074
|
/**
|
|
1050
1075
|
* Asynchronously loads an item by its key and caches the result
|
|
1051
1076
|
* @param key Key to load item by
|
|
@@ -1806,9 +1806,15 @@ class MXSelectionValueComponent extends MXValueComponent {
|
|
|
1806
1806
|
if (value == null || (isArray && value.length === 0)) {
|
|
1807
1807
|
return this.multiple ? [] : null;
|
|
1808
1808
|
}
|
|
1809
|
-
//
|
|
1809
|
+
// Check if items already have text properties to avoid unnecessary findByKey calls
|
|
1810
1810
|
const itemsToNormalize = isArray ? value : [value];
|
|
1811
|
-
const
|
|
1811
|
+
const needsFindByKey = itemsToNormalize.some(item => {
|
|
1812
|
+
if (typeof item === 'object' && item !== null) {
|
|
1813
|
+
return get(item, this.textField) == null;
|
|
1814
|
+
}
|
|
1815
|
+
return true; // Primitive values need findByKey
|
|
1816
|
+
});
|
|
1817
|
+
const normalizedItems = this.normalizeItemsList(itemsToNormalize, needsFindByKey);
|
|
1812
1818
|
if (normalizedItems.length === 0) {
|
|
1813
1819
|
return this.multiple ? [] : null;
|
|
1814
1820
|
}
|
|
@@ -1858,13 +1864,14 @@ class MXSelectionValueComponent extends MXValueComponent {
|
|
|
1858
1864
|
}
|
|
1859
1865
|
const hasTextProperty = !isComplexObject || get(itemRecord, this.textField) != null;
|
|
1860
1866
|
const normalizedObj = {};
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1867
|
+
// If findByKey is true, we need to resolve the item to get its text
|
|
1868
|
+
// This is needed for string values like 'ir', 'us' that don't have text
|
|
1869
|
+
if (findByKey) {
|
|
1870
|
+
this.handleItemNormalization(normalizedObj, item, key, true, cacheKey);
|
|
1864
1871
|
}
|
|
1865
1872
|
else {
|
|
1866
|
-
//
|
|
1867
|
-
|
|
1873
|
+
// Item already has all required properties, just copy them
|
|
1874
|
+
Object.assign(normalizedObj, item);
|
|
1868
1875
|
}
|
|
1869
1876
|
this.dataService.cacheList[cacheKey] = normalizedObj;
|
|
1870
1877
|
return normalizedObj;
|
|
@@ -1891,7 +1898,7 @@ class MXSelectionValueComponent extends MXValueComponent {
|
|
|
1891
1898
|
// Set loading state
|
|
1892
1899
|
obj[this.valueField] = key;
|
|
1893
1900
|
obj['isLoading'] = true;
|
|
1894
|
-
obj[this.textField] = 'Loading';
|
|
1901
|
+
obj[this.textField] = 'Loading...';
|
|
1895
1902
|
promise
|
|
1896
1903
|
.then(result => {
|
|
1897
1904
|
if (typeof result === 'object' && result) {
|
|
@@ -1901,11 +1908,23 @@ class MXSelectionValueComponent extends MXValueComponent {
|
|
|
1901
1908
|
obj[this.valueField] = result || key;
|
|
1902
1909
|
obj[this.textField] = result;
|
|
1903
1910
|
}
|
|
1911
|
+
})
|
|
1912
|
+
.catch(error => {
|
|
1913
|
+
console.warn('Failed to load item by key:', key, error);
|
|
1914
|
+
// On error, keep the original value but mark as failed
|
|
1915
|
+
obj[this.textField] = this.defaultText;
|
|
1904
1916
|
})
|
|
1905
1917
|
.finally(() => {
|
|
1906
1918
|
delete obj['isLoading'];
|
|
1919
|
+
delete obj['_displayTextLoading'];
|
|
1920
|
+
delete obj['_loadingTriggered'];
|
|
1907
1921
|
this.dataService.cacheList[cacheKey] = obj;
|
|
1908
|
-
|
|
1922
|
+
// Use setTimeout to defer change detection and prevent immediate re-rendering
|
|
1923
|
+
setTimeout(() => {
|
|
1924
|
+
if (this.cdr) {
|
|
1925
|
+
this.cdr.markForCheck();
|
|
1926
|
+
}
|
|
1927
|
+
}, 0);
|
|
1909
1928
|
});
|
|
1910
1929
|
}
|
|
1911
1930
|
/**
|
|
@@ -1934,13 +1953,19 @@ class MXSelectionValueComponent extends MXValueComponent {
|
|
|
1934
1953
|
return `k-${key}`;
|
|
1935
1954
|
}
|
|
1936
1955
|
/**
|
|
1937
|
-
|
|
1938
|
-
|
|
1956
|
+
* Normalizes currently selected items and updates the data service
|
|
1957
|
+
*/
|
|
1939
1958
|
_normalizeSelectedItems() {
|
|
1940
1959
|
const values = Array.isArray(this.value)
|
|
1941
1960
|
? this.value
|
|
1942
1961
|
: this.value != null ? [this.value] : [];
|
|
1943
|
-
|
|
1962
|
+
// Only call normalizeItem with findByKey=true if the item doesn't have text
|
|
1963
|
+
// This prevents unnecessary API calls for items that already have text
|
|
1964
|
+
this.dataService.selectedItems = values.map(value => {
|
|
1965
|
+
const hasText = typeof value === 'object' && value !== null &&
|
|
1966
|
+
get(value, this.textField) != null;
|
|
1967
|
+
return this.normalizeItem(value, !hasText);
|
|
1968
|
+
});
|
|
1944
1969
|
}
|
|
1945
1970
|
// #endregion
|
|
1946
1971
|
// #region Public Selection Methods
|
|
@@ -2017,6 +2042,15 @@ class MXSelectionValueComponent extends MXValueComponent {
|
|
|
2017
2042
|
coerceBooleanProperty(get(itemRecord, this.disabledField)) === true ||
|
|
2018
2043
|
(this.disabledCallback?.({ item, index: -1 }) ?? false));
|
|
2019
2044
|
}
|
|
2045
|
+
/**
|
|
2046
|
+
* Checks if an item is currently loading
|
|
2047
|
+
* @param item Item to check
|
|
2048
|
+
* @returns True if the item is loading
|
|
2049
|
+
*/
|
|
2050
|
+
isItemLoading(item) {
|
|
2051
|
+
const normalizedItem = this.normalizeItem(item);
|
|
2052
|
+
return normalizedItem['isLoading'] === true;
|
|
2053
|
+
}
|
|
2020
2054
|
// #endregion
|
|
2021
2055
|
// #region Protected Utility Methods
|
|
2022
2056
|
/**
|
|
@@ -2025,26 +2059,108 @@ class MXSelectionValueComponent extends MXValueComponent {
|
|
|
2025
2059
|
* @returns Formatted display text
|
|
2026
2060
|
*/
|
|
2027
2061
|
getDisplayText(item) {
|
|
2028
|
-
|
|
2062
|
+
// Prevent recursive calls that could cause infinite loops
|
|
2063
|
+
if (item && typeof item === 'object' && item['_displayTextLoading']) {
|
|
2064
|
+
return 'Loading...';
|
|
2065
|
+
}
|
|
2066
|
+
// Add timestamp-based debouncing to prevent excessive calls for ALL item types
|
|
2067
|
+
const now = Date.now();
|
|
2068
|
+
const itemKey = typeof item === 'string' ? item : get(item, this.valueField);
|
|
2069
|
+
const cacheKey = this.createCacheKey(String(itemKey));
|
|
2070
|
+
// Check if we have a recent result in the cache to prevent duplicate processing
|
|
2071
|
+
const cachedResult = this.dataService.cacheList[cacheKey];
|
|
2072
|
+
if (cachedResult && cachedResult['_lastDisplayTextCall']) {
|
|
2073
|
+
const timeSinceLastCall = now - cachedResult['_lastDisplayTextCall'];
|
|
2074
|
+
if (timeSinceLastCall < 50) { // 50ms debounce
|
|
2075
|
+
return cachedResult['_lastDisplayTextResult'] || String(itemKey || '');
|
|
2076
|
+
}
|
|
2077
|
+
}
|
|
2078
|
+
// For string values, we need to normalize with findByKey=true to trigger async loading
|
|
2079
|
+
const isStringValue = typeof item === 'string';
|
|
2080
|
+
const normalizedItem = this.normalizeItem(item, isStringValue);
|
|
2081
|
+
// Check if this item is already in the cache with proper text
|
|
2082
|
+
const itemValue = get(normalizedItem, this.valueField);
|
|
2083
|
+
if (itemValue != null && itemValue !== '') {
|
|
2084
|
+
const cacheKey = this.createCacheKey(String(itemValue));
|
|
2085
|
+
const cachedItem = this.dataService.cacheList[cacheKey];
|
|
2086
|
+
if (cachedItem && get(cachedItem, this.textField) != null) {
|
|
2087
|
+
// Use cached text if available
|
|
2088
|
+
const cachedText = get(cachedItem, this.textField);
|
|
2089
|
+
if (cachedText && cachedText !== 'Loading...') {
|
|
2090
|
+
const result = String(cachedText);
|
|
2091
|
+
// Cache the timestamp and result for debouncing in the cache (works for all item types)
|
|
2092
|
+
if (cachedItem) {
|
|
2093
|
+
cachedItem['_lastDisplayTextCall'] = now;
|
|
2094
|
+
cachedItem['_lastDisplayTextResult'] = result;
|
|
2095
|
+
}
|
|
2096
|
+
return result;
|
|
2097
|
+
}
|
|
2098
|
+
}
|
|
2099
|
+
}
|
|
2029
2100
|
// Try template formatting first
|
|
2030
2101
|
if (this.textTemplate) {
|
|
2031
2102
|
const formattedTemplate = this.formatService.format(this.textTemplate, 'string', normalizedItem);
|
|
2032
2103
|
if (formattedTemplate !== this.textTemplate) {
|
|
2033
|
-
|
|
2104
|
+
const result = formattedTemplate;
|
|
2105
|
+
// Cache the timestamp and result for debouncing in the cache
|
|
2106
|
+
if (normalizedItem) {
|
|
2107
|
+
normalizedItem['_lastDisplayTextCall'] = now;
|
|
2108
|
+
normalizedItem['_lastDisplayTextResult'] = result;
|
|
2109
|
+
}
|
|
2110
|
+
return result;
|
|
2034
2111
|
}
|
|
2035
2112
|
}
|
|
2036
2113
|
// Use text field or fallback to value field
|
|
2037
2114
|
const textValue = get(normalizedItem, this.textField);
|
|
2038
2115
|
if (textValue != null && textValue !== '') {
|
|
2039
|
-
|
|
2116
|
+
const result = String(textValue);
|
|
2117
|
+
// Cache the timestamp and result for debouncing in the cache
|
|
2118
|
+
if (normalizedItem) {
|
|
2119
|
+
normalizedItem['_lastDisplayTextCall'] = now;
|
|
2120
|
+
normalizedItem['_lastDisplayTextResult'] = result;
|
|
2121
|
+
}
|
|
2122
|
+
return result;
|
|
2123
|
+
}
|
|
2124
|
+
// If text is missing and item is loading, show loading state
|
|
2125
|
+
if (normalizedItem['isLoading']) {
|
|
2126
|
+
const result = 'Loading...';
|
|
2127
|
+
// Cache the timestamp and result for debouncing in the cache
|
|
2128
|
+
if (normalizedItem) {
|
|
2129
|
+
normalizedItem['_lastDisplayTextCall'] = now;
|
|
2130
|
+
normalizedItem['_lastDisplayTextResult'] = result;
|
|
2131
|
+
}
|
|
2132
|
+
return result;
|
|
2040
2133
|
}
|
|
2041
2134
|
// Attempt to load item by key if text is missing
|
|
2042
2135
|
const value = get(normalizedItem, this.valueField);
|
|
2043
2136
|
if (value == null || value === '') {
|
|
2044
|
-
|
|
2137
|
+
const result = this.defaultText;
|
|
2138
|
+
// Cache the timestamp and result for debouncing in the cache
|
|
2139
|
+
if (normalizedItem) {
|
|
2140
|
+
normalizedItem['_lastDisplayTextCall'] = now;
|
|
2141
|
+
normalizedItem['_lastDisplayTextResult'] = result;
|
|
2142
|
+
}
|
|
2143
|
+
return result;
|
|
2045
2144
|
}
|
|
2046
|
-
|
|
2047
|
-
|
|
2145
|
+
// If we have a value but no text, trigger async loading only once
|
|
2146
|
+
// Use a flag to prevent multiple calls
|
|
2147
|
+
if (!normalizedItem['_loadingTriggered'] && !normalizedItem[this.textField]) {
|
|
2148
|
+
normalizedItem['_loadingTriggered'] = true;
|
|
2149
|
+
normalizedItem['_displayTextLoading'] = true;
|
|
2150
|
+
// Use setTimeout to defer the async loading and prevent immediate change detection
|
|
2151
|
+
setTimeout(() => {
|
|
2152
|
+
this.loadAndCacheItemByKey(value);
|
|
2153
|
+
}, 0);
|
|
2154
|
+
}
|
|
2155
|
+
// Return the value itself as a fallback
|
|
2156
|
+
// This will be updated once the async loading completes
|
|
2157
|
+
const result = String(value);
|
|
2158
|
+
// Cache the timestamp and result for debouncing in the cache
|
|
2159
|
+
if (normalizedItem) {
|
|
2160
|
+
normalizedItem['_lastDisplayTextCall'] = now;
|
|
2161
|
+
normalizedItem['_lastDisplayTextResult'] = result;
|
|
2162
|
+
}
|
|
2163
|
+
return result;
|
|
2048
2164
|
}
|
|
2049
2165
|
/**
|
|
2050
2166
|
* Gets the value of an item
|
|
@@ -2070,6 +2186,52 @@ class MXSelectionValueComponent extends MXValueComponent {
|
|
|
2070
2186
|
this.dataService.cacheList = {};
|
|
2071
2187
|
this.cdr.markForCheck();
|
|
2072
2188
|
}
|
|
2189
|
+
/**
|
|
2190
|
+
* Clears the debouncing cache for specific items to ensure fresh data is displayed
|
|
2191
|
+
*/
|
|
2192
|
+
clearItemDebouncingCache(item) {
|
|
2193
|
+
// Get the cache key for this item
|
|
2194
|
+
const itemKey = typeof item === 'string' ? item : get(item, this.valueField);
|
|
2195
|
+
if (itemKey != null) {
|
|
2196
|
+
const cacheKey = this.createCacheKey(String(itemKey));
|
|
2197
|
+
const cachedItem = this.dataService.cacheList[cacheKey];
|
|
2198
|
+
if (cachedItem) {
|
|
2199
|
+
delete cachedItem['_lastDisplayTextCall'];
|
|
2200
|
+
delete cachedItem['_lastDisplayTextResult'];
|
|
2201
|
+
delete cachedItem['_displayTextLoading'];
|
|
2202
|
+
delete cachedItem['_loadingTriggered'];
|
|
2203
|
+
}
|
|
2204
|
+
}
|
|
2205
|
+
}
|
|
2206
|
+
/**
|
|
2207
|
+
* Forces a refresh of the display when items are loaded asynchronously
|
|
2208
|
+
* This should be called when the data source is updated
|
|
2209
|
+
*/
|
|
2210
|
+
refreshDisplay() {
|
|
2211
|
+
this.cdr.markForCheck();
|
|
2212
|
+
}
|
|
2213
|
+
/**
|
|
2214
|
+
* Refreshes the display for all selected items to ensure consistency
|
|
2215
|
+
* This is useful when the data source has been updated
|
|
2216
|
+
*/
|
|
2217
|
+
refreshSelectedItemsDisplay() {
|
|
2218
|
+
// Force a refresh of the display without re-normalizing to avoid infinite loops
|
|
2219
|
+
if (this.selectedItems.length > 0) {
|
|
2220
|
+
this.cdr.markForCheck();
|
|
2221
|
+
}
|
|
2222
|
+
}
|
|
2223
|
+
/**
|
|
2224
|
+
* Forces a refresh of all selected items by clearing their debouncing cache
|
|
2225
|
+
* This ensures fresh data is displayed when the data source is updated
|
|
2226
|
+
*/
|
|
2227
|
+
forceRefreshSelectedItems() {
|
|
2228
|
+
if (this.selectedItems.length > 0) {
|
|
2229
|
+
this.selectedItems.forEach(item => {
|
|
2230
|
+
this.clearItemDebouncingCache(item);
|
|
2231
|
+
});
|
|
2232
|
+
this.cdr.markForCheck();
|
|
2233
|
+
}
|
|
2234
|
+
}
|
|
2073
2235
|
// #endregion
|
|
2074
2236
|
// #region Private Helper Methods
|
|
2075
2237
|
/**
|
|
@@ -2082,12 +2244,29 @@ class MXSelectionValueComponent extends MXValueComponent {
|
|
|
2082
2244
|
if (item) {
|
|
2083
2245
|
const normalizedItem = this.normalizeItem(item);
|
|
2084
2246
|
const cacheKey = this.createCacheKey(String(normalizedItem[this.valueField]));
|
|
2247
|
+
// Update the cache with the loaded item
|
|
2085
2248
|
this.dataService.cacheList[cacheKey] = normalizedItem;
|
|
2086
|
-
|
|
2249
|
+
// Clear loading flags
|
|
2250
|
+
if (this.dataService.cacheList[cacheKey]) {
|
|
2251
|
+
delete this.dataService.cacheList[cacheKey]['_displayTextLoading'];
|
|
2252
|
+
delete this.dataService.cacheList[cacheKey]['_loadingTriggered'];
|
|
2253
|
+
}
|
|
2254
|
+
// Use setTimeout to defer change detection and prevent immediate re-rendering
|
|
2255
|
+
setTimeout(() => {
|
|
2256
|
+
if (this.cdr) {
|
|
2257
|
+
this.cdr.markForCheck();
|
|
2258
|
+
}
|
|
2259
|
+
}, 0);
|
|
2087
2260
|
}
|
|
2088
2261
|
}
|
|
2089
2262
|
catch (error) {
|
|
2090
2263
|
console.warn('Failed to load item by key:', key, error);
|
|
2264
|
+
// Clear loading flags on error too
|
|
2265
|
+
const cacheKey = this.createCacheKey(String(key));
|
|
2266
|
+
if (this.dataService.cacheList[cacheKey]) {
|
|
2267
|
+
delete this.dataService.cacheList[cacheKey]['_displayTextLoading'];
|
|
2268
|
+
delete this.dataService.cacheList[cacheKey]['_loadingTriggered'];
|
|
2269
|
+
}
|
|
2091
2270
|
}
|
|
2092
2271
|
}
|
|
2093
2272
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.8", ngImport: i0, type: MXSelectionValueComponent, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
|