@sumaris-net/ngx-components 2.12.14 → 2.12.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.
@@ -1,7 +1,7 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { InjectionToken, Directive, Pipe, Injectable, EventEmitter, Output, Optional, Inject, NgModule, forwardRef, Component, ChangeDetectionStrategy, Input, ViewChildren, HostBinding, HostListener, ElementRef, booleanAttribute, numberAttribute, ViewChild, ANIMATION_MODULE_TYPE, RendererStyleFlags2, CUSTOM_ELEMENTS_SCHEMA, inject, ViewEncapsulation, APP_INITIALIZER, ChangeDetectorRef } from '@angular/core';
3
- import { firstValueFrom, shareReplay, tap, of, Subject, merge, delay, isObservable, from, Subscription, timer, BehaviorSubject, fromEvent, noop as noop$a, Observable, forkJoin, defer, timeout, debounceTime as debounceTime$1, distinctUntilChanged as distinctUntilChanged$1, fromEventPattern, interval, combineLatest, mergeMap as mergeMap$1, EMPTY, switchMap as switchMap$1 } from 'rxjs';
4
- import { catchError, map, first, tap as tap$1, switchMap, takeUntil, filter, debounceTime, startWith, distinctUntilChanged, mergeMap, skip, combineLatestWith, finalize, throttleTime, bufferWhen, mapTo, distinctUntilKeyChanged, take } from 'rxjs/operators';
3
+ import { firstValueFrom, shareReplay, tap, of, timer, Subject, merge, delay, isObservable, from, Subscription, BehaviorSubject, fromEvent, noop as noop$a, Observable, forkJoin, defer, timeout, debounceTime as debounceTime$1, distinctUntilChanged as distinctUntilChanged$1, fromEventPattern, interval, combineLatest, mergeMap as mergeMap$1, EMPTY, switchMap as switchMap$1 } from 'rxjs';
4
+ import { catchError, map, filter, takeUntil, first, switchMap, tap as tap$1, debounceTime, startWith, distinctUntilChanged, mergeMap, skip, combineLatestWith, finalize, throttleTime, bufferWhen, mapTo, distinctUntilKeyChanged, take } from 'rxjs/operators';
5
5
  import { setTimeout as setTimeout$1 } from '@rx-angular/cdk/zone-less/browser';
6
6
  import * as i3$1 from '@angular/common';
7
7
  import { CommonModule, DOCUMENT, Location } from '@angular/common';
@@ -35,7 +35,7 @@ import * as i1 from '@angular/material-moment-adapter';
35
35
  import { MatMomentDateModule } from '@angular/material-moment-adapter';
36
36
  import * as momentTZImported from 'moment-timezone';
37
37
  import * as i1$2 from '@angular/forms';
38
- import { AbstractControl, UntypedFormGroup, UntypedFormArray, UntypedFormControl, NG_VALUE_ACCESSOR, Validators, ReactiveFormsModule, FormControl, FormGroup, FormsModule } from '@angular/forms';
38
+ import { AbstractControl, UntypedFormArray, UntypedFormGroup, UntypedFormControl, NG_VALUE_ACCESSOR, Validators, ReactiveFormsModule, FormControl, FormGroup, FormsModule } from '@angular/forms';
39
39
  import { maskitoWithPlaceholder, maskitoEventHandler, maskitoDateOptionsGenerator, maskitoTimeOptionsGenerator } from '@maskito/kit';
40
40
  import * as i12$1 from 'ngx-material-timepicker';
41
41
  import { NgxMaterialTimepickerModule } from 'ngx-material-timepicker';
@@ -2004,6 +2004,337 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
2004
2004
  type: Output
2005
2005
  }] } });
2006
2006
 
2007
+ function isFocusableElement(object) {
2008
+ if (!object)
2009
+ return false;
2010
+ return 'focus' in object;
2011
+ }
2012
+
2013
+ function selectInputContent(event) {
2014
+ if (event.defaultPrevented)
2015
+ return false;
2016
+ const input = event.target;
2017
+ if (!input)
2018
+ return true;
2019
+ // Nothing to select
2020
+ if (isNilOrBlank(input.value))
2021
+ return false;
2022
+ if (typeof input.selectRange === 'function') {
2023
+ try {
2024
+ input.selectRange(input.value.length, 0);
2025
+ return true;
2026
+ }
2027
+ catch (err) {
2028
+ console.error('Could not select input content, using selectRange()', err);
2029
+ return false;
2030
+ }
2031
+ }
2032
+ if (input && typeof input.select === 'function') {
2033
+ try {
2034
+ input.select();
2035
+ return true;
2036
+ }
2037
+ catch (err) {
2038
+ console.error('Could not select input content, using select()', err);
2039
+ return false;
2040
+ }
2041
+ }
2042
+ return true;
2043
+ }
2044
+ function selectInputRange(input, startIndex, endIndex) {
2045
+ if (input && typeof input.setSelectionRange === 'function') {
2046
+ // No content
2047
+ if (isNilOrBlank(input.value))
2048
+ return false;
2049
+ try {
2050
+ input.setSelectionRange(startIndex, isNotNil(endIndex) ? endIndex : startIndex);
2051
+ }
2052
+ catch (err) {
2053
+ console.error('Could not select input range', err);
2054
+ return false;
2055
+ }
2056
+ }
2057
+ return true;
2058
+ }
2059
+ function getCaretPosition(input) {
2060
+ if (input && input.selectionStart != null) {
2061
+ return input.selectionDirection ? (input.selectionDirection === 'backward' ? input.selectionStart : input.selectionEnd) : input.selectionStart;
2062
+ }
2063
+ return -1;
2064
+ }
2065
+ function moveInputCaretToSeparator(event, separator, forward) {
2066
+ if (event.defaultPrevented || !separator)
2067
+ return false;
2068
+ const input = event.target;
2069
+ if (!input)
2070
+ return true;
2071
+ const caretPosition = getCaretPosition(input);
2072
+ // DEBUG
2073
+ //console.debug('caretPosition=', caretPosition);
2074
+ if (caretPosition === -1)
2075
+ return true; // Caret pos not found: skip
2076
+ // Get input value
2077
+ const value = input.value;
2078
+ // No content: skip
2079
+ if (isNilOrBlank(value))
2080
+ return false;
2081
+ try {
2082
+ if (value && caretPosition <= value.length) {
2083
+ // DEBUG
2084
+ //console.debug("Input text value: ", value);
2085
+ //console.debug("Cursor at: ", caretPosition);
2086
+ //console.debug("Text after cursor: ", value.substr(caretPosition));
2087
+ //console.debug("Next separator at: ", value.indexOf(separator, caretPosition));
2088
+ forward = forward !== false;
2089
+ const separatorIndex = forward ? value.indexOf(separator, caretPosition) : value.lastIndexOf(separator, caretPosition);
2090
+ if (separatorIndex !== -1 && ((forward && separatorIndex + 1 < value.length) || (!forward && separatorIndex > 0))) {
2091
+ if (input.setSelectionRange) {
2092
+ // Move after the next separator
2093
+ if (selectInputRange(input, separatorIndex + (forward ? 1 : -1))) {
2094
+ // Stop the keyboard event
2095
+ event.preventDefault();
2096
+ event.stopPropagation();
2097
+ }
2098
+ }
2099
+ }
2100
+ }
2101
+ }
2102
+ catch (err) {
2103
+ console.error('Could not move caret to next separator', err);
2104
+ return false;
2105
+ }
2106
+ return true;
2107
+ }
2108
+ function filterNumberInput(event, allowDecimals, decimalSeparator) {
2109
+ //input number entered or one of the 4 direction up, down, left and right
2110
+ if ((event.which >= 48 && event.which <= 57) || (event.which >= 37 && event.which <= 40)) {
2111
+ //console.debug('input number entered :' + event.which + ' ' + event.keyCode + ' ' + event.charCode);
2112
+ // OK
2113
+ }
2114
+ // Decimal separator
2115
+ else if (allowDecimals &&
2116
+ ((!decimalSeparator && (event.key === '.' || event.key === ',')) || (decimalSeparator && event.key === decimalSeparator))) {
2117
+ //console.debug('input decimal separator entered :' + event.code);
2118
+ // OK
2119
+ }
2120
+ else {
2121
+ //input command entered of delete, backspace or one of the 4 direction up, down, left and right, or negative sign
2122
+ if ((event.keyCode >= 37 && event.keyCode <= 40) || event.keyCode == 46 || event.which == 8 || event.keyCode == 9 || event.keyCode == 45) {
2123
+ //console.debug('input command entered :' + event.which + ' ' + event.keyCode + ' ' + event.charCode);
2124
+ // OK
2125
+ }
2126
+ // Cancel other keyboard events
2127
+ else {
2128
+ //console.debug('input not number entered :' + event.which + ' ' + event.keyCode + ' ' + event.charCode + ' ' + event.code );
2129
+ event.preventDefault();
2130
+ }
2131
+ }
2132
+ }
2133
+ function focusInput(element) {
2134
+ const inputElement = asInputElement(element);
2135
+ if (inputElement)
2136
+ inputElement.focus();
2137
+ else {
2138
+ console.warn('Trying to focus on this element:', element);
2139
+ }
2140
+ }
2141
+ function setTabIndex(element, tabIndex) {
2142
+ if (isInputElement(element)) {
2143
+ element.tabindex = tabIndex;
2144
+ }
2145
+ else if (element && isInputElement(element.nativeElement)) {
2146
+ element.nativeElement.tabIndex = tabIndex;
2147
+ }
2148
+ else {
2149
+ console.warn('Trying to change tabindex on this element:', element);
2150
+ }
2151
+ }
2152
+ function isInputElement(object) {
2153
+ return (isFocusableElement(object) &&
2154
+ ('value' in object ||
2155
+ // has value is not always set (neither tabindex) check on 2 properties with a logical OR
2156
+ 'tabindex' in object ||
2157
+ 'tabIndex' in object));
2158
+ }
2159
+ function asInputElement(object) {
2160
+ if (object) {
2161
+ if (isInputElement(object))
2162
+ return object;
2163
+ if (object.nativeElement && isInputElement(object.nativeElement))
2164
+ return object.nativeElement;
2165
+ }
2166
+ return undefined;
2167
+ }
2168
+ function tabindexComparator(a, b) {
2169
+ const valueA = a.tabindex || a.tabIndex;
2170
+ const valueB = b.tabindex || b.tabIndex;
2171
+ return valueA === valueB ? 0 : valueA > valueB ? 1 : -1;
2172
+ }
2173
+ function canHaveFocus(input, opts) {
2174
+ if (!input)
2175
+ return false;
2176
+ // Exclude disabled element
2177
+ return (!toBoolean(input.disabled, false) &&
2178
+ // Exclude hidden element
2179
+ !toBoolean(input.hidden, false) &&
2180
+ // Exclude minTabIndex < element.tabIndex
2181
+ (isNil(opts.minTabindex) || toNumber(input.tabIndex, input.tabindex) > opts.minTabindex) &&
2182
+ // Exclude maxTabIndex > element.tabIndex
2183
+ (isNil(opts.maxTabindex) || toNumber(input.tabIndex, input.tabindex) < opts.maxTabindex) &&
2184
+ // Exclude nil input value
2185
+ (!opts.excludeEmptyInput || isNilOrBlank(input.value)));
2186
+ }
2187
+ function getFocusableInputElements(elements, opts) {
2188
+ opts = { sortByTabIndex: false, excludeEmptyInput: false, ...opts };
2189
+ // Focus to first input
2190
+ const filteredElements = elements
2191
+ // Transform to input
2192
+ .map(asInputElement)
2193
+ .filter((input) => {
2194
+ const included = canHaveFocus(input, opts);
2195
+ // DEBUG
2196
+ if (input && opts.debug)
2197
+ console.debug(`[inputs] Focusable input {canFocus: ${included}, tabIndex: ${input.tabIndex || input.tabindex}}`, input);
2198
+ return included;
2199
+ });
2200
+ // Sort by tabIndex
2201
+ if (opts.sortByTabIndex) {
2202
+ return filteredElements.sort(tabindexComparator);
2203
+ }
2204
+ return filteredElements;
2205
+ }
2206
+ function focusNextInput(event, elements, opts) {
2207
+ // Cancelling event (e.g. when emitted by (keydown.tab) )
2208
+ if (event) {
2209
+ event.preventDefault();
2210
+ event.stopPropagation();
2211
+ }
2212
+ // Get current index
2213
+ const minTabindex = event && isInputElement(event.target) ? event.target.tabIndex || event.target.tabindex : undefined;
2214
+ // Get focusable input elements
2215
+ const focusableInputs = getFocusableInputElements(elements, { minTabindex, ...opts });
2216
+ if (isNotEmptyArray(focusableInputs)) {
2217
+ // Focus on first inputs
2218
+ focusableInputs[0].focus();
2219
+ return true;
2220
+ }
2221
+ return false;
2222
+ }
2223
+ function focusPreviousInput(event, elements, opts) {
2224
+ // Cancelling event (e.g. when emitted by (keydown.tab) )
2225
+ if (event) {
2226
+ event.preventDefault();
2227
+ event.stopPropagation();
2228
+ }
2229
+ // Get current index
2230
+ const maxTabindex = event && isInputElement(event.target) ? event.target.tabIndex || event.target.tabindex : undefined;
2231
+ // Get focusable input elements
2232
+ const focusableInputs = getFocusableInputElements(elements, { maxTabindex, ...opts });
2233
+ if (isNotEmptyArray(focusableInputs)) {
2234
+ // Focus on last inputs
2235
+ focusableInputs[focusableInputs.length - 1].focus();
2236
+ return true;
2237
+ }
2238
+ return false;
2239
+ }
2240
+
2241
+ function filterNotNil(obs) {
2242
+ return obs.pipe(filter(isNotNil));
2243
+ }
2244
+ function filterTrue(obs) {
2245
+ return obs.pipe(filter((v) => v === true));
2246
+ }
2247
+ function filterFalse(obs, opts) {
2248
+ return obs.pipe(filter((v) => v === false));
2249
+ }
2250
+ function decorateWithTakeUntil(obs, opts) {
2251
+ // Set take until notifier
2252
+ let takeUntil$ = opts?.stop || (opts?.timeout && timer(opts.timeout));
2253
+ if (!takeUntil$)
2254
+ return obs; // Skip
2255
+ // When stop, throw an error (useful when used with a toPromise())
2256
+ if (opts.stopError) {
2257
+ let error;
2258
+ if (opts.stopError === true) {
2259
+ error = new Error(opts.timeout ? `Timeout ${opts.timeout}ms` : `stop`);
2260
+ }
2261
+ else if (typeof opts.stopError === 'string') {
2262
+ error = new Error(opts.stopError);
2263
+ }
2264
+ else {
2265
+ error = opts.stopError;
2266
+ }
2267
+ takeUntil$ = takeUntil$.pipe(map(() => {
2268
+ throw error;
2269
+ }));
2270
+ }
2271
+ return obs.pipe(takeUntil(takeUntil$));
2272
+ }
2273
+ function firstNotNil(obs, opts) {
2274
+ return decorateWithTakeUntil(obs.pipe(first(isNotNil)), opts);
2275
+ }
2276
+ function firstTrue(obs, opts) {
2277
+ return decorateWithTakeUntil(obs.pipe(first((v) => v === true), map((_) => { }) // Convert to void
2278
+ ), opts);
2279
+ }
2280
+ function firstFalse(obs, opts) {
2281
+ return decorateWithTakeUntil(obs.pipe(first((v) => v === false), map((_) => { }) // Convert to void
2282
+ ), opts);
2283
+ }
2284
+ function firstTruePromise(obs, opts) {
2285
+ return firstTrue(obs, { stopError: true, ...opts }).toPromise();
2286
+ }
2287
+ function firstFalsePromise(obs, opts) {
2288
+ return firstFalse(obs, { stopError: true, ...opts }).toPromise();
2289
+ }
2290
+ function firstNotNilPromise(obs, opts) {
2291
+ return firstNotNil(obs, { stopError: true, ...opts }).toPromise();
2292
+ }
2293
+ function chainPromises(defers) {
2294
+ return (defers || []).reduce((previous, defer) => {
2295
+ // First job
2296
+ if (!previous) {
2297
+ return (defer()
2298
+ // Init the final result array, with the first result
2299
+ .then((jobRes) => [jobRes]));
2300
+ }
2301
+ // Other jobs
2302
+ return previous.then((finalResult) => defer()
2303
+ // Add job result to final result array
2304
+ .then((jobRes) => finalResult.concat(jobRes)));
2305
+ }, null);
2306
+ }
2307
+ /**
2308
+ * Wait form a predicate return true. This need to implement AppFormUtils.waitWhilePending(), AppFormUtils.waitIdle()
2309
+ *
2310
+ * @param predicate
2311
+ * @param opts
2312
+ */
2313
+ async function waitFor(predicate, opts) {
2314
+ if (predicate())
2315
+ return Promise.resolve();
2316
+ const period = (opts && opts.checkPeriod) || 300;
2317
+ const dueTime = (opts && opts.dueTime) || period;
2318
+ const wait$ = timer(dueTime, period).pipe(
2319
+ // For DEBUG :
2320
+ //tap(() => console.debug("Waiting form idle...", form)),
2321
+ filter((_) => predicate()), map((_) => true));
2322
+ await firstNotNilPromise(wait$, opts);
2323
+ }
2324
+ function waitForTrue(observable, opts) {
2325
+ opts = { stopError: true, ...opts };
2326
+ // dueTime (without timeout)
2327
+ if (opts && opts.dueTime > 0) {
2328
+ observable = timer(opts.dueTime).pipe(switchMap(() => observable));
2329
+ }
2330
+ return firstTrue(observable, opts).toPromise();
2331
+ }
2332
+ function waitForFalse(observable, opts) {
2333
+ return waitForTrue(
2334
+ // Inverse the logic
2335
+ observable.pipe(map((v) => v === false)), opts);
2336
+ }
2337
+
2007
2338
  // @dynamic
2008
2339
  class SharedValidators {
2009
2340
  static _DOUBLE_REGEXP_CACHE = {
@@ -2584,335 +2915,324 @@ class SharedAsyncValidators {
2584
2915
  }
2585
2916
  }
2586
2917
 
2587
- function isFocusableElement(object) {
2588
- if (!object)
2589
- return false;
2590
- return 'focus' in object;
2591
- }
2592
-
2593
- function selectInputContent(event) {
2594
- if (event.defaultPrevented)
2595
- return false;
2596
- const input = event.target;
2597
- if (!input)
2598
- return true;
2599
- // Nothing to select
2600
- if (isNilOrBlank(input.value))
2601
- return false;
2602
- if (typeof input.selectRange === 'function') {
2603
- try {
2604
- input.selectRange(input.value.length, 0);
2605
- return true;
2606
- }
2607
- catch (err) {
2608
- console.error('Could not select input content, using selectRange()', err);
2609
- return false;
2918
+ class FormArrayHelper {
2919
+ _formArray;
2920
+ createControl;
2921
+ equals;
2922
+ isEmpty;
2923
+ static getOrCreateArray(formBuilder, form, arrayName) {
2924
+ const disabled = form.disabled;
2925
+ let arrayControl = form.get(arrayName);
2926
+ if (!arrayControl) {
2927
+ arrayControl = formBuilder.array([]);
2928
+ // Apply parent disabled state, before to push it into the array
2929
+ // This is need to avoid parent form to be enabled, after calling resizeArray()
2930
+ if (disabled && arrayControl.enabled)
2931
+ arrayControl.disable({ emitEvent: false });
2932
+ form.addControl(arrayName, arrayControl);
2610
2933
  }
2934
+ return arrayControl;
2611
2935
  }
2612
- if (input && typeof input.select === 'function') {
2613
- try {
2614
- input.select();
2615
- return true;
2616
- }
2617
- catch (err) {
2618
- console.error('Could not select input content, using select()', err);
2619
- return false;
2620
- }
2936
+ _allowEmptyArray;
2937
+ _allowManyNullValues;
2938
+ _allowDuplicatedValues;
2939
+ _validators;
2940
+ get allowEmptyArray() {
2941
+ return this._allowEmptyArray;
2621
2942
  }
2622
- return true;
2623
- }
2624
- function selectInputRange(input, startIndex, endIndex) {
2625
- if (input && typeof input.setSelectionRange === 'function') {
2626
- // No content
2627
- if (isNilOrBlank(input.value))
2628
- return false;
2629
- try {
2630
- input.setSelectionRange(startIndex, isNotNil(endIndex) ? endIndex : startIndex);
2943
+ set allowEmptyArray(value) {
2944
+ this.setAllowEmptyArray(value);
2945
+ }
2946
+ get allowManyNullValues() {
2947
+ return this._allowManyNullValues;
2948
+ }
2949
+ set allowManyNullValues(value) {
2950
+ this._allowManyNullValues = value;
2951
+ }
2952
+ get allowDuplicatedValues() {
2953
+ return this._allowDuplicatedValues;
2954
+ }
2955
+ set allowDuplicatedValues(value) {
2956
+ this._allowDuplicatedValues = value;
2957
+ }
2958
+ get formArray() {
2959
+ return this._formArray;
2960
+ }
2961
+ constructor(_formArray, createControl, equals, isEmpty, options) {
2962
+ this._formArray = _formArray;
2963
+ this.createControl = createControl;
2964
+ this.equals = equals;
2965
+ this.isEmpty = isEmpty;
2966
+ this._validators = options && options.validators;
2967
+ // empty array not allow by default
2968
+ this.setAllowEmptyArray(toBoolean(options?.allowEmptyArray, false));
2969
+ this._allowManyNullValues = toBoolean(options?.allowManyNullValues, false);
2970
+ this._allowDuplicatedValues = toBoolean(options?.allowDuplicatedValues, false);
2971
+ }
2972
+ /**
2973
+ * @param value
2974
+ * @param options
2975
+ */
2976
+ add(value, options) {
2977
+ return addValueInArray(this._formArray, this.createControl, this.equals, this.isEmpty, value, {
2978
+ allowManyNullValues: this.allowManyNullValues,
2979
+ allowDuplicateValue: this.allowDuplicatedValues,
2980
+ ...options,
2981
+ });
2982
+ }
2983
+ removeAt(index) {
2984
+ // Do not remove if last criterion
2985
+ if (!this._allowEmptyArray && this._formArray.length === 1) {
2986
+ return clearValueInArray(this._formArray, this.isEmpty, index);
2631
2987
  }
2632
- catch (err) {
2633
- console.error('Could not select input range', err);
2634
- return false;
2988
+ else {
2989
+ return removeValueInArray(this._formArray, this.isEmpty, index);
2635
2990
  }
2636
2991
  }
2637
- return true;
2638
- }
2639
- function getCaretPosition(input) {
2640
- if (input && input.selectionStart != null) {
2641
- return input.selectionDirection ? (input.selectionDirection === 'backward' ? input.selectionStart : input.selectionEnd) : input.selectionStart;
2992
+ resize(length, options) {
2993
+ return resizeArray(this._formArray, this.createControl, length, options);
2642
2994
  }
2643
- return -1;
2644
- }
2645
- function moveInputCaretToSeparator(event, separator, forward) {
2646
- if (event.defaultPrevented || !separator)
2647
- return false;
2648
- const input = event.target;
2649
- if (!input)
2650
- return true;
2651
- const caretPosition = getCaretPosition(input);
2652
- // DEBUG
2653
- //console.debug('caretPosition=', caretPosition);
2654
- if (caretPosition === -1)
2655
- return true; // Caret pos not found: skip
2656
- // Get input value
2657
- const value = input.value;
2658
- // No content: skip
2659
- if (isNilOrBlank(value))
2660
- return false;
2661
- try {
2662
- if (value && caretPosition <= value.length) {
2663
- // DEBUG
2664
- //console.debug("Input text value: ", value);
2665
- //console.debug("Cursor at: ", caretPosition);
2666
- //console.debug("Text after cursor: ", value.substr(caretPosition));
2667
- //console.debug("Next separator at: ", value.indexOf(separator, caretPosition));
2668
- forward = forward !== false;
2669
- const separatorIndex = forward ? value.indexOf(separator, caretPosition) : value.lastIndexOf(separator, caretPosition);
2670
- if (separatorIndex !== -1 && ((forward && separatorIndex + 1 < value.length) || (!forward && separatorIndex > 0))) {
2671
- if (input.setSelectionRange) {
2672
- // Move after the next separator
2673
- if (selectInputRange(input, separatorIndex + (forward ? 1 : -1))) {
2674
- // Stop the keyboard event
2675
- event.preventDefault();
2676
- event.stopPropagation();
2677
- }
2678
- }
2679
- }
2995
+ clearAt(index) {
2996
+ return clearValueInArray(this._formArray, this.isEmpty, index);
2997
+ }
2998
+ isLast(index) {
2999
+ return this._formArray.length - 1 === index;
3000
+ }
3001
+ removeAllEmpty() {
3002
+ let index = this._formArray.controls.findIndex((c) => this.isEmpty(c.value));
3003
+ while (index !== -1) {
3004
+ this.removeAt(index);
3005
+ index = this._formArray.controls.findIndex((c) => this.isEmpty(c.value));
2680
3006
  }
2681
3007
  }
2682
- catch (err) {
2683
- console.error('Could not move caret to next separator', err);
2684
- return false;
3008
+ size() {
3009
+ return this._formArray.length;
2685
3010
  }
2686
- return true;
2687
- }
2688
- function filterNumberInput(event, allowDecimals, decimalSeparator) {
2689
- //input number entered or one of the 4 direction up, down, left and right
2690
- if ((event.which >= 48 && event.which <= 57) || (event.which >= 37 && event.which <= 40)) {
2691
- //console.debug('input number entered :' + event.which + ' ' + event.keyCode + ' ' + event.charCode);
2692
- // OK
3011
+ at(index) {
3012
+ return this._formArray.at(index);
2693
3013
  }
2694
- // Decimal separator
2695
- else if (allowDecimals &&
2696
- ((!decimalSeparator && (event.key === '.' || event.key === ',')) || (decimalSeparator && event.key === decimalSeparator))) {
2697
- //console.debug('input decimal separator entered :' + event.code);
2698
- // OK
3014
+ /**
3015
+ * Resize the FormArray, then set values
3016
+ *
3017
+ * @param values
3018
+ * @param options
3019
+ */
3020
+ setValue(values, options) {
3021
+ this.resize(values?.length || 0);
3022
+ this._formArray.setValue(values, options);
2699
3023
  }
2700
- else {
2701
- //input command entered of delete, backspace or one of the 4 direction up, down, left and right, or negative sign
2702
- if ((event.keyCode >= 37 && event.keyCode <= 40) || event.keyCode == 46 || event.which == 8 || event.keyCode == 9 || event.keyCode == 45) {
2703
- //console.debug('input command entered :' + event.which + ' ' + event.keyCode + ' ' + event.charCode);
2704
- // OK
3024
+ /**
3025
+ * Resize the FormArray, then patch values
3026
+ *
3027
+ * @param values
3028
+ * @param options
3029
+ */
3030
+ patchValue(values, options) {
3031
+ this.resize(values?.length || 0);
3032
+ this._formArray.patchValue(values, options);
3033
+ }
3034
+ disable(opts) {
3035
+ this._formArray.controls.forEach((c) => c.disable(opts));
3036
+ }
3037
+ enable(opts) {
3038
+ this._formArray.controls.forEach((c) => c.enable(opts));
3039
+ }
3040
+ forEach(ite) {
3041
+ const size = this.size();
3042
+ for (let i = 0; i < size; i++) {
3043
+ const control = this._formArray.at(i);
3044
+ if (control)
3045
+ ite(control);
3046
+ }
3047
+ }
3048
+ /* -- internal methods -- */
3049
+ setAllowEmptyArray(value) {
3050
+ if (this._allowEmptyArray === value)
3051
+ return; // Skip if same
3052
+ this._allowEmptyArray = value;
3053
+ // Set required (or reste) min length validator
3054
+ if (this._allowEmptyArray) {
3055
+ this._formArray.setValidators(this._validators || null);
2705
3056
  }
2706
- // Cancel other keyboard events
2707
3057
  else {
2708
- //console.debug('input not number entered :' + event.which + ' ' + event.keyCode + ' ' + event.charCode + ' ' + event.code );
2709
- event.preventDefault();
3058
+ this._formArray.setValidators((this._validators || []).concat(SharedFormArrayValidators.requiredArrayMinLength(1)));
2710
3059
  }
2711
3060
  }
2712
3061
  }
2713
- function focusInput(element) {
2714
- const inputElement = asInputElement(element);
2715
- if (inputElement)
2716
- inputElement.focus();
2717
- else {
2718
- console.warn('Trying to focus on this element:', element);
3062
+ class AppFormArray extends UntypedFormArray {
3063
+ createControl;
3064
+ equals;
3065
+ isEmpty;
3066
+ options;
3067
+ get allowEmptyArray() {
3068
+ return this.options.allowEmptyArray;
2719
3069
  }
2720
- }
2721
- function setTabIndex(element, tabIndex) {
2722
- if (isInputElement(element)) {
2723
- element.tabindex = tabIndex;
3070
+ set allowEmptyArray(value) {
3071
+ this.setAllowEmptyArray(value);
2724
3072
  }
2725
- else if (element && isInputElement(element.nativeElement)) {
2726
- element.nativeElement.tabIndex = tabIndex;
3073
+ get allowManyNullValues() {
3074
+ return this.options.allowManyNullValues;
2727
3075
  }
2728
- else {
2729
- console.warn('Trying to change tabindex on this element:', element);
3076
+ set allowManyNullValues(value) {
3077
+ this.options.allowManyNullValues = value;
2730
3078
  }
2731
- }
2732
- function isInputElement(object) {
2733
- return (isFocusableElement(object) &&
2734
- ('value' in object ||
2735
- // has value is not always set (neither tabindex) check on 2 properties with a logical OR
2736
- 'tabindex' in object ||
2737
- 'tabIndex' in object));
2738
- }
2739
- function asInputElement(object) {
2740
- if (object) {
2741
- if (isInputElement(object))
2742
- return object;
2743
- if (object.nativeElement && isInputElement(object.nativeElement))
2744
- return object.nativeElement;
3079
+ get allowDuplicateValue() {
3080
+ return this.options.allowDuplicateValue;
2745
3081
  }
2746
- return undefined;
2747
- }
2748
- function tabindexComparator(a, b) {
2749
- const valueA = a.tabindex || a.tabIndex;
2750
- const valueB = b.tabindex || b.tabIndex;
2751
- return valueA === valueB ? 0 : valueA > valueB ? 1 : -1;
2752
- }
2753
- function canHaveFocus(input, opts) {
2754
- if (!input)
2755
- return false;
2756
- // Exclude disabled element
2757
- return (!toBoolean(input.disabled, false) &&
2758
- // Exclude hidden element
2759
- !toBoolean(input.hidden, false) &&
2760
- // Exclude minTabIndex < element.tabIndex
2761
- (isNil(opts.minTabindex) || toNumber(input.tabIndex, input.tabindex) > opts.minTabindex) &&
2762
- // Exclude maxTabIndex > element.tabIndex
2763
- (isNil(opts.maxTabindex) || toNumber(input.tabIndex, input.tabindex) < opts.maxTabindex) &&
2764
- // Exclude nil input value
2765
- (!opts.excludeEmptyInput || isNilOrBlank(input.value)));
2766
- }
2767
- function getFocusableInputElements(elements, opts) {
2768
- opts = { sortByTabIndex: false, excludeEmptyInput: false, ...opts };
2769
- // Focus to first input
2770
- const filteredElements = elements
2771
- // Transform to input
2772
- .map(asInputElement)
2773
- .filter((input) => {
2774
- const included = canHaveFocus(input, opts);
2775
- // DEBUG
2776
- if (input && opts.debug)
2777
- console.debug(`[inputs] Focusable input {canFocus: ${included}, tabIndex: ${input.tabIndex || input.tabindex}}`, input);
2778
- return included;
2779
- });
2780
- // Sort by tabIndex
2781
- if (opts.sortByTabIndex) {
2782
- return filteredElements.sort(tabindexComparator);
3082
+ set allowDuplicateValue(value) {
3083
+ this.options.allowDuplicateValue = value;
2783
3084
  }
2784
- return filteredElements;
2785
- }
2786
- function focusNextInput(event, elements, opts) {
2787
- // Cancelling event (e.g. when emitted by (keydown.tab) )
2788
- if (event) {
2789
- event.preventDefault();
2790
- event.stopPropagation();
3085
+ constructor(createControl, equals, isEmpty, options) {
3086
+ super([], options);
3087
+ this.createControl = createControl;
3088
+ this.equals = equals;
3089
+ this.isEmpty = isEmpty;
3090
+ this.options = {
3091
+ allowEmptyArray: true,
3092
+ allowReuseControls: true,
3093
+ allowManyNullValues: false,
3094
+ allowDuplicateValue: false,
3095
+ ...options,
3096
+ };
3097
+ this.setAllowEmptyArray(this.options.allowEmptyArray);
2791
3098
  }
2792
- // Get current index
2793
- const minTabindex = event && isInputElement(event.target) ? event.target.tabIndex || event.target.tabindex : undefined;
2794
- // Get focusable input elements
2795
- const focusableInputs = getFocusableInputElements(elements, { minTabindex, ...opts });
2796
- if (isNotEmptyArray(focusableInputs)) {
2797
- // Focus on first inputs
2798
- focusableInputs[0].focus();
2799
- return true;
3099
+ /**
3100
+ * WIll rebuild the array, using the given values
3101
+ *
3102
+ * @param values
3103
+ * @param options
3104
+ */
3105
+ setValue(values, options) {
3106
+ if (values === undefined)
3107
+ throw new Error("'undefined' value not allowed in AppFormArray.setValue(). Use 'null' or '[]' to clear the array");
3108
+ if (this.options.allowReuseControls === false) {
3109
+ const disabled = this.disabled;
3110
+ // Clean all
3111
+ this.resize(0, { emitEvent: options?.emitEvent });
3112
+ // Recreate each control, with a default value
3113
+ (values || []).forEach((value) => {
3114
+ const control = this.createControl(value);
3115
+ // Apply parent disabled state, before to push it into the array
3116
+ // This is need to avoid parent form to be enabled, after calling AppFormArray.patchValue() (e.g. in table's row validator)
3117
+ if (disabled && control.enabled)
3118
+ control.disable({ emitEvent: false });
3119
+ else if (!disabled && control.disabled)
3120
+ control.enable({ emitEvent: false });
3121
+ this.push(control, options);
3122
+ });
3123
+ }
3124
+ else {
3125
+ this.resize(values?.length || 0, { emitEvent: false });
3126
+ super.setValue(values, options);
3127
+ }
2800
3128
  }
2801
- return false;
2802
- }
2803
- function focusPreviousInput(event, elements, opts) {
2804
- // Cancelling event (e.g. when emitted by (keydown.tab) )
2805
- if (event) {
2806
- event.preventDefault();
2807
- event.stopPropagation();
3129
+ patchValue(values, options) {
3130
+ // --- /!\ From official Angular doc of 'FormGroup.patchValue()' :
3131
+ // Even though the `value` argument type doesn't allow `null` and `undefined` values, the
3132
+ // `patchValue` can be called recursively and inner data structures might have these values, so
3133
+ // we just ignore such cases when a field containing FormGroup instance receives `null` or
3134
+ // `undefined` as a value.
3135
+ // ---
3136
+ if (values == null)
3137
+ return; // Ignore
3138
+ if (this.options.allowReuseControls === false) {
3139
+ const disabled = this.disabled;
3140
+ // Clean all
3141
+ this.resize(0, { emitEvent: options?.emitEvent });
3142
+ // Recreate each control, with a default value
3143
+ (values || []).forEach((value) => {
3144
+ const control = this.createControl(value);
3145
+ // Apply parent disabled state, before to push it into the array
3146
+ // This is need to avoid parent form to be enabled, after calling AppFormArray.patchValue() (e.g. in table's row validator)
3147
+ if (disabled && control.enabled)
3148
+ control.disable({ emitEvent: false });
3149
+ else if (!disabled && control.disabled)
3150
+ control.enable({ emitEvent: false });
3151
+ this.push(control, options);
3152
+ });
3153
+ }
3154
+ else {
3155
+ this.resize(values?.length || 0, { emitEvent: false });
3156
+ super.patchValue(values, options);
3157
+ }
2808
3158
  }
2809
- // Get current index
2810
- const maxTabindex = event && isInputElement(event.target) ? event.target.tabIndex || event.target.tabindex : undefined;
2811
- // Get focusable input elements
2812
- const focusableInputs = getFocusableInputElements(elements, { maxTabindex, ...opts });
2813
- if (isNotEmptyArray(focusableInputs)) {
2814
- // Focus on last inputs
2815
- focusableInputs[focusableInputs.length - 1].focus();
2816
- return true;
3159
+ resize(length, options) {
3160
+ return resizeArray(this, this.createControl, length, options);
2817
3161
  }
2818
- return false;
2819
- }
2820
-
2821
- function filterNotNil(obs) {
2822
- return obs.pipe(filter(isNotNil));
2823
- }
2824
- function filterTrue(obs) {
2825
- return obs.pipe(filter((v) => v === true));
2826
- }
2827
- function filterFalse(obs, opts) {
2828
- return obs.pipe(filter((v) => v === false));
2829
- }
2830
- function decorateWithTakeUntil(obs, opts) {
2831
- // Set take until notifier
2832
- let takeUntil$ = opts?.stop || (opts?.timeout && timer(opts.timeout));
2833
- if (!takeUntil$)
2834
- return obs; // Skip
2835
- // When stop, throw an error (useful when used with a toPromise())
2836
- if (opts.stopError) {
2837
- let error;
2838
- if (opts.stopError === true) {
2839
- error = new Error(opts.timeout ? `Timeout ${opts.timeout}ms` : `stop`);
3162
+ disable(opts) {
3163
+ super.disable(opts);
3164
+ this.controls.forEach((c) => c.disable(opts));
3165
+ }
3166
+ enable(opts) {
3167
+ super.enable(opts);
3168
+ this.controls.forEach((c) => c.enable(opts));
3169
+ }
3170
+ forEach(ite) {
3171
+ const size = this.length;
3172
+ for (let i = 0; i < size; i++) {
3173
+ const control = this.at(i);
3174
+ if (control)
3175
+ ite(control);
2840
3176
  }
2841
- else if (typeof opts.stopError === 'string') {
2842
- error = new Error(opts.stopError);
3177
+ }
3178
+ /**
3179
+ * @param value
3180
+ * @param options
3181
+ */
3182
+ add(value, options) {
3183
+ addValueInArray(this, this.createControl, this.equals, this.isEmpty, value, { ...this.options, ...options });
3184
+ }
3185
+ removeAt(index, options) {
3186
+ // Do not remove if last criterion
3187
+ if (this.options.allowEmptyArray === false && this.length === 1) {
3188
+ this.clearAt(index, options);
3189
+ return false;
3190
+ }
3191
+ else if (index < this.length) {
3192
+ super.removeAt(index, options);
3193
+ return true;
3194
+ }
3195
+ return false;
3196
+ }
3197
+ clearAt(index, options) {
3198
+ const control = this.at(index);
3199
+ if (this.isEmpty(control.value))
3200
+ return; // skip (not need to clear)
3201
+ if (control instanceof UntypedFormGroup) {
3202
+ copyEntity2Form({}, control, options);
3203
+ }
3204
+ else if (control instanceof UntypedFormArray) {
3205
+ control.setValue([], options);
2843
3206
  }
2844
3207
  else {
2845
- error = opts.stopError;
3208
+ control.setValue(null, options);
2846
3209
  }
2847
- takeUntil$ = takeUntil$.pipe(map(() => {
2848
- throw error;
2849
- }));
3210
+ this.markAsDirty();
2850
3211
  }
2851
- return obs.pipe(takeUntil(takeUntil$));
2852
- }
2853
- function firstNotNil(obs, opts) {
2854
- return decorateWithTakeUntil(obs.pipe(first(isNotNil)), opts);
2855
- }
2856
- function firstTrue(obs, opts) {
2857
- return decorateWithTakeUntil(obs.pipe(first((v) => v === true), map((_) => { }) // Convert to void
2858
- ), opts);
2859
- }
2860
- function firstFalse(obs, opts) {
2861
- return decorateWithTakeUntil(obs.pipe(first((v) => v === false), map((_) => { }) // Convert to void
2862
- ), opts);
2863
- }
2864
- function firstTruePromise(obs, opts) {
2865
- return firstTrue(obs, { stopError: true, ...opts }).toPromise();
2866
- }
2867
- function firstFalsePromise(obs, opts) {
2868
- return firstFalse(obs, { stopError: true, ...opts }).toPromise();
2869
- }
2870
- function firstNotNilPromise(obs, opts) {
2871
- return firstNotNil(obs, { stopError: true, ...opts }).toPromise();
2872
- }
2873
- function chainPromises(defers) {
2874
- return (defers || []).reduce((previous, defer) => {
2875
- // First job
2876
- if (!previous) {
2877
- return (defer()
2878
- // Init the final result array, with the first result
2879
- .then((jobRes) => [jobRes]));
3212
+ isLast(index) {
3213
+ return this.length - 1 === index;
3214
+ }
3215
+ removeAllEmpty() {
3216
+ let index = this.controls.findIndex((c) => this.isEmpty(c.value));
3217
+ while (index !== -1) {
3218
+ this.removeAt(index);
3219
+ index = this.controls.findIndex((c) => this.isEmpty(c.value));
3220
+ }
3221
+ }
3222
+ /* -- internal -- */
3223
+ setAllowEmptyArray(value) {
3224
+ if (this.options.allowEmptyArray === value)
3225
+ return; // Skip if same
3226
+ this.options.allowEmptyArray = value;
3227
+ // Set required (or reset) min length validator
3228
+ if (this.options.allowEmptyArray) {
3229
+ this.setValidators(this.options.validators || null);
3230
+ }
3231
+ else {
3232
+ const validators = this.options?.validators || [];
3233
+ this.setValidators((Array.isArray(validators) ? validators : [validators]).concat(SharedFormArrayValidators.requiredArrayMinLength(1)));
2880
3234
  }
2881
- // Other jobs
2882
- return previous.then((finalResult) => defer()
2883
- // Add job result to final result array
2884
- .then((jobRes) => finalResult.concat(jobRes)));
2885
- }, null);
2886
- }
2887
- /**
2888
- * Wait form a predicate return true. This need to implement AppFormUtils.waitWhilePending(), AppFormUtils.waitIdle()
2889
- *
2890
- * @param predicate
2891
- * @param opts
2892
- */
2893
- async function waitFor(predicate, opts) {
2894
- if (predicate())
2895
- return Promise.resolve();
2896
- const period = (opts && opts.checkPeriod) || 300;
2897
- const dueTime = (opts && opts.dueTime) || period;
2898
- const wait$ = timer(dueTime, period).pipe(
2899
- // For DEBUG :
2900
- //tap(() => console.debug("Waiting form idle...", form)),
2901
- filter((_) => predicate()), map((_) => true));
2902
- await firstNotNilPromise(wait$, opts);
2903
- }
2904
- function waitForTrue(observable, opts) {
2905
- opts = { stopError: true, ...opts };
2906
- // dueTime (without timeout)
2907
- if (opts && opts.dueTime > 0) {
2908
- observable = timer(opts.dueTime).pipe(switchMap(() => observable));
2909
3235
  }
2910
- return firstTrue(observable, opts).toPromise();
2911
- }
2912
- function waitForFalse(observable, opts) {
2913
- return waitForTrue(
2914
- // Inverse the logic
2915
- observable.pipe(map((v) => v === false)), opts);
2916
3236
  }
2917
3237
 
2918
3238
  /**
@@ -3121,17 +3441,21 @@ function addValueInArray(arrayControl, createControl, equals, isEmpty, value, op
3121
3441
  const disabled = arrayControl.disabled;
3122
3442
  let hasChanged = false;
3123
3443
  let index = -1;
3444
+ let isEmptyValue = isEmpty(value);
3124
3445
  // Search if value already exists
3125
- if (!isEmpty(value)) {
3446
+ if (!isEmptyValue && options?.allowDuplicateValue !== true) {
3126
3447
  index = (arrayControl.value || []).findIndex((v) => equals(value, v));
3127
3448
  }
3128
- // If value not exists, but last value is empty: use it
3129
- if (index === -1 && arrayControl.length && isEmpty(arrayControl.at(arrayControl.length - 1).value)) {
3130
- index = arrayControl.length - 1;
3449
+ // If value not exists, but last value is empty: reuse last value
3450
+ if (index === -1 && options?.allowManyNullValues !== true && arrayControl.length > 0) {
3451
+ const lastValue = arrayControl.at(arrayControl.length - 1).value;
3452
+ if (isEmpty(lastValue)) {
3453
+ index = arrayControl.length - 1;
3454
+ }
3131
3455
  }
3132
3456
  // Replace the existing value
3133
3457
  if (index !== -1) {
3134
- if (!isEmpty(value)) {
3458
+ if (!isEmptyValue) {
3135
3459
  arrayControl.at(index).patchValue(value, options);
3136
3460
  hasChanged = true;
3137
3461
  }
@@ -3155,7 +3479,7 @@ function addValueInArray(arrayControl, createControl, equals, isEmpty, value, op
3155
3479
  if (hasChanged) {
3156
3480
  if (!options || options.emitEvent !== false) {
3157
3481
  // Mark array control dirty
3158
- if (!isEmpty(value)) {
3482
+ if (!isEmptyValue) {
3159
3483
  arrayControl.markAsDirty();
3160
3484
  }
3161
3485
  }
@@ -3472,309 +3796,6 @@ class AppFormProvider {
3472
3796
  return true;
3473
3797
  }
3474
3798
  }
3475
- class FormArrayHelper {
3476
- _formArray;
3477
- createControl;
3478
- equals;
3479
- isEmpty;
3480
- static getOrCreateArray(formBuilder, form, arrayName) {
3481
- const disabled = form.disabled;
3482
- let arrayControl = form.get(arrayName);
3483
- if (!arrayControl) {
3484
- arrayControl = formBuilder.array([]);
3485
- // Apply parent disabled state, before to push it into the array
3486
- // This is need to avoid parent form to be enabled, after calling resizeArray()
3487
- if (disabled && arrayControl.enabled)
3488
- arrayControl.disable({ emitEvent: false });
3489
- form.addControl(arrayName, arrayControl);
3490
- }
3491
- return arrayControl;
3492
- }
3493
- _allowEmptyArray;
3494
- _validators;
3495
- get allowEmptyArray() {
3496
- return this._allowEmptyArray;
3497
- }
3498
- set allowEmptyArray(value) {
3499
- this.setAllowEmptyArray(value);
3500
- }
3501
- get formArray() {
3502
- return this._formArray;
3503
- }
3504
- constructor(_formArray, createControl, equals, isEmpty, options) {
3505
- this._formArray = _formArray;
3506
- this.createControl = createControl;
3507
- this.equals = equals;
3508
- this.isEmpty = isEmpty;
3509
- this._validators = options && options.validators;
3510
- // empty array not allow by default
3511
- this.setAllowEmptyArray(toBoolean(options?.allowEmptyArray, false));
3512
- }
3513
- /**
3514
- * @param value
3515
- * @param options
3516
- */
3517
- add(value, options) {
3518
- return addValueInArray(this._formArray, this.createControl, this.equals, this.isEmpty, value, options);
3519
- }
3520
- removeAt(index) {
3521
- // Do not remove if last criterion
3522
- if (!this._allowEmptyArray && this._formArray.length === 1) {
3523
- return clearValueInArray(this._formArray, this.isEmpty, index);
3524
- }
3525
- else {
3526
- return removeValueInArray(this._formArray, this.isEmpty, index);
3527
- }
3528
- }
3529
- resize(length, options) {
3530
- return resizeArray(this._formArray, this.createControl, length, options);
3531
- }
3532
- clearAt(index) {
3533
- return clearValueInArray(this._formArray, this.isEmpty, index);
3534
- }
3535
- isLast(index) {
3536
- return this._formArray.length - 1 === index;
3537
- }
3538
- removeAllEmpty() {
3539
- let index = this._formArray.controls.findIndex((c) => this.isEmpty(c.value));
3540
- while (index !== -1) {
3541
- this.removeAt(index);
3542
- index = this._formArray.controls.findIndex((c) => this.isEmpty(c.value));
3543
- }
3544
- }
3545
- size() {
3546
- return this._formArray.length;
3547
- }
3548
- at(index) {
3549
- return this._formArray.at(index);
3550
- }
3551
- /**
3552
- * Resize the FormArray, then set values
3553
- *
3554
- * @param values
3555
- * @param options
3556
- */
3557
- setValue(values, options) {
3558
- this.resize(values?.length || 0);
3559
- this._formArray.setValue(values, options);
3560
- }
3561
- /**
3562
- * Resize the FormArray, then patch values
3563
- *
3564
- * @param values
3565
- * @param options
3566
- */
3567
- patchValue(values, options) {
3568
- this.resize(values?.length || 0);
3569
- this._formArray.patchValue(values, options);
3570
- }
3571
- disable(opts) {
3572
- this._formArray.controls.forEach((c) => c.disable(opts));
3573
- }
3574
- enable(opts) {
3575
- this._formArray.controls.forEach((c) => c.enable(opts));
3576
- }
3577
- forEach(ite) {
3578
- const size = this.size();
3579
- for (let i = 0; i < size; i++) {
3580
- const control = this._formArray.at(i);
3581
- if (control)
3582
- ite(control);
3583
- }
3584
- }
3585
- /* -- internal methods -- */
3586
- setAllowEmptyArray(value) {
3587
- if (this._allowEmptyArray === value)
3588
- return; // Skip if same
3589
- this._allowEmptyArray = value;
3590
- // Set required (or reste) min length validator
3591
- if (this._allowEmptyArray) {
3592
- this._formArray.setValidators(this._validators || null);
3593
- }
3594
- else {
3595
- this._formArray.setValidators((this._validators || []).concat(SharedFormArrayValidators.requiredArrayMinLength(1)));
3596
- }
3597
- }
3598
- }
3599
- class AppFormArray extends UntypedFormArray {
3600
- createControl;
3601
- equals;
3602
- isEmpty;
3603
- options;
3604
- get allowEmptyArray() {
3605
- return this.options.allowEmptyArray;
3606
- }
3607
- set allowEmptyArray(value) {
3608
- this.setAllowEmptyArray(value);
3609
- }
3610
- constructor(createControl, equals, isEmpty, options) {
3611
- super([], options);
3612
- this.createControl = createControl;
3613
- this.equals = equals;
3614
- this.isEmpty = isEmpty;
3615
- this.options = {
3616
- allowEmptyArray: true,
3617
- allowReuseControls: true,
3618
- ...options,
3619
- };
3620
- this.setAllowEmptyArray(this.options.allowEmptyArray);
3621
- }
3622
- /**
3623
- * WIll rebuild the array, using the given values
3624
- *
3625
- * @param values
3626
- * @param options
3627
- */
3628
- setValue(values, options) {
3629
- if (values === undefined)
3630
- throw new Error("'undefined' value not allowed in AppFormArray.setValue(). Use 'null' or '[]' to clear the array");
3631
- if (this.options.allowReuseControls === false) {
3632
- const disabled = this.disabled;
3633
- // Clean all
3634
- this.resize(0, { emitEvent: options?.emitEvent });
3635
- // Recreate each control, with a default value
3636
- (values || []).forEach((value) => {
3637
- const control = this.createControl(value);
3638
- // Apply parent disabled state, before to push it into the array
3639
- // This is need to avoid parent form to be enabled, after calling AppFormArray.patchValue() (e.g. in table's row validator)
3640
- if (disabled && control.enabled)
3641
- control.disable({ emitEvent: false });
3642
- else if (!disabled && control.disabled)
3643
- control.enable({ emitEvent: false });
3644
- this.push(control, options);
3645
- });
3646
- }
3647
- else {
3648
- this.resize(values?.length || 0, { emitEvent: false });
3649
- super.setValue(values, options);
3650
- }
3651
- }
3652
- patchValue(values, options) {
3653
- // --- /!\ From official Angular doc of 'FormGroup.patchValue()' :
3654
- // Even though the `value` argument type doesn't allow `null` and `undefined` values, the
3655
- // `patchValue` can be called recursively and inner data structures might have these values, so
3656
- // we just ignore such cases when a field containing FormGroup instance receives `null` or
3657
- // `undefined` as a value.
3658
- // ---
3659
- if (values == null)
3660
- return; // Ignore
3661
- if (this.options.allowReuseControls === false) {
3662
- const disabled = this.disabled;
3663
- // Clean all
3664
- this.resize(0, { emitEvent: options?.emitEvent });
3665
- // Recreate each control, with a default value
3666
- (values || []).forEach((value) => {
3667
- const control = this.createControl(value);
3668
- // Apply parent disabled state, before to push it into the array
3669
- // This is need to avoid parent form to be enabled, after calling AppFormArray.patchValue() (e.g. in table's row validator)
3670
- if (disabled && control.enabled)
3671
- control.disable({ emitEvent: false });
3672
- else if (!disabled && control.disabled)
3673
- control.enable({ emitEvent: false });
3674
- this.push(control, options);
3675
- });
3676
- }
3677
- else {
3678
- this.resize(values?.length || 0, { emitEvent: false });
3679
- super.patchValue(values, options);
3680
- }
3681
- }
3682
- resize(length, options) {
3683
- if (this.length === length)
3684
- return; // Nothing to do
3685
- const disabled = this.disabled; // Save enabled here, because update can occur from resizeArray()
3686
- // Reduce
3687
- while (this.length > length) {
3688
- // WARN: We use super.removeAt() and NOT this.removeAt(), to avoid infinite loop, when calling resize(0) AND options.allowEmptyArray=false
3689
- super.removeAt(this.length - 1, { emitEvent: options?.emitEvent });
3690
- }
3691
- // Or increase size
3692
- while (this.length < length) {
3693
- const control = this.createControl();
3694
- // Apply parent disabled state, before to push it into the array
3695
- // This is need to avoid parent form to be enabled, after calling AppFormArray.patchValue() (e.g. in table's row validator)
3696
- if (disabled && control.enabled)
3697
- control.disable({ emitEvent: false });
3698
- else if (!disabled && control.disabled)
3699
- control.enable({ emitEvent: false });
3700
- this.push(control, options);
3701
- }
3702
- }
3703
- disable(opts) {
3704
- super.disable(opts);
3705
- this.controls.forEach((c) => c.disable(opts));
3706
- }
3707
- enable(opts) {
3708
- super.enable(opts);
3709
- this.controls.forEach((c) => c.enable(opts));
3710
- }
3711
- forEach(ite) {
3712
- const size = this.length;
3713
- for (let i = 0; i < size; i++) {
3714
- const control = this.at(i);
3715
- if (control)
3716
- ite(control);
3717
- }
3718
- }
3719
- /**
3720
- * @param value
3721
- * @param options
3722
- */
3723
- add(value, options) {
3724
- addValueInArray(this, this.createControl, this.equals, this.isEmpty, value, options);
3725
- }
3726
- removeAt(index, options) {
3727
- // Do not remove if last criterion
3728
- if (this.options.allowEmptyArray === false && this.length === 1) {
3729
- this.clearAt(index, options);
3730
- return false;
3731
- }
3732
- else if (index < this.length) {
3733
- super.removeAt(index, options);
3734
- return true;
3735
- }
3736
- return false;
3737
- }
3738
- clearAt(index, options) {
3739
- const control = this.at(index);
3740
- if (this.isEmpty(control.value))
3741
- return; // skip (not need to clear)
3742
- if (control instanceof UntypedFormGroup) {
3743
- copyEntity2Form({}, control, options);
3744
- }
3745
- else if (control instanceof UntypedFormArray) {
3746
- control.setValue([], options);
3747
- }
3748
- else {
3749
- control.setValue(null, options);
3750
- }
3751
- this.markAsDirty();
3752
- }
3753
- isLast(index) {
3754
- return this.length - 1 === index;
3755
- }
3756
- removeAllEmpty() {
3757
- let index = this.controls.findIndex((c) => this.isEmpty(c.value));
3758
- while (index !== -1) {
3759
- this.removeAt(index);
3760
- index = this.controls.findIndex((c) => this.isEmpty(c.value));
3761
- }
3762
- }
3763
- /* -- internal -- */
3764
- setAllowEmptyArray(value) {
3765
- if (this.options.allowEmptyArray === value)
3766
- return; // Skip if same
3767
- this.options.allowEmptyArray = value;
3768
- // Set required (or reste) min length validator
3769
- if (this.options.allowEmptyArray) {
3770
- this.setValidators(this.options.validators || null);
3771
- }
3772
- else {
3773
- const validators = this.options?.validators || [];
3774
- this.setValidators((Array.isArray(validators) ? validators : [validators]).concat(SharedFormArrayValidators.requiredArrayMinLength(1)));
3775
- }
3776
- }
3777
- }
3778
3799
  /**
3779
3800
  * Helper class, for angular forms
3780
3801
  */
@@ -42122,12 +42143,105 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
42122
42143
  }]
42123
42144
  }] });
42124
42145
 
42146
+ class ArrayFormTestPage extends AppForm {
42147
+ formBuilder;
42148
+ platform;
42149
+ injection;
42150
+ cd;
42151
+ subscription = new Subscription();
42152
+ get defaultFormArray() {
42153
+ return this.form.get('default');
42154
+ }
42155
+ get notEmptyFormArray() {
42156
+ return this.form.get('notEmpty');
42157
+ }
42158
+ get nullValuesFormArray() {
42159
+ return this.form.get('nullValues');
42160
+ }
42161
+ get duplicatedValuesFormArray() {
42162
+ return this.form.get('duplicatedValues');
42163
+ }
42164
+ get valueToAddControl() {
42165
+ return this.form?.get('valueToAdd');
42166
+ }
42167
+ constructor(formBuilder, platform, injection, cd) {
42168
+ super(injection, formBuilder.group({
42169
+ default: new AppFormArray((value) => this.formBuilder.control(value), (v1, v2) => v1 === v2, (value) => isNilOrBlank(value)),
42170
+ notEmpty: new AppFormArray((value) => this.formBuilder.control(value), (v1, v2) => v1 === v2, (value) => isNilOrBlank(value), {
42171
+ allowEmptyArray: false,
42172
+ }),
42173
+ nullValues: new AppFormArray((value) => this.formBuilder.control(value), (v1, v2) => v1 === v2, (value) => isNilOrBlank(value), {
42174
+ allowEmptyArray: false,
42175
+ allowManyNullValues: true,
42176
+ }),
42177
+ duplicatedValues: new AppFormArray((value) => this.formBuilder.control(value), (v1, v2) => v1 === v2, (value) => isNilOrBlank(value), {
42178
+ allowEmptyArray: false,
42179
+ allowManyNullValues: true,
42180
+ allowDuplicateValue: true,
42181
+ }),
42182
+ // Value to add
42183
+ valueToAdd: formBuilder.control(null, []),
42184
+ }));
42185
+ this.formBuilder = formBuilder;
42186
+ this.platform = platform;
42187
+ this.injection = injection;
42188
+ this.cd = cd;
42189
+ }
42190
+ ngOnInit() {
42191
+ setTimeout$1(() => this.loadData(), 250);
42192
+ }
42193
+ ngOnDestroy() {
42194
+ this.subscription.unsubscribe();
42195
+ }
42196
+ // Load the form with data
42197
+ async loadData() {
42198
+ this.setValue({
42199
+ default: ['TESTING_1', 'TESTING_2'],
42200
+ notEmpty: ['TESTING_1', 'TESTING_2'],
42201
+ nullValues: ['TESTING_1', null, 'TESTING_4'],
42202
+ duplicatedValues: ['TESTING_1', null, 'TESTING_1'],
42203
+ // Add with value
42204
+ valueToAdd: null,
42205
+ });
42206
+ }
42207
+ setFormArrayValue(formArray, values) {
42208
+ const arrayValues = Array.isArray(values) ? values : values?.split(',').map(trimEmptyToNull);
42209
+ formArray.patchValue(arrayValues);
42210
+ this.markAsDirty();
42211
+ }
42212
+ /* -- protected methods -- */
42213
+ markForCheck() {
42214
+ this.cd.markForCheck();
42215
+ }
42216
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ArrayFormTestPage, deps: [{ token: i1$2.UntypedFormBuilder }, { token: PlatformService }, { token: i0.Injector }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
42217
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: ArrayFormTestPage, selector: "app-array-test", usesInheritance: true, ngImport: i0, template: "<ion-header>\n <ion-toolbar color=\"primary\">\n <ion-buttons slot=\"start\">\n <ion-back-button></ion-back-button>\n </ion-buttons>\n\n <ion-title>Form Array test page</ion-title>\n </ion-toolbar>\n</ion-header>\n\n<ion-content>\n <form class=\"form-container ion-padding\" [formGroup]=\"form\">\n <ion-grid>\n <ion-row>\n <!-- Default -->\n <ion-col size=\"6\">\n <ng-container\n *ngTemplateOutlet=\"\n arrayCard;\n context: { $implicit: defaultFormArray, title: 'Default behavior', showSetValue: true }\n \"\n ></ng-container>\n </ion-col>\n\n <!-- Not empty array -->\n <ion-col size=\"6\">\n <ng-container\n *ngTemplateOutlet=\"\n arrayCard;\n context: { $implicit: notEmptyFormArray, title: 'Not empty array', showSetValue: true }\n \"\n ></ng-container>\n </ion-col>\n\n <!-- Allow null values -->\n <ion-col size=\"6\">\n <ng-container\n *ngTemplateOutlet=\"\n arrayCard;\n context: {\n $implicit: nullValuesFormArray,\n title: 'Allow many null values',\n showAddToolbar: true,\n showResizeToolbar: true,\n showSetValue: true\n }\n \"\n ></ng-container>\n </ion-col>\n\n <!-- Allow duplicated value -->\n <ion-col size=\"6\">\n <ng-container\n *ngTemplateOutlet=\"\n arrayCard;\n context: {\n $implicit: duplicatedValuesFormArray,\n title: 'Allow duplicated values',\n showAddToolbar: true,\n showResizeToolbar: true,\n showSetValue: true\n }\n \"\n ></ng-container>\n </ion-col>\n </ion-row>\n </ion-grid>\n </form>\n</ion-content>\n\n<ng-template\n #arrayCard\n let-formArray\n let-title=\"title\"\n let-showAddToolbar=\"showAddToolbar\"\n let-showResizeToolbar=\"showResizeToolbar\"\n let-showSetValue=\"showSetValue\"\n>\n <ion-card>\n <ion-card-header>\n <ion-card-title>{{ title }}</ion-card-title>\n </ion-card-header>\n <ion-card-content>\n @for (\n control of (formArray | formGetArray).controls;\n track i;\n let i = $index;\n let first = $first;\n let last = $last\n ) {\n <ion-row>\n <ion-col>\n <mat-form-field>\n <mat-label>Value #{{ i + 1 }}</mat-label>\n <input matInput [formControl]=\"control | formGetControl\" />\n </mat-form-field>\n </ion-col>\n <ion-col size=\"auto\">\n @if (last && !showAddToolbar) {\n <button type=\"button\" mat-icon-button [title]=\"'COMMON.BTN_ADD' | translate\" (click)=\"formArray.add()\">\n <mat-icon>add</mat-icon>\n </button>\n }\n <button\n type=\"button\"\n mat-icon-button\n [title]=\"'COMMON.BTN_DELETE' | translate\"\n (click)=\"formArray.removeAt(i) && markAsDirty()\"\n >\n <mat-icon>close</mat-icon>\n </button>\n </ion-col>\n </ion-row>\n }\n\n @if (formArray.length === 0 && !showAddToolbar) {\n <button type=\"button\" mat-icon-button [title]=\"'COMMON.BTN_ADD' | translate\" (click)=\"formArray.add()\">\n <mat-icon>add</mat-icon>\n </button>\n }\n </ion-card-content>\n\n @if (showAddToolbar || showResizeToolbar || showSetValue) {\n <ion-toolbar>\n <!-- Set array value -->\n <ion-row *ngIf=\"showSetValue\">\n <ion-col>\n <mat-form-field>\n <mat-label>Set Value</mat-label>\n <input\n #setValueInput\n matInput\n type=\"text\"\n placeholder=\"Comma separated values\"\n (keydown.enter)=\"setFormArrayValue(formArray, setValueInput.value)\"\n />\n </mat-form-field>\n </ion-col>\n <ion-col size=\"auto\">\n <button\n type=\"button\"\n mat-icon-button\n title=\"Set value\"\n (click)=\"setFormArrayValue(formArray, setValueInput.value)\"\n >\n <mat-icon>checkmark</mat-icon>\n </button>\n </ion-col>\n </ion-row>\n\n <!-- Resize the array -->\n <ion-row *ngIf=\"showResizeToolbar\">\n <ion-col>\n <mat-form-field>\n <mat-label>Resize</mat-label>\n <input\n #sizeInput\n matInput\n type=\"number\"\n step=\"1\"\n min=\"0\"\n placeholder=\"New array length\"\n (keydown.enter)=\"formArray.resize(sizeInput.value)\"\n />\n </mat-form-field>\n </ion-col>\n <ion-col size=\"auto\">\n <button type=\"button\" mat-icon-button title=\"Resize\" (click)=\"formArray.resize(sizeInput.value)\">\n <mat-icon>checkmark</mat-icon>\n </button>\n </ion-col>\n </ion-row>\n\n <!-- Add a item with a value -->\n <ion-row *ngIf=\"showAddToolbar\">\n <ion-col>\n <mat-form-field>\n <mat-label>Value to add</mat-label>\n <input #valueInput matInput type=\"text\" (keydown.enter)=\"formArray.add(valueInput.value)\" />\n </mat-form-field>\n </ion-col>\n <ion-col size=\"auto\">\n <button\n type=\"button\"\n mat-icon-button\n [title]=\"'COMMON.BTN_ADD' | translate\"\n (click)=\"formArray.add(valueInput.value)\"\n >\n <mat-icon>add</mat-icon>\n </button>\n </ion-col>\n </ion-row>\n </ion-toolbar>\n }\n </ion-card>\n</ng-template>\n", dependencies: [{ kind: "directive", type: i3$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i3$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: i2$1.IonButtons, selector: "ion-buttons", inputs: ["collapse"] }, { kind: "component", type: i2$1.IonCard, selector: "ion-card", inputs: ["button", "color", "disabled", "download", "href", "mode", "rel", "routerAnimation", "routerDirection", "target", "type"] }, { kind: "component", type: i2$1.IonCardContent, selector: "ion-card-content", inputs: ["mode"] }, { kind: "component", type: i2$1.IonCardHeader, selector: "ion-card-header", inputs: ["color", "mode", "translucent"] }, { kind: "component", type: i2$1.IonCardTitle, selector: "ion-card-title", inputs: ["color", "mode"] }, { kind: "component", type: i2$1.IonCol, selector: "ion-col", inputs: ["offset", "offsetLg", "offsetMd", "offsetSm", "offsetXl", "offsetXs", "pull", "pullLg", "pullMd", "pullSm", "pullXl", "pullXs", "push", "pushLg", "pushMd", "pushSm", "pushXl", "pushXs", "size", "sizeLg", "sizeMd", "sizeSm", "sizeXl", "sizeXs"] }, { kind: "component", type: i2$1.IonContent, selector: "ion-content", inputs: ["color", "forceOverscroll", "fullscreen", "scrollEvents", "scrollX", "scrollY"] }, { kind: "component", type: i2$1.IonGrid, selector: "ion-grid", inputs: ["fixed"] }, { kind: "component", type: i2$1.IonHeader, selector: "ion-header", inputs: ["collapse", "mode", "translucent"] }, { kind: "component", type: i2$1.IonRow, selector: "ion-row" }, { kind: "component", type: i2$1.IonTitle, selector: "ion-title", inputs: ["color", "size"] }, { kind: "component", type: i2$1.IonToolbar, selector: "ion-toolbar", inputs: ["color", "mode"] }, { kind: "component", type: i2$1.IonBackButton, selector: "ion-back-button" }, { kind: "directive", type: i1$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i9.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "pipe", type: i1$1.TranslatePipe, name: "translate" }, { kind: "pipe", type: FormGetControlPipe, name: "formGetControl" }, { kind: "pipe", type: FormGetArrayPipe, name: "formGetArray" }] });
42218
+ }
42219
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ArrayFormTestPage, decorators: [{
42220
+ type: Component,
42221
+ args: [{ selector: 'app-array-test', template: "<ion-header>\n <ion-toolbar color=\"primary\">\n <ion-buttons slot=\"start\">\n <ion-back-button></ion-back-button>\n </ion-buttons>\n\n <ion-title>Form Array test page</ion-title>\n </ion-toolbar>\n</ion-header>\n\n<ion-content>\n <form class=\"form-container ion-padding\" [formGroup]=\"form\">\n <ion-grid>\n <ion-row>\n <!-- Default -->\n <ion-col size=\"6\">\n <ng-container\n *ngTemplateOutlet=\"\n arrayCard;\n context: { $implicit: defaultFormArray, title: 'Default behavior', showSetValue: true }\n \"\n ></ng-container>\n </ion-col>\n\n <!-- Not empty array -->\n <ion-col size=\"6\">\n <ng-container\n *ngTemplateOutlet=\"\n arrayCard;\n context: { $implicit: notEmptyFormArray, title: 'Not empty array', showSetValue: true }\n \"\n ></ng-container>\n </ion-col>\n\n <!-- Allow null values -->\n <ion-col size=\"6\">\n <ng-container\n *ngTemplateOutlet=\"\n arrayCard;\n context: {\n $implicit: nullValuesFormArray,\n title: 'Allow many null values',\n showAddToolbar: true,\n showResizeToolbar: true,\n showSetValue: true\n }\n \"\n ></ng-container>\n </ion-col>\n\n <!-- Allow duplicated value -->\n <ion-col size=\"6\">\n <ng-container\n *ngTemplateOutlet=\"\n arrayCard;\n context: {\n $implicit: duplicatedValuesFormArray,\n title: 'Allow duplicated values',\n showAddToolbar: true,\n showResizeToolbar: true,\n showSetValue: true\n }\n \"\n ></ng-container>\n </ion-col>\n </ion-row>\n </ion-grid>\n </form>\n</ion-content>\n\n<ng-template\n #arrayCard\n let-formArray\n let-title=\"title\"\n let-showAddToolbar=\"showAddToolbar\"\n let-showResizeToolbar=\"showResizeToolbar\"\n let-showSetValue=\"showSetValue\"\n>\n <ion-card>\n <ion-card-header>\n <ion-card-title>{{ title }}</ion-card-title>\n </ion-card-header>\n <ion-card-content>\n @for (\n control of (formArray | formGetArray).controls;\n track i;\n let i = $index;\n let first = $first;\n let last = $last\n ) {\n <ion-row>\n <ion-col>\n <mat-form-field>\n <mat-label>Value #{{ i + 1 }}</mat-label>\n <input matInput [formControl]=\"control | formGetControl\" />\n </mat-form-field>\n </ion-col>\n <ion-col size=\"auto\">\n @if (last && !showAddToolbar) {\n <button type=\"button\" mat-icon-button [title]=\"'COMMON.BTN_ADD' | translate\" (click)=\"formArray.add()\">\n <mat-icon>add</mat-icon>\n </button>\n }\n <button\n type=\"button\"\n mat-icon-button\n [title]=\"'COMMON.BTN_DELETE' | translate\"\n (click)=\"formArray.removeAt(i) && markAsDirty()\"\n >\n <mat-icon>close</mat-icon>\n </button>\n </ion-col>\n </ion-row>\n }\n\n @if (formArray.length === 0 && !showAddToolbar) {\n <button type=\"button\" mat-icon-button [title]=\"'COMMON.BTN_ADD' | translate\" (click)=\"formArray.add()\">\n <mat-icon>add</mat-icon>\n </button>\n }\n </ion-card-content>\n\n @if (showAddToolbar || showResizeToolbar || showSetValue) {\n <ion-toolbar>\n <!-- Set array value -->\n <ion-row *ngIf=\"showSetValue\">\n <ion-col>\n <mat-form-field>\n <mat-label>Set Value</mat-label>\n <input\n #setValueInput\n matInput\n type=\"text\"\n placeholder=\"Comma separated values\"\n (keydown.enter)=\"setFormArrayValue(formArray, setValueInput.value)\"\n />\n </mat-form-field>\n </ion-col>\n <ion-col size=\"auto\">\n <button\n type=\"button\"\n mat-icon-button\n title=\"Set value\"\n (click)=\"setFormArrayValue(formArray, setValueInput.value)\"\n >\n <mat-icon>checkmark</mat-icon>\n </button>\n </ion-col>\n </ion-row>\n\n <!-- Resize the array -->\n <ion-row *ngIf=\"showResizeToolbar\">\n <ion-col>\n <mat-form-field>\n <mat-label>Resize</mat-label>\n <input\n #sizeInput\n matInput\n type=\"number\"\n step=\"1\"\n min=\"0\"\n placeholder=\"New array length\"\n (keydown.enter)=\"formArray.resize(sizeInput.value)\"\n />\n </mat-form-field>\n </ion-col>\n <ion-col size=\"auto\">\n <button type=\"button\" mat-icon-button title=\"Resize\" (click)=\"formArray.resize(sizeInput.value)\">\n <mat-icon>checkmark</mat-icon>\n </button>\n </ion-col>\n </ion-row>\n\n <!-- Add a item with a value -->\n <ion-row *ngIf=\"showAddToolbar\">\n <ion-col>\n <mat-form-field>\n <mat-label>Value to add</mat-label>\n <input #valueInput matInput type=\"text\" (keydown.enter)=\"formArray.add(valueInput.value)\" />\n </mat-form-field>\n </ion-col>\n <ion-col size=\"auto\">\n <button\n type=\"button\"\n mat-icon-button\n [title]=\"'COMMON.BTN_ADD' | translate\"\n (click)=\"formArray.add(valueInput.value)\"\n >\n <mat-icon>add</mat-icon>\n </button>\n </ion-col>\n </ion-row>\n </ion-toolbar>\n }\n </ion-card>\n</ng-template>\n" }]
42222
+ }], ctorParameters: () => [{ type: i1$2.UntypedFormBuilder }, { type: PlatformService }, { type: i0.Injector }, { type: i0.ChangeDetectorRef }] });
42223
+
42224
+ class FormArrayTestModule {
42225
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: FormArrayTestModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
42226
+ static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "17.3.12", ngImport: i0, type: FormArrayTestModule, declarations: [ArrayFormTestPage], imports: [CommonModule, SharedModule, CoreModule, i1$1.TranslateModule], exports: [ArrayFormTestPage, RouterModule] });
42227
+ static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: FormArrayTestModule, imports: [CommonModule, SharedModule, CoreModule, TranslateModule.forChild(), RouterModule] });
42228
+ }
42229
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: FormArrayTestModule, decorators: [{
42230
+ type: NgModule,
42231
+ args: [{
42232
+ imports: [CommonModule, SharedModule, CoreModule, TranslateModule.forChild()],
42233
+ declarations: [ArrayFormTestPage],
42234
+ exports: [ArrayFormTestPage, RouterModule],
42235
+ }]
42236
+ }] });
42237
+
42125
42238
  const CORE_TESTING_PAGES = [
42126
42239
  { label: 'Core components', divider: true },
42127
42240
  { label: 'Table (click to edit)', page: '/testing/core/table' },
42128
42241
  { label: 'Table 2 (click to select)', page: '/testing/core/table2' },
42129
42242
  { label: 'Text popover', page: '/testing/core/text-popover' },
42130
42243
  { label: 'Properties form', page: '/testing/core/properties-form' },
42244
+ { label: 'Array form', page: '/testing/core/array-form' },
42131
42245
  ];
42132
42246
  const routes$1 = [
42133
42247
  {
@@ -42155,6 +42269,11 @@ const routes$1 = [
42155
42269
  pathMatch: 'full',
42156
42270
  component: PropertiesFormTestPage,
42157
42271
  },
42272
+ {
42273
+ path: 'array-form',
42274
+ pathMatch: 'full',
42275
+ component: ArrayFormTestPage,
42276
+ },
42158
42277
  ];
42159
42278
  class CoreTestingModule {
42160
42279
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: CoreTestingModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
@@ -42162,22 +42281,26 @@ class CoreTestingModule {
42162
42281
  // Sub modules
42163
42282
  TableTestingModule,
42164
42283
  TextPopoverTestingModule,
42165
- PropertiesFormTestingModule], exports: [RouterModule,
42284
+ PropertiesFormTestingModule,
42285
+ FormArrayTestModule], exports: [RouterModule,
42166
42286
  // Sub modules
42167
42287
  TableTestingModule,
42168
42288
  TextPopoverTestingModule,
42169
- PropertiesFormTestingModule] });
42289
+ PropertiesFormTestingModule,
42290
+ FormArrayTestModule] });
42170
42291
  static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: CoreTestingModule, imports: [CommonModule,
42171
42292
  TranslateModule.forChild(),
42172
42293
  RouterModule.forChild(routes$1),
42173
42294
  // Sub modules
42174
42295
  TableTestingModule,
42175
42296
  TextPopoverTestingModule,
42176
- PropertiesFormTestingModule, RouterModule,
42297
+ PropertiesFormTestingModule,
42298
+ FormArrayTestModule, RouterModule,
42177
42299
  // Sub modules
42178
42300
  TableTestingModule,
42179
42301
  TextPopoverTestingModule,
42180
- PropertiesFormTestingModule] });
42302
+ PropertiesFormTestingModule,
42303
+ FormArrayTestModule] });
42181
42304
  }
42182
42305
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: CoreTestingModule, decorators: [{
42183
42306
  type: NgModule,
@@ -42190,6 +42313,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
42190
42313
  TableTestingModule,
42191
42314
  TextPopoverTestingModule,
42192
42315
  PropertiesFormTestingModule,
42316
+ FormArrayTestModule,
42193
42317
  ],
42194
42318
  exports: [
42195
42319
  RouterModule,
@@ -42197,6 +42321,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
42197
42321
  TableTestingModule,
42198
42322
  TextPopoverTestingModule,
42199
42323
  PropertiesFormTestingModule,
42324
+ FormArrayTestModule,
42200
42325
  ],
42201
42326
  }]
42202
42327
  }] });
@@ -42804,5 +42929,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
42804
42929
  * Generated bundle index. Do not edit.
42805
42930
  */
42806
42931
 
42807
- export { APP_ABOUT_DEVELOPERS, APP_ABOUT_PARTNERS, APP_CONFIG_OPTIONS, APP_DEBUG_DATA_SERVICE, APP_FORM_ERROR_I18N_KEYS, APP_GRAPHQL_FRAGMENTS, APP_GRAPHQL_TYPE_POLICIES, APP_HOME_BUTTONS, APP_JOB_PROGRESSION_SERVICE, APP_LOCALES, APP_LOCAL_SETTINGS, APP_LOCAL_SETTINGS_OPTIONS, APP_LOCAL_STORAGE_TYPE_POLICIES, APP_LOGGING_SERVICE, APP_MENU_ITEMS, APP_MENU_OPTIONS, APP_NAMED_FILTER_SERVICE, APP_PROGRESS_BAR_SERVICE, APP_SETTINGS_MENU_ITEMS, APP_STORAGE, APP_STORAGE_EXPLORER_PROTECTED_KEYS, APP_TESTING_PAGES, APP_USER_EVENT_LIST_INFINITE_SCROLL_THRESHOLD, APP_USER_EVENT_SERVICE, APP_USER_SETTINGS_OPTIONS, APP_USER_TOKEN_SCOPES, AboutModal, AbstractNamedFilterService, AbstractSelectionModelPipe, AbstractTableSelectionPipe, AbstractUserEventService, Account, AccountPage, AccountService, AccountToStringPipe, AccountUtils, ActionsColumnComponent, AdminModule, AdminRoutingModule, AdminUsersModule, Alerts, AndroidOsEnvironment, AppAboutModalModule, AppAccountModule, AppAsyncTable, AppAuthForm, AppAuthModal, AppAuthModule, AppEditor, AppEditorOptions, AppEntityEditor, AppEntityEditorModal, AppEntityEditorModalOptions, AppEntityFormModule, AppForm, AppFormArray, AppFormButtonsBarModule, AppFormContainer, AppFormField, AppFormModule, AppFormProvider, AppFormUtils, AppGestureConfig, AppGraphQLModule, AppHelpModal, AppHomePageModule, AppIconComponent, AppIconModule, AppImageGalleryComponent, AppInMemoryTable, AppInstallUpgradeCard, AppInstallUpgradeCardModule, AppListForm, AppListFormModule, AppLoadingSpinner, AppMenuModule, AppNullForm, AppPropertiesForm, AppPropertiesFormModule, AppPropertiesTable, AppRegisterModule, AppRowField, AppSelectPeerModule, AppSettingsPageModule, AppTabEditor, AppTabEditorOptions, AppTable, AppTableModule, AppTableUtils, AppTextPopoverModule, AppUpdateOfflineModeCard, AppUpdateOfflineModeCardModule, AppValidatorService, ArrayFilterPipe, ArrayFirstPipe, ArrayIncludesPipe, ArrayJoinPipe, ArrayLastPipe, ArrayLengthPipe, ArrayPluckPipe, AsAnyPipe, AsArrayPipe, AsFloatLabelTypePipe, AsObservablePipe, AudioProvider, AudioTestingModule, AudioTestingPage, AuthGuardService, AutoTitleDirective, AutocompleteTestPage, AutofocusDirective, BadgeDirective, BadgeNumberPipe, Base58, BaseEntityService, BaseGraphqlService, BaseGraphqlServiceOptions, BaseReferential, Beans, BooleanFormatPipe, BooleanTestPage, CORE_CONFIG_OPTIONS, CORE_TESTING_PAGES, CapitalizePipe, CellValueChangeListener, ChipsTestPage, Color, ColorScale, ComponentDirtyGuard, ConfigFragments, ConfigService, Configuration, CoreModule, CorePipesModule, CoreTestingModule, CryptoService, CsvUtils, DATE_ISO_PATTERN, DATE_MATCH_REGEXP, DATE_PATTERN, DATE_UNIX_MS_TIMESTAMP, DATE_UNIX_TIMESTAMP, DEFAULT_MENU_SHOW_WHEN, DEFAULT_PAGE_SIZE, DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_PLACEHOLDER_CHAR, DEFAULT_REQUIRED_COLUMNS, DateDiffDurationPipe, DateFormatPipe, DateFormatService, DateFromNowPipe, DateShortTestPage, DateTestPage, DateTimeTestPage, DateUtils, DebugComponent, Department, DepartmentToStringPipe, DragAndDropDirective, DurationPipe, DurationTestPage, ED25519_SEED_LENGTH, EMPTY_PLACEHOLDER_CHAR, ENTITIES_STORAGE_KEY_PREFIX, ENVIRONMENT, EmptyArrayPipe, EntitiesAsyncTableDataSource, EntitiesStorage, EntitiesTableDataSource, Entity, EntityClass, EntityClasses, EntityFilter, EntityFilterUtils, EntityMetadataComponent, EntityStore, EntityUtils, Environment, EnvironmentHttpLoader, EnvironmentLoader, ErrorCodes, EvenPipe, FileResponse, FileService, FileSizePipe, FilesUtils, FormArrayHelper, FormButtonsBarComponent, FormButtonsBarToken, FormErrorPipe, FormErrorTranslatePipe, FormErrorTranslator, FormFieldValuesHolder, FormGetArrayPipe, FormGetControlPipe, FormGetGroupPipe, FormGetPipe, FormGetValuePipe, GalleryTestPage, GeolocationUtils, GraphqlService, HAMMER_PRESS_TIME, HAMMER_TAP_TIME, HighlightPipe, HomePage, Hotkeys, HotkeysDialogComponent, IMAGE_DEFAULTS, IPosition, ImageAttachment, ImageAttachmentFilter, ImageAttachmentService, ImageGalleryModule, ImageGalleryTestingModule, ImageModule, ImageService, ImagesUtils, InMemoryEntitiesService, IsAllSelectedPipe, IsEmptySelectionPipe, IsLoginAccountPipe, IsMultipleSelectionPipe, IsNilOrBlankPipe, IsNilOrNaNPipe, IsNilPipe, IsNotAllSelectedPipe, IsNotEmptySelectionPipe, IsNotNilOrBlankPipe, IsNotNilOrNaNPipe, IsNotNilPipe, IsOnDeskPipe, IsOnFieldPipe, IsSelectedPipe, IsSingleSelectionPipe, JobModule, JobProgression, JobProgressionComponent, JobProgressionIcon, JobProgressionList, JobProgressionService, JobProgressionTestService, JobProgressionTestingPage, JobTestingModule, JobUtils, JsonUtils, KEYBOARD_HIDE_DELAY_MS, LAT_LONG_DEFAULT_MAX_DECIMALS, LAT_LONG_PATTERNS, LatLongFormatPipe, LatLongTestPage, LatitudeFormatPipe, LocalSettingsService, LogLevel, LogUtils, Logger, LoggingService, LoggingServiceModule, LongitudeFormatPipe, MAT_FORM_FIELD_DEFAULT_APPEARANCE, MAT_FORM_FIELD_DEFAULT_SUBSCRIPT_SIZING, MINIFY_ENTITY_FOR_LOCAL_STORAGE, MINIFY_ENTITY_FOR_POD, MOMENT_NO_TIME_PROPERTY, MapGetPipe, MapKeysPipe, MapValuesPipe, MaskitoPlaceholderPipe, MatAutocompleteConfigHolder, MatAutocompleteField, MatAutocompleteFieldUtils, MatBadgeTestPage, MatBooleanField, MatChipsField, MatColorPipe, MatCommonTestPage, MatDate, MatDateShort, MatDateTime, MatDuration, MatLatLongField, MatPaginatorI18n, MatStepperI18n, MatSwipeField, MaterialTestingModule, MathAbsPipe, MenuComponent, MenuItem, MenuItems, MenuOptions, MenuService, MenuTestingModule, MenuTestingPage, Message, MessageFilter, MessageForm, MessageModal, MessageModule, MessageService, MessageTypeList, MessageTypes, MimeTypes, ModalToolbarComponent, NETWORK_DEFAULT_CONNECTION_TIMEOUT, NamedFilter, NamedFilterFilter, NamedFilterSelector, NamedFilterSelectorTestingModule, NamedFilterSelectorTestingPage, NetworkService, NewTokenModal, NgInitDirective, NgVarDirective, NotEmptyArrayPipe, NumberFormatPipe, ObservableTestPage, OddPipe, OtherMenuTestingPage, PEER_URL_REGEXP, PRIORITIZED_AUTHORITIES, PUBKEY_REGEXP, Peer, Person, PersonFilter, PersonFragments, PersonService, PersonToStringPipe, PersonUtils, PersonValidatorService, PlatformService, ProgressBarService, ProgressInterceptor, PropertiesFormTestPage, PropertiesFormTestingModule, PropertyEntity, PropertyEntityFilter, PropertyEntityValidator, PropertyFormatPipe, PropertyGetPipe, RESERVED_END_COLUMNS, RESERVED_START_COLUMNS, Referential, ReferentialFilter, ReferentialRef, ReferentialToStringPipe, ReferentialUtils, ReferentialValidatorService, RegisterConfirmPage, RegisterForm, RegisterModal, ResizableComponent, ResizableDirective, ResizableModule, RxStateModule, SCRYPT_PARAMS, SETTINGS_DISPLAY_COLUMNS, SETTINGS_FILTER, SETTINGS_PAGE_SIZE, SETTINGS_SORTED_COLUMN, SETTINGS_STORAGE_KEY, SETTINGS_TRANSIENT_PROPERTIES, SHARED_MATERIAL_TESTING_PAGES, SHARED_STORAGE_TESTING_PAGES, SHARED_TESTING_PAGES, SOCIAL_TESTING_PAGES, SPACE_PLACEHOLDER_CHAR, SelectPeerModal, SelectionLengthPipe, ServerErrorCodes, SettingsPage, SharedAsyncValidators, SharedBadgeModule, SharedDebugModule, SharedDirectivesModule, SharedFormArrayValidators, SharedFormGroupValidators, SharedHotkeysModule, SharedMatAutocompleteModule, SharedMatBooleanModule, SharedMatChipsModule, SharedMatDateTimeModule, SharedMatDurationModule, SharedMatLatLongModule, SharedMatSwipeModule, SharedMaterialModule, SharedModule, SharedNamedFilterModule, SharedPipesModule, SharedRoutingModule, SharedTestingModule, SharedTestsPage, SharedTextFormModule, SharedToolbarModule, SharedValidators, SocialErrorCodes, SocialModule, SocialModuleOptionsToken, SocialTestingModule, Software, StartableService, StatusById, StatusIds, StatusList, StorageDrivers, StorageExplorerComponent, StorageExplorerModule, StorageExplorerTestingModule, StorageExplorerTestingRoutingModule, StorageService, StrIncludesPipe, StrLengthPipe, SubMenuTabDirective, SwipeTestPage, Table2TestPage, TableSelectColumnsComponent, TableTestPage, TableTestingModule, TableValidatorService, TextForm, TextFormTestingPage, TextPopover, TextPopoverTestingModule, TextPopoverTestingPage, ToStringPipe, ToastTestingModule, ToastTestingPage, Toasts, TokenScope, ToolbarComponent, ToolbarToken, TranslatablePipe, TranslateContextPipe, TranslateContextService, TreeItemEntityUtils, UploadFile, UploadFileComponent, UploadFilePopover, UploadFileTestingModule, UploadFileTestingPage, UriUtils, UserEventModule, UserEventNotificationIcon, UserEventNotificationList, UserEventTestService, UserEventTestingModule, UserEventTestingPage, UserSettings, UserToken, UserTokenTable, UsersPage, ValueFormatPipe, accountToString, adaptValueToControl, addValueInArray, arrayDistinct, arrayResize, arraySize, asInputElement, booleanToString, canHaveFocus, capitalizeFirstLetter, chainPromises, changeCaseToUnderscore, clearValueInArray, collectByProperty, copyEntity2Form, createPromiseEvent, createPromiseEventEmitter, departmentToString, departmentsToString, disableControls, emitPromiseEvent, entityToString, equals, equalsOrNil, escapeRegExp, fadeInAnimation, fadeInOutAnimation, fadeInSlowAnimation, filterFalse, filterFormErrors, filterFormErrorsByPath, filterFormErrorsByPrefix, filterNotNil, filterNumberInput, filterTrue, findParentWithClass, firstArrayValue, firstFalse, firstFalsePromise, firstNotNil, firstNotNilPromise, firstTrue, firstTruePromise, focusInput, focusNextInput, focusPreviousInput, formatLatitude, formatLongitude, fromDateISOString, fromScrollEndEvent, fromUnixMsTimestamp, fromUnixTimestamp, getCaretPosition, getColorContrast, getColorShade, getColorTint, getConnectionType, getControlFromPath, getFocusableInputElements, getFormErrors, getFormValueFromEntity, getProperty, getPropertyByPath, getPropertyByPathAsString, getRandomImage, hexToRgb, hexToRgbArray, initArrayControlsFromValues, isAndroid, isBlankString, isCapacitor, isControlHasInput, isEmptyArray, isIOS, isInputElement, isInstanceOf, isInt, isMobile, isNil, isNilOrBlank, isNilOrNaN, isNotEmptyArray, isNotNil, isNotNilBoolean, isNotNilOrBlank, isNotNilOrNaN, isNotNilString, isNumber, isNumberRange, isOnFieldMode, isProgressEvent, isResponseEvent, isStartableService, isTouchUi, isWindows, joinProperties, joinPropertiesPath, lastArrayValue, logFormErrors, markAllAsTouched, markAsUntouched, markControlAsTouched, markFormGroupAsTouched, maskitoPrefixPlugin, matchMedia, matchUpperCase, mergeLoadResult, mixHex, moveInputCaretToSeparator, newArray, noTrailingSlash, notNilOrDefault, nullIfNilOrBlank, nullIfUndefined, parseLatitudeOrLongitude, propertiesPathComparator, propertyComparator, propertyPathComparator, referentialToString, referentialsToString, remove, removeAll, removeDiacritics, removeDuplicatesFromArray, removeEnd, removeValueInArray, replaceAll, resetCalculatedValue, resizeArray, rgbArrayToHex, rgbToHex, round, scrollFactory, selectInputContent, selectInputRange, setCalculatedValue, setPropertyByPath, setTabIndex, sleep, slideInAnimation, slideInOutAnimation, slideUpDownAnimation, sort, splitById, splitByProperty, startsWithUpperCase, suggestFromArray, suggestFromStringArray, tabindexComparator, testUserAgent, toBoolean, toDateISOString, toDuration, toFloat, toInt, toNotNil, toNumber, trimEmptyToNull, uncapitalizeFirstLetter, undefinedIfNull, underscoreToChangeCase, updateValueAndValidity, waitFor, waitForFalse, waitForTrue, waitIdle, waitWhilePending };
42932
+ export { APP_ABOUT_DEVELOPERS, APP_ABOUT_PARTNERS, APP_CONFIG_OPTIONS, APP_DEBUG_DATA_SERVICE, APP_FORM_ERROR_I18N_KEYS, APP_GRAPHQL_FRAGMENTS, APP_GRAPHQL_TYPE_POLICIES, APP_HOME_BUTTONS, APP_JOB_PROGRESSION_SERVICE, APP_LOCALES, APP_LOCAL_SETTINGS, APP_LOCAL_SETTINGS_OPTIONS, APP_LOCAL_STORAGE_TYPE_POLICIES, APP_LOGGING_SERVICE, APP_MENU_ITEMS, APP_MENU_OPTIONS, APP_NAMED_FILTER_SERVICE, APP_PROGRESS_BAR_SERVICE, APP_SETTINGS_MENU_ITEMS, APP_STORAGE, APP_STORAGE_EXPLORER_PROTECTED_KEYS, APP_TESTING_PAGES, APP_USER_EVENT_LIST_INFINITE_SCROLL_THRESHOLD, APP_USER_EVENT_SERVICE, APP_USER_SETTINGS_OPTIONS, APP_USER_TOKEN_SCOPES, AboutModal, AbstractNamedFilterService, AbstractSelectionModelPipe, AbstractTableSelectionPipe, AbstractUserEventService, Account, AccountPage, AccountService, AccountToStringPipe, AccountUtils, ActionsColumnComponent, AdminModule, AdminRoutingModule, AdminUsersModule, Alerts, AndroidOsEnvironment, AppAboutModalModule, AppAccountModule, AppAsyncTable, AppAuthForm, AppAuthModal, AppAuthModule, AppEditor, AppEditorOptions, AppEntityEditor, AppEntityEditorModal, AppEntityEditorModalOptions, AppEntityFormModule, AppForm, AppFormArray, AppFormButtonsBarModule, AppFormContainer, AppFormField, AppFormModule, AppFormProvider, AppFormUtils, AppGestureConfig, AppGraphQLModule, AppHelpModal, AppHomePageModule, AppIconComponent, AppIconModule, AppImageGalleryComponent, AppInMemoryTable, AppInstallUpgradeCard, AppInstallUpgradeCardModule, AppListForm, AppListFormModule, AppLoadingSpinner, AppMenuModule, AppNullForm, AppPropertiesForm, AppPropertiesFormModule, AppPropertiesTable, AppRegisterModule, AppRowField, AppSelectPeerModule, AppSettingsPageModule, AppTabEditor, AppTabEditorOptions, AppTable, AppTableModule, AppTableUtils, AppTextPopoverModule, AppUpdateOfflineModeCard, AppUpdateOfflineModeCardModule, AppValidatorService, ArrayFilterPipe, ArrayFirstPipe, ArrayFormTestPage, ArrayIncludesPipe, ArrayJoinPipe, ArrayLastPipe, ArrayLengthPipe, ArrayPluckPipe, AsAnyPipe, AsArrayPipe, AsFloatLabelTypePipe, AsObservablePipe, AudioProvider, AudioTestingModule, AudioTestingPage, AuthGuardService, AutoTitleDirective, AutocompleteTestPage, AutofocusDirective, BadgeDirective, BadgeNumberPipe, Base58, BaseEntityService, BaseGraphqlService, BaseGraphqlServiceOptions, BaseReferential, Beans, BooleanFormatPipe, BooleanTestPage, CORE_CONFIG_OPTIONS, CORE_TESTING_PAGES, CapitalizePipe, CellValueChangeListener, ChipsTestPage, Color, ColorScale, ComponentDirtyGuard, ConfigFragments, ConfigService, Configuration, CoreModule, CorePipesModule, CoreTestingModule, CryptoService, CsvUtils, DATE_ISO_PATTERN, DATE_MATCH_REGEXP, DATE_PATTERN, DATE_UNIX_MS_TIMESTAMP, DATE_UNIX_TIMESTAMP, DEFAULT_MENU_SHOW_WHEN, DEFAULT_PAGE_SIZE, DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_PLACEHOLDER_CHAR, DEFAULT_REQUIRED_COLUMNS, DateDiffDurationPipe, DateFormatPipe, DateFormatService, DateFromNowPipe, DateShortTestPage, DateTestPage, DateTimeTestPage, DateUtils, DebugComponent, Department, DepartmentToStringPipe, DragAndDropDirective, DurationPipe, DurationTestPage, ED25519_SEED_LENGTH, EMPTY_PLACEHOLDER_CHAR, ENTITIES_STORAGE_KEY_PREFIX, ENVIRONMENT, EmptyArrayPipe, EntitiesAsyncTableDataSource, EntitiesStorage, EntitiesTableDataSource, Entity, EntityClass, EntityClasses, EntityFilter, EntityFilterUtils, EntityMetadataComponent, EntityStore, EntityUtils, Environment, EnvironmentHttpLoader, EnvironmentLoader, ErrorCodes, EvenPipe, FileResponse, FileService, FileSizePipe, FilesUtils, FormArrayHelper, FormArrayTestModule, FormButtonsBarComponent, FormButtonsBarToken, FormErrorPipe, FormErrorTranslatePipe, FormErrorTranslator, FormFieldValuesHolder, FormGetArrayPipe, FormGetControlPipe, FormGetGroupPipe, FormGetPipe, FormGetValuePipe, GalleryTestPage, GeolocationUtils, GraphqlService, HAMMER_PRESS_TIME, HAMMER_TAP_TIME, HighlightPipe, HomePage, Hotkeys, HotkeysDialogComponent, IMAGE_DEFAULTS, IPosition, ImageAttachment, ImageAttachmentFilter, ImageAttachmentService, ImageGalleryModule, ImageGalleryTestingModule, ImageModule, ImageService, ImagesUtils, InMemoryEntitiesService, IsAllSelectedPipe, IsEmptySelectionPipe, IsLoginAccountPipe, IsMultipleSelectionPipe, IsNilOrBlankPipe, IsNilOrNaNPipe, IsNilPipe, IsNotAllSelectedPipe, IsNotEmptySelectionPipe, IsNotNilOrBlankPipe, IsNotNilOrNaNPipe, IsNotNilPipe, IsOnDeskPipe, IsOnFieldPipe, IsSelectedPipe, IsSingleSelectionPipe, JobModule, JobProgression, JobProgressionComponent, JobProgressionIcon, JobProgressionList, JobProgressionService, JobProgressionTestService, JobProgressionTestingPage, JobTestingModule, JobUtils, JsonUtils, KEYBOARD_HIDE_DELAY_MS, LAT_LONG_DEFAULT_MAX_DECIMALS, LAT_LONG_PATTERNS, LatLongFormatPipe, LatLongTestPage, LatitudeFormatPipe, LocalSettingsService, LogLevel, LogUtils, Logger, LoggingService, LoggingServiceModule, LongitudeFormatPipe, MAT_FORM_FIELD_DEFAULT_APPEARANCE, MAT_FORM_FIELD_DEFAULT_SUBSCRIPT_SIZING, MINIFY_ENTITY_FOR_LOCAL_STORAGE, MINIFY_ENTITY_FOR_POD, MOMENT_NO_TIME_PROPERTY, MapGetPipe, MapKeysPipe, MapValuesPipe, MaskitoPlaceholderPipe, MatAutocompleteConfigHolder, MatAutocompleteField, MatAutocompleteFieldUtils, MatBadgeTestPage, MatBooleanField, MatChipsField, MatColorPipe, MatCommonTestPage, MatDate, MatDateShort, MatDateTime, MatDuration, MatLatLongField, MatPaginatorI18n, MatStepperI18n, MatSwipeField, MaterialTestingModule, MathAbsPipe, MenuComponent, MenuItem, MenuItems, MenuOptions, MenuService, MenuTestingModule, MenuTestingPage, Message, MessageFilter, MessageForm, MessageModal, MessageModule, MessageService, MessageTypeList, MessageTypes, MimeTypes, ModalToolbarComponent, NETWORK_DEFAULT_CONNECTION_TIMEOUT, NamedFilter, NamedFilterFilter, NamedFilterSelector, NamedFilterSelectorTestingModule, NamedFilterSelectorTestingPage, NetworkService, NewTokenModal, NgInitDirective, NgVarDirective, NotEmptyArrayPipe, NumberFormatPipe, ObservableTestPage, OddPipe, OtherMenuTestingPage, PEER_URL_REGEXP, PRIORITIZED_AUTHORITIES, PUBKEY_REGEXP, Peer, Person, PersonFilter, PersonFragments, PersonService, PersonToStringPipe, PersonUtils, PersonValidatorService, PlatformService, ProgressBarService, ProgressInterceptor, PropertiesFormTestPage, PropertiesFormTestingModule, PropertyEntity, PropertyEntityFilter, PropertyEntityValidator, PropertyFormatPipe, PropertyGetPipe, RESERVED_END_COLUMNS, RESERVED_START_COLUMNS, Referential, ReferentialFilter, ReferentialRef, ReferentialToStringPipe, ReferentialUtils, ReferentialValidatorService, RegisterConfirmPage, RegisterForm, RegisterModal, ResizableComponent, ResizableDirective, ResizableModule, RxStateModule, SCRYPT_PARAMS, SETTINGS_DISPLAY_COLUMNS, SETTINGS_FILTER, SETTINGS_PAGE_SIZE, SETTINGS_SORTED_COLUMN, SETTINGS_STORAGE_KEY, SETTINGS_TRANSIENT_PROPERTIES, SHARED_MATERIAL_TESTING_PAGES, SHARED_STORAGE_TESTING_PAGES, SHARED_TESTING_PAGES, SOCIAL_TESTING_PAGES, SPACE_PLACEHOLDER_CHAR, SelectPeerModal, SelectionLengthPipe, ServerErrorCodes, SettingsPage, SharedAsyncValidators, SharedBadgeModule, SharedDebugModule, SharedDirectivesModule, SharedFormArrayValidators, SharedFormGroupValidators, SharedHotkeysModule, SharedMatAutocompleteModule, SharedMatBooleanModule, SharedMatChipsModule, SharedMatDateTimeModule, SharedMatDurationModule, SharedMatLatLongModule, SharedMatSwipeModule, SharedMaterialModule, SharedModule, SharedNamedFilterModule, SharedPipesModule, SharedRoutingModule, SharedTestingModule, SharedTestsPage, SharedTextFormModule, SharedToolbarModule, SharedValidators, SocialErrorCodes, SocialModule, SocialModuleOptionsToken, SocialTestingModule, Software, StartableService, StatusById, StatusIds, StatusList, StorageDrivers, StorageExplorerComponent, StorageExplorerModule, StorageExplorerTestingModule, StorageExplorerTestingRoutingModule, StorageService, StrIncludesPipe, StrLengthPipe, SubMenuTabDirective, SwipeTestPage, Table2TestPage, TableSelectColumnsComponent, TableTestPage, TableTestingModule, TableValidatorService, TextForm, TextFormTestingPage, TextPopover, TextPopoverTestingModule, TextPopoverTestingPage, ToStringPipe, ToastTestingModule, ToastTestingPage, Toasts, TokenScope, ToolbarComponent, ToolbarToken, TranslatablePipe, TranslateContextPipe, TranslateContextService, TreeItemEntityUtils, UploadFile, UploadFileComponent, UploadFilePopover, UploadFileTestingModule, UploadFileTestingPage, UriUtils, UserEventModule, UserEventNotificationIcon, UserEventNotificationList, UserEventTestService, UserEventTestingModule, UserEventTestingPage, UserSettings, UserToken, UserTokenTable, UsersPage, ValueFormatPipe, accountToString, adaptValueToControl, addValueInArray, arrayDistinct, arrayResize, arraySize, asInputElement, booleanToString, canHaveFocus, capitalizeFirstLetter, chainPromises, changeCaseToUnderscore, clearValueInArray, collectByProperty, copyEntity2Form, createPromiseEvent, createPromiseEventEmitter, departmentToString, departmentsToString, disableControls, emitPromiseEvent, entityToString, equals, equalsOrNil, escapeRegExp, fadeInAnimation, fadeInOutAnimation, fadeInSlowAnimation, filterFalse, filterFormErrors, filterFormErrorsByPath, filterFormErrorsByPrefix, filterNotNil, filterNumberInput, filterTrue, findParentWithClass, firstArrayValue, firstFalse, firstFalsePromise, firstNotNil, firstNotNilPromise, firstTrue, firstTruePromise, focusInput, focusNextInput, focusPreviousInput, formatLatitude, formatLongitude, fromDateISOString, fromScrollEndEvent, fromUnixMsTimestamp, fromUnixTimestamp, getCaretPosition, getColorContrast, getColorShade, getColorTint, getConnectionType, getControlFromPath, getFocusableInputElements, getFormErrors, getFormValueFromEntity, getProperty, getPropertyByPath, getPropertyByPathAsString, getRandomImage, hexToRgb, hexToRgbArray, initArrayControlsFromValues, isAndroid, isBlankString, isCapacitor, isControlHasInput, isEmptyArray, isIOS, isInputElement, isInstanceOf, isInt, isMobile, isNil, isNilOrBlank, isNilOrNaN, isNotEmptyArray, isNotNil, isNotNilBoolean, isNotNilOrBlank, isNotNilOrNaN, isNotNilString, isNumber, isNumberRange, isOnFieldMode, isProgressEvent, isResponseEvent, isStartableService, isTouchUi, isWindows, joinProperties, joinPropertiesPath, lastArrayValue, logFormErrors, markAllAsTouched, markAsUntouched, markControlAsTouched, markFormGroupAsTouched, maskitoPrefixPlugin, matchMedia, matchUpperCase, mergeLoadResult, mixHex, moveInputCaretToSeparator, newArray, noTrailingSlash, notNilOrDefault, nullIfNilOrBlank, nullIfUndefined, parseLatitudeOrLongitude, propertiesPathComparator, propertyComparator, propertyPathComparator, referentialToString, referentialsToString, remove, removeAll, removeDiacritics, removeDuplicatesFromArray, removeEnd, removeValueInArray, replaceAll, resetCalculatedValue, resizeArray, rgbArrayToHex, rgbToHex, round, scrollFactory, selectInputContent, selectInputRange, setCalculatedValue, setPropertyByPath, setTabIndex, sleep, slideInAnimation, slideInOutAnimation, slideUpDownAnimation, sort, splitById, splitByProperty, startsWithUpperCase, suggestFromArray, suggestFromStringArray, tabindexComparator, testUserAgent, toBoolean, toDateISOString, toDuration, toFloat, toInt, toNotNil, toNumber, trimEmptyToNull, uncapitalizeFirstLetter, undefinedIfNull, underscoreToChangeCase, updateValueAndValidity, waitFor, waitForFalse, waitForTrue, waitIdle, waitWhilePending };
42808
42933
  //# sourceMappingURL=sumaris-net.ngx-components.mjs.map