@sumaris-net/ngx-components 1.6.0 → 1.6.4

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.
Files changed (25) hide show
  1. package/bundles/sumaris-net.ngx-components.umd.js +572 -218
  2. package/bundles/sumaris-net.ngx-components.umd.js.map +1 -1
  3. package/bundles/sumaris-net.ngx-components.umd.min.js +2 -2
  4. package/bundles/sumaris-net.ngx-components.umd.min.js.map +1 -1
  5. package/doc/changelog.md +8 -0
  6. package/esm2015/src/app/core/auth/modal/modal-auth.js +21 -9
  7. package/esm2015/src/app/core/table/entities-table-datasource.class.js +35 -1
  8. package/esm2015/src/app/shared/form/field.model.js +1 -1
  9. package/esm2015/src/app/shared/material/autocomplete/material.autocomplete.js +103 -102
  10. package/esm2015/src/app/shared/material/autocomplete/testing/autocomplete.test.js +2 -2
  11. package/esm2015/src/app/shared/material/chips/material.chips.js +354 -81
  12. package/esm2015/src/app/shared/material/chips/testing/chips.test.js +13 -6
  13. package/esm2015/src/app/shared/material/swipe/material.swipe.js +5 -5
  14. package/esm2015/src/app/shared/material/swipe/testing/swipe.test.js +2 -2
  15. package/fesm2015/sumaris-net.ngx-components.js +521 -196
  16. package/fesm2015/sumaris-net.ngx-components.js.map +1 -1
  17. package/package.json +1 -1
  18. package/src/app/core/auth/modal/modal-auth.d.ts +12 -5
  19. package/src/app/core/table/entities-table-datasource.class.d.ts +6 -1
  20. package/src/app/shared/form/field.model.d.ts +2 -1
  21. package/src/app/shared/material/autocomplete/material.autocomplete.d.ts +35 -35
  22. package/src/app/shared/material/chips/material.chips.d.ts +38 -10
  23. package/src/app/shared/material/chips/testing/chips.test.d.ts +1 -1
  24. package/src/app/shared/material/swipe/material.swipe.d.ts +2 -2
  25. package/sumaris-net.ngx-components.metadata.json +1 -1
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, EventEmitter, forwardRef, Component, ChangeDetectionStrategy, ChangeDetectorRef, Optional, Input, HostBinding, Output, ViewChild, Pipe, Injectable, Directive, Inject, NgModule, ElementRef, ViewChildren, HostListener, ComponentFactoryResolver, ApplicationRef, Injector, Renderer2 } from '@angular/core';
2
+ import { InjectionToken, EventEmitter, Component, forwardRef, ChangeDetectionStrategy, ChangeDetectorRef, Optional, Input, Output, ViewChild, HostBinding, Pipe, Injectable, Directive, Inject, NgModule, ElementRef, ViewChildren, HostListener, ComponentFactoryResolver, ApplicationRef, Injector, Renderer2, ViewEncapsulation } from '@angular/core';
3
3
  import * as i1$3 from '@angular/common';
4
4
  import { CommonModule, DOCUMENT, Location } from '@angular/common';
5
5
  import { CdkTableModule } from '@angular/cdk/table';
@@ -36,7 +36,7 @@ import { MatAutocomplete, MatAutocompleteTrigger, MatAutocompleteModule, MAT_AUT
36
36
  import { __awaiter, __decorate } from 'tslib';
37
37
  import * as i1$6 from '@angular/forms';
38
38
  import { NG_VALUE_ACCESSOR, FormGroupDirective, AbstractControl, FormGroup, FormArray, FormControl, ReactiveFormsModule, Validators, FormBuilder } from '@angular/forms';
39
- import { timer, merge, fromEvent, Subscription, BehaviorSubject, Subject, isObservable, noop as noop$8, Observable, defer, of, from, combineLatest, EMPTY } from 'rxjs';
39
+ import { timer, merge, fromEvent, BehaviorSubject, Subscription, Subject, isObservable, noop as noop$8, Observable, defer, of, from, combineLatest, EMPTY } from 'rxjs';
40
40
  import { filter, first, map, takeUntil, switchMap, startWith, debounceTime, takeWhile, tap, distinctUntilChanged, mergeMap, catchError, throttleTime, skip } from 'rxjs/operators';
41
41
  import * as i1$1 from '@ngx-translate/core';
42
42
  import { TranslateService, TranslateModule } from '@ngx-translate/core';
@@ -976,11 +976,10 @@ class MatAutocompleteConfigHolder {
976
976
  const attributesOrFn = attributes.map((a, index) => typeof a === 'function' && options.attributes[index] || a);
977
977
  const filter = Object.assign({ searchAttribute: attributes.length === 1 ? attributes[0] : undefined, searchAttributes: attributes.length > 1 ? attributes : undefined }, options.filter);
978
978
  const displayWith = options.displayWith || ((obj) => obj && joinPropertiesPath(obj, attributes));
979
- const compareWith = options.compareWith || ((o1, o2) => o1 && o2 && o1.id === o2.id);
979
+ const compareWith = options.equals || ((o1, o2) => o1 && o2 && o1.id === o2.id);
980
980
  const config = Object.assign(Object.assign({}, options), { attributes: attributesOrFn, suggestFn,
981
981
  filter,
982
- displayWith,
983
- compareWith });
982
+ displayWith, equals: compareWith });
984
983
  this.fields[fieldName] = config;
985
984
  return config;
986
985
  }
@@ -991,11 +990,6 @@ class MatAutocompleteConfigHolder {
991
990
  return this.fields[fieldName] || this.add(fieldName);
992
991
  }
993
992
  }
994
- const DEFAULT_VALUE_ACCESSOR$6 = {
995
- provide: NG_VALUE_ACCESSOR,
996
- useExisting: forwardRef(() => MatAutocompleteField),
997
- multi: true
998
- };
999
993
  const noop$7 = () => { };
1000
994
  const ɵ0$a = noop$7;
1001
995
  class MatAutocompleteField {
@@ -1004,14 +998,6 @@ class MatAutocompleteField {
1004
998
  this.formGroupDir = formGroupDir;
1005
999
  this._onChangeCallback = noop$7;
1006
1000
  this._onTouchedCallback = noop$7;
1007
- this._subscription = new Subscription();
1008
- this._filter$ = new BehaviorSubject(undefined);
1009
- this._reload$ = new Subject();
1010
- this._readonly = false;
1011
- this.$inputItems = new BehaviorSubject(undefined);
1012
- this.$filteredItems = new BehaviorSubject(undefined);
1013
- this.displayValue = '';
1014
- this._fetchMore$ = new EventEmitter();
1015
1001
  this.logPrefix = '[mat-autocomplete] ';
1016
1002
  this.formControlName = null;
1017
1003
  this.required = false;
@@ -1024,13 +1010,21 @@ class MatAutocompleteField {
1024
1010
  this.fetchMoreThreshold = '15%';
1025
1011
  this.suggestLengthThreshold = null;
1026
1012
  this.showLoadingSpinner = true;
1013
+ this.clicked = new EventEmitter();
1014
+ this.blurred = new EventEmitter();
1015
+ this.focused = new EventEmitter();
1016
+ this.dropButtonClick = new EventEmitter(true);
1017
+ this.keydownEscape = new EventEmitter();
1018
+ this.keyupEnter = new EventEmitter();
1027
1019
  this.animationsDisabled = true;
1028
- this.onClick = new EventEmitter();
1029
- this.onBlur = new EventEmitter();
1030
- this.onFocus = new EventEmitter();
1031
- this.onDropButtonClick = new EventEmitter(true);
1032
- this.onKeydownEscape = new EventEmitter();
1033
- this.onKeyupEnter = new EventEmitter();
1020
+ this._readonly = false;
1021
+ this.$inputItems = new BehaviorSubject(undefined);
1022
+ this.$filteredItems = new BehaviorSubject(undefined);
1023
+ this.displayValue = '';
1024
+ this._fetchMore$ = new EventEmitter();
1025
+ this._subscription = new Subscription();
1026
+ this._$filter = new BehaviorSubject(undefined);
1027
+ this._reload$ = new Subject();
1034
1028
  }
1035
1029
  get itemCount() {
1036
1030
  return this._itemCount;
@@ -1039,14 +1033,14 @@ class MatAutocompleteField {
1039
1033
  return this._onFetchMoreCallback && this._moreItemsCount > 0;
1040
1034
  }
1041
1035
  set filter(value) {
1042
- if (value !== this._filter$.value) {
1036
+ if (value !== this._$filter.value) {
1043
1037
  // DEBUG
1044
1038
  //console.debug(this.logPrefix + " Setting filter:", value);
1045
- this._filter$.next(value);
1039
+ this._$filter.next(value);
1046
1040
  }
1047
1041
  }
1048
1042
  get filter() {
1049
- return this._filter$.value;
1043
+ return this._$filter.value;
1050
1044
  }
1051
1045
  set readonly(value) {
1052
1046
  this._readonly = value;
@@ -1109,6 +1103,7 @@ class MatAutocompleteField {
1109
1103
  this.displayColumnSizes = this.displayColumnSizes || this.config.columnSizes;
1110
1104
  this.displayColumnNames = this.displayColumnNames || this.config.columnNames;
1111
1105
  this.displayWith = this.displayWith || this.config.displayWith;
1106
+ this.equals = this.equals || this.config.equals;
1112
1107
  this.mobile = toBoolean(this.mobile, this.config.mobile);
1113
1108
  this.suggestLengthThreshold = toNumber(this.suggestLengthThreshold, this.config.suggestLengthThreshold || 0);
1114
1109
  this.showAllOnFocus = toBoolean(this.showAllOnFocus, toBoolean(this.config.showAllOnFocus, this.suggestLengthThreshold <= 0));
@@ -1123,20 +1118,18 @@ class MatAutocompleteField {
1123
1118
  this.displayColumnSizes = this.displayColumnSizes
1124
1119
  // if only column: auto size
1125
1120
  || (this.displayAttributes.length === 1 && [undefined])
1126
- || this.displayAttributes.map(attr =>
1127
- // If label, set col size = 2
1128
- (attr && attr.endsWith('label')) ? 2 :
1129
- // If rankOrder => col size = 1
1130
- ((attr && attr.endsWith('rankOrder')) ? 1
1131
- // Else => auto size
1121
+ // Computed column size:
1122
+ // - if label then col size = 2
1123
+ // - if rankOrder then col size = 1
1124
+ // - ese => auto size
1125
+ || this.displayAttributes.map(attr => (attr && attr.endsWith('label')) ? 2
1126
+ : ((attr && attr.endsWith('rankOrder')) ? 1
1132
1127
  : undefined));
1133
1128
  this.displayColumnNames = this.displayAttributes.map((attr, index) => this.displayColumnNames && this.displayColumnNames[index] ||
1134
1129
  (this.i18nPrefix + changeCaseToUnderscore(attr).toUpperCase()));
1135
1130
  this.mobile = toBoolean(this.mobile, false);
1136
1131
  this.searchable = !this.mobile && !this.multiple;
1137
- // Default comparator (only need when using mat-select)
1138
- if (!this.searchable && !this.compareWith)
1139
- this.compareWith = (o1, o2) => o1 && o2 && o1.id === o2.id;
1132
+ this.equals = this.equals || ((o1, o2) => o1 && o2 && o1.id === o2.id);
1140
1133
  const classList = (_b = this.classList) === null || _b === void 0 ? void 0 : _b.split(' ');
1141
1134
  this.panelWidth = this.panelWidth || (classList && ((classList.includes('min-width-medium') && '300px')
1142
1135
  || (classList.includes('min-width-large') && '400px')
@@ -1151,7 +1144,7 @@ class MatAutocompleteField {
1151
1144
  if (!this.suggestFn) {
1152
1145
  const suggestFromArrayFn = (value, filter) => __awaiter(this, void 0, void 0, function* () {
1153
1146
  // DEBUG
1154
- //console.debug(this.logPrefix + " Calling suggestFromArray with value=", value);
1147
+ //console.debug(this.logPrefix + ' Calling suggestFromArray with value=', value);
1155
1148
  return suggestFromArray(this.$inputItems.value, value, Object.assign({ searchAttributes: this.displayAttributes }, filter));
1156
1149
  });
1157
1150
  // Wait (once) that items are loaded, then call suggest from array fn
@@ -1169,10 +1162,18 @@ class MatAutocompleteField {
1169
1162
  }
1170
1163
  else if (!this.searchable) {
1171
1164
  // Manually fill input items, from the given suggest function
1172
- this._subscription.add(this._filter$.pipe(startWith(this.filter), debounceTime(250), takeWhile((_) => !this.searchable), // Close subscription, when enabling search (no more mat-select)
1165
+ this._subscription.add(this._$filter.pipe(startWith(this.filter), debounceTime(250), takeWhile((_) => !this.searchable), // Close subscription, when enabling search (no more mat-select)
1173
1166
  switchMap((filter) => this.suggest('*', filter)))
1174
1167
  .subscribe(items => this.$inputItems.next(items)));
1175
1168
  }
1169
+ // Send value to suggest when :
1170
+ // - is nil and '*', and no min length threshold
1171
+ // - is not nil and not a string type (e.g. object)
1172
+ // - is not nil and is a string, and has enough character
1173
+ const thresholdFilterFn = (value) => (isNilOrBlank(value) || (value === '*')
1174
+ ? (this.suggestLengthThreshold <= 0)
1175
+ : (!(typeof value === 'string')
1176
+ || value.length >= this.suggestLengthThreshold));
1176
1177
  const filteredItemsChanges = merge(
1177
1178
  // Reload event (no distinctUntilChanged() pipe, to force reload even if same value)
1178
1179
  this._reload$,
@@ -1180,11 +1181,7 @@ class MatAutocompleteField {
1180
1181
  this.formControl.valueChanges
1181
1182
  .pipe(filter(value => this.enabled && this.searchable && isNotNilString(value)),
1182
1183
  // Mark as loading (to show loading spinner) - earlier as possible, so before the debounce time
1183
- tap(value => {
1184
- if (this.showLoadingSpinner) {
1185
- this.markAsLoading();
1186
- }
1187
- }),
1184
+ tap(_ => this.showLoadingSpinner && this.markAsLoading()),
1188
1185
  // Filter if not enough character
1189
1186
  filter(value => value.length >= this.suggestLengthThreshold),
1190
1187
  // Wait a debounce time
@@ -1210,28 +1207,24 @@ class MatAutocompleteField {
1210
1207
  // DEBUG
1211
1208
  //tap(value => console.debug(this.logPrefix + ' Received an object value: ', value))
1212
1209
  ),
1210
+ // On dropdown button click (no distinctUntilChanged(), nor debounceTime)
1211
+ this.dropButtonClick
1212
+ .pipe(filter(event => this.searchable && this.enabled && (!event || !event.defaultPrevented)), map(_ => this.showAllOnFocus ? '*' : this.formControl.value),
1213
+ // Make enough characters
1214
+ filter(thresholdFilterFn)),
1213
1215
  // Merge some observables, with a common distinctUntilChanged() pipe, and NO debounceTime
1214
1216
  merge(
1215
1217
  // Focus or click => Load all
1216
- merge(this.onFocus, this.onClick)
1217
- .pipe(filter(_ => this.enabled && this.searchable), map(_ => this.showAllOnFocus ? '*' : this.formControl.value)),
1218
- // On dropdown button click
1219
- this.onDropButtonClick
1220
- .pipe(filter(event => this.enabled && this.searchable && (!event || !event.defaultPrevented)), map(_ => this.showAllOnFocus ? '*' : this.formControl.value)), merge(this.$inputItems, this._filter$)
1221
- .pipe(filter(event => this.enabled && this.searchable), map(_ => this.formControl.value)))
1218
+ merge(this.focused, this.clicked)
1219
+ .pipe(filter(_ => this.enabled && this.searchable), map(_ => this.showAllOnFocus ? '*' : this.formControl.value)), merge(this.$inputItems, this._$filter)
1220
+ .pipe(filter(_ => this.searchable && this.enabled), map(_ => this.formControl.value)))
1222
1221
  .pipe(
1223
- // Start to suggest when :
1224
- // - '*'
1225
- // - is nil, and there is no min length threshold
1226
- // - is not nil and not a string type (e.g. object)
1227
- // - is not nil and is a string, and has enough character
1228
- filter(value => value === '*'
1229
- || (isNil(value)
1230
- ? (this.suggestLengthThreshold <= 0)
1231
- : (!(typeof value === 'string')
1232
- || value.length >= this.suggestLengthThreshold))),
1222
+ // Make enough characters
1223
+ filter(thresholdFilterFn),
1233
1224
  // Distinguish changes
1234
- distinctUntilChanged()))
1225
+ distinctUntilChanged(),
1226
+ // DEBUG
1227
+ tap(value => console.debug(this.logPrefix + ' Received update event: ', value))))
1235
1228
  .pipe(
1236
1229
  // Suggest values
1237
1230
  mergeMap(value => this.suggest(value, this.filter)),
@@ -1257,7 +1250,7 @@ class MatAutocompleteField {
1257
1250
  // Make sure control value is inside (mat-select)
1258
1251
  if (items && !this.searchable) {
1259
1252
  const value = this.formControl.value;
1260
- if (isNotNil(value) && typeof value === 'object' && items.findIndex(item => this.compareWith(item, value)) === -1) {
1253
+ if (isNotNil(value) && typeof value === 'object' && items.findIndex(item => this.equals(item, value)) === -1) {
1261
1254
  // If form value is missing: add it to the list
1262
1255
  items = items.concat(value);
1263
1256
  }
@@ -1266,7 +1259,7 @@ class MatAutocompleteField {
1266
1259
  this.$filteredItems.next(items);
1267
1260
  }));
1268
1261
  // Applying implicit value, on blur
1269
- this._subscription.add(this.onBlur
1262
+ this._subscription.add(this.blurred
1270
1263
  .pipe(
1271
1264
  // Skip if not searchable
1272
1265
  filter(_ => this.searchable), map(_ => this.formControl.value))
@@ -1280,12 +1273,14 @@ class MatAutocompleteField {
1280
1273
  this.formControl.markAsPending({ emitEvent: false, onlySelf: true });
1281
1274
  this.formControl.updateValueAndValidity({ emitEvent: false, onlySelf: true });
1282
1275
  this.checkIfTouched();
1276
+ this.$filteredItems.next([this._implicitValue]);
1283
1277
  }
1284
1278
  else if (isNilOrBlank(value)) {
1285
1279
  this.writeValue(null);
1286
1280
  this.formControl.markAsPending({ emitEvent: false, onlySelf: true });
1287
1281
  this.formControl.updateValueAndValidity({ emitEvent: false, onlySelf: true });
1288
1282
  this.checkIfTouched();
1283
+ this.$filteredItems.next(undefined);
1289
1284
  }
1290
1285
  this._implicitValue = null; // reset the implicit value
1291
1286
  }));
@@ -1296,7 +1291,7 @@ class MatAutocompleteField {
1296
1291
  distinctUntilChanged())
1297
1292
  // Update component state
1298
1293
  .subscribe(() => this.checkIfTouched()));
1299
- this._subscription.add(this.onKeydownEscape.subscribe(event => {
1294
+ this._subscription.add(this.keydownEscape.subscribe(event => {
1300
1295
  if (this.isOpen) {
1301
1296
  event === null || event === void 0 ? void 0 : event.stopPropagation();
1302
1297
  event === null || event === void 0 ? void 0 : event.preventDefault();
@@ -1315,20 +1310,22 @@ class MatAutocompleteField {
1315
1310
  this._openedSubscription.unsubscribe();
1316
1311
  }
1317
1312
  this.$inputItems.complete();
1313
+ this.$inputItems.unsubscribe();
1318
1314
  this.$filteredItems.complete();
1315
+ this.$filteredItems.unsubscribe();
1319
1316
  this._reload$.complete();
1320
- this._filter$.complete();
1321
- this.onDropButtonClick.complete();
1322
- this.onClick.complete();
1323
- this.onBlur.complete();
1324
- this.onFocus.complete();
1325
- this.onKeydownEscape.complete();
1326
- this.onKeyupEnter.complete();
1317
+ this._$filter.complete();
1318
+ this.dropButtonClick.complete();
1319
+ this.clicked.complete();
1320
+ this.blurred.complete();
1321
+ this.focused.complete();
1322
+ this.keydownEscape.complete();
1323
+ this.keyupEnter.complete();
1327
1324
  this._fetchMore$.complete();
1328
1325
  this._implicitValue = undefined;
1329
1326
  this.formControl = null;
1330
1327
  this.displayWith = null;
1331
- this.compareWith = null;
1328
+ this.equals = null;
1332
1329
  this.suggestFn = null;
1333
1330
  this.$filteredItems = null;
1334
1331
  this.matSelectItems$ = null;
@@ -1338,8 +1335,8 @@ class MatAutocompleteField {
1338
1335
  this._onTouchedCallback = null;
1339
1336
  this._onFetchMoreCallback = null;
1340
1337
  // Destroy the 'autocomplete' instance, to avoid memory leak
1341
- if (this.matAutocomplete) {
1342
- this.matAutocomplete.ngOnDestroy();
1338
+ if (this.autocomplete) {
1339
+ this.autocomplete.ngOnDestroy();
1343
1340
  }
1344
1341
  }
1345
1342
  /**
@@ -1365,7 +1362,7 @@ class MatAutocompleteField {
1365
1362
  }
1366
1363
  setDisabledState(isDisabled) {
1367
1364
  var _a;
1368
- if (isDisabled && ((_a = this.matAutocomplete) === null || _a === void 0 ? void 0 : _a.isOpen)) {
1365
+ if (isDisabled && ((_a = this.autocomplete) === null || _a === void 0 ? void 0 : _a.isOpen)) {
1369
1366
  this.closePanel();
1370
1367
  }
1371
1368
  this.cd.markForCheck();
@@ -1379,7 +1376,7 @@ class MatAutocompleteField {
1379
1376
  (_a = this.matSelect) === null || _a === void 0 ? void 0 : _a.focus();
1380
1377
  }
1381
1378
  }
1382
- filterInputTextFocusInEvent(event) {
1379
+ filterInputTextFocusEvent(event) {
1383
1380
  if (!event || event.defaultPrevented)
1384
1381
  return;
1385
1382
  // Ignore event from mat-option
@@ -1388,7 +1385,7 @@ class MatAutocompleteField {
1388
1385
  if (event.stopPropagation)
1389
1386
  event.stopPropagation();
1390
1387
  event.returnValue = false;
1391
- //DEBUG console.debug(this.logPrefix + " Cancelling focus event");
1388
+ //DEBUG console.debug(this.logPrefix + ' Cancelling focus event');
1392
1389
  return false;
1393
1390
  }
1394
1391
  const hasContent = selectInputContent(event);
@@ -1396,7 +1393,7 @@ class MatAutocompleteField {
1396
1393
  if (!hasContent || (this.showPanelOnFocus && this.showAllOnFocus)) {
1397
1394
  // DEBUG
1398
1395
  //console.debug(this.logPrefix + ' Emit focus event');
1399
- this.onFocus.emit(event);
1396
+ this.focused.emit(event);
1400
1397
  return true;
1401
1398
  }
1402
1399
  return false;
@@ -1417,7 +1414,7 @@ class MatAutocompleteField {
1417
1414
  //console.debug(this.logPrefix + ' Blur: move caret to the beginning');
1418
1415
  // Move caret to the beginning (fix issue IMAGINE-469)
1419
1416
  selectInputRange(event.target, 0, 0);
1420
- this.onBlur.emit(event);
1417
+ this.blurred.emit(event);
1421
1418
  return true;
1422
1419
  }
1423
1420
  filterMatSelectFocusEvent(event) {
@@ -1425,7 +1422,7 @@ class MatAutocompleteField {
1425
1422
  return;
1426
1423
  // DEBUG
1427
1424
  // console.debug(this.logPrefix + " Received <mat-select> focus event", event);
1428
- this.onFocus.emit(event);
1425
+ this.focused.emit(event);
1429
1426
  }
1430
1427
  filterMatSelectBlurEvent(event) {
1431
1428
  if (!event || event.defaultPrevented)
@@ -1442,7 +1439,7 @@ class MatAutocompleteField {
1442
1439
  // console.debug(this.logPrefix + " Cancelling <mat-select> blur event");
1443
1440
  return false;
1444
1441
  }
1445
- this.onBlur.emit(event);
1442
+ this.blurred.emit(event);
1446
1443
  return true;
1447
1444
  }
1448
1445
  toggleSearch(event) {
@@ -1464,7 +1461,7 @@ class MatAutocompleteField {
1464
1461
  event.stopPropagation();
1465
1462
  }
1466
1463
  _initAutocompleteInfiniteScroll(threshold) {
1467
- if (!this.matAutocomplete)
1464
+ if (!this.autocomplete)
1468
1465
  return; // Skip
1469
1466
  // DEBUG
1470
1467
  //console.debug(this.logPrefix + ' Init autocomplete scroll listener');
@@ -1472,12 +1469,12 @@ class MatAutocompleteField {
1472
1469
  this._openedSubscription.unsubscribe();
1473
1470
  this._subscription.remove(this._openedSubscription);
1474
1471
  }
1475
- const opened$ = this.matAutocomplete._isOpen
1472
+ const opened$ = this.autocomplete._isOpen
1476
1473
  ? timer(0)
1477
- : this.matAutocomplete.opened.pipe(debounceTime(200));
1474
+ : this.autocomplete.opened.pipe(debounceTime(200));
1478
1475
  const scrollEnd$ = opened$
1479
1476
  .pipe(map((_) => this.getAutocompletePanel()), filter(isNotNil), switchMap((panel) => fromScrollEndEvent(panel, threshold)
1480
- .pipe(takeUntil(this.matAutocomplete.closed))));
1477
+ .pipe(takeUntil(this.autocomplete.closed))));
1481
1478
  // Trigger fetch more, end end scroll reached
1482
1479
  this._openedSubscription = scrollEnd$
1483
1480
  .subscribe(() => this._fetchMore$.emit());
@@ -1508,14 +1505,14 @@ class MatAutocompleteField {
1508
1505
  }
1509
1506
  get isOpen() {
1510
1507
  var _a, _b;
1511
- return (this.searchable && ((_a = this.matAutocomplete) === null || _a === void 0 ? void 0 : _a.isOpen))
1508
+ return (this.searchable && ((_a = this.autocomplete) === null || _a === void 0 ? void 0 : _a.isOpen))
1512
1509
  || ((_b = this.matSelect) === null || _b === void 0 ? void 0 : _b.panelOpen)
1513
1510
  || false;
1514
1511
  }
1515
1512
  closePanel() {
1516
1513
  var _a, _b, _c, _d;
1517
- if ((_a = this.matAutocomplete) === null || _a === void 0 ? void 0 : _a.isOpen) {
1518
- (_b = this.matAutocompleteTrigger) === null || _b === void 0 ? void 0 : _b.closePanel();
1514
+ if ((_a = this.autocomplete) === null || _a === void 0 ? void 0 : _a.isOpen) {
1515
+ (_b = this.autocompleteTrigger) === null || _b === void 0 ? void 0 : _b.closePanel();
1519
1516
  return;
1520
1517
  }
1521
1518
  if ((_c = this.matSelect) === null || _c === void 0 ? void 0 : _c.panelOpen) {
@@ -1582,7 +1579,7 @@ class MatAutocompleteField {
1582
1579
  }
1583
1580
  getAutocompletePanel() {
1584
1581
  var _a, _b;
1585
- const ele = (_b = (_a = this.matAutocomplete) === null || _a === void 0 ? void 0 : _a.options.last) === null || _b === void 0 ? void 0 : _b._getHostElement().parentElement;
1582
+ const ele = (_b = (_a = this.autocomplete) === null || _a === void 0 ? void 0 : _a.options.last) === null || _b === void 0 ? void 0 : _b._getHostElement().parentElement;
1586
1583
  if (!ele) {
1587
1584
  // DEBUG
1588
1585
  console.warn(this.logPrefix + ' Unable to find the autocomplete div...');
@@ -1616,10 +1613,14 @@ MatAutocompleteField.decorators = [
1616
1613
  { type: Component, args: [{
1617
1614
  // eslint-disable-next-line @angular-eslint/component-selector
1618
1615
  selector: 'mat-autocomplete-field',
1619
- template: "<ion-grid class=\"ion-no-padding\">\n <ion-row>\n <ion-col class=\"ion-no-padding\">\n <mat-form-field *ngIf=\"_readonly; else writable\"\n [floatLabel]=\"floatLabel\"\n [title]=\"displayValue\"\n class=\"mat-form-field-disabled\">\n\n <div matPrefix>\n <ng-container *ngTemplateOutlet=\"matPrefixTemplate\"></ng-container>\n </div>\n\n <!-- readonly -->\n <ng-container>\n <input matInput hidden type=\"text\"\n [placeholder]=\"placeholder\"\n [formControl]=\"formControl\" readonly>\n <ion-text>{{displayWith(value)}}</ion-text>\n </ng-container>\n\n <div matSuffix>\n <ng-container *ngTemplateOutlet=\"matSuffixTemplate\"></ng-container>\n </div>\n\n </mat-form-field>\n\n <ng-template #writable>\n <mat-form-field [floatLabel]=\"floatLabel\"\n [class.mat-form-field-disabled]=\"disabled\"\n [title]=\"displayValue\">\n\n <div matPrefix>\n <ng-container *ngTemplateOutlet=\"matPrefixTemplate\"></ng-container>\n </div>\n\n <!-- Basic select -->\n <mat-select #matSelect\n *ngIf=\"!searchable; else searchableTemplate\"\n [placeholder]=\"placeholder\"\n [formControl]=\"formControl\"\n [tabindex]=\"_tabindex\"\n [panelClass]=\"classList\"\n [style.width]=\"panelWidth\"\n (focus)=\"filterMatSelectFocusEvent($event)\"\n (blur)=\"filterMatSelectBlurEvent($event)\"\n (keydown.escape)=\"onKeydownEscape.emit($event)\"\n (keyup.enter)=\"onKeyupEnter.emit($event)\"\n [compareWith]=\"compareWith\"\n [multiple]=\"multiple\"\n [required]=\"required\">\n <mat-select-trigger>{{ displayWith(value) }}</mat-select-trigger>\n\n <!-- header -->\n <ion-row class=\"mat-autocomplete-header ion-no-padding\">\n <ion-col *ngFor=\"let attr of displayAttributes; index as i; last as isLast\" [size]=\"displayColumnSizes[i]\">\n <ion-label [innerHTML]=\"displayColumnNames[i] | translate\"></ion-label>\n </ion-col>\n </ion-row>\n\n <!-- None option -->\n <mat-option *ngIf=\"!required\" class=\"ion-no-padding\">\n <ion-row class=\"ion-no-padding\">\n <ion-col class=\"ion-no-padding\">\n <ion-label><i translate>COMMON.EMPTY_OPTION</i></ion-label>\n </ion-col>\n </ion-row>\n </mat-option>\n\n <ng-container *ngIf=\"$filteredItems | async; let items\">\n <!-- No item option -->\n <mat-option *ngIf=\"items | isEmptyArray\" class=\"ion-padding text-italic\" disabled>\n <ion-label>{{noResultMessage | translate}}</ion-label>\n </mat-option>\n\n <!-- options -->\n <mat-option *ngFor=\"let item of items\" [value]=\"item\" class=\"ion-no-padding\">\n <ion-row [classList]=\"item.classList\">\n <ion-col *ngFor=\"let path of displayAttributes; index as i;\"\n [size]=\"displayColumnSizes[i]\">\n <ion-text>{{item | propertyGet: path }}</ion-text>\n </ion-col>\n </ion-row>\n </mat-option>\n\n <!-- More item -->\n <mat-option *ngIf=\"_moreItemsCount\" (ngInit)=\"_initMatSelectInfiniteScroll()\"\n class=\"ion-padding\" disabled>\n <ion-skeleton-text [animated]=\"true\" style=\"width: 100%\"></ion-skeleton-text>\n </mat-option>\n </ng-container>\n </mat-select>\n\n\n <!--\n NOTE :\n - \"selectInputContent($event) || onFocus.emit($event)\" : call onFocus only when to the input is empty (nothing to select)\n -->\n <ng-template #searchableTemplate>\n <input matInput #matInputText type=\"text\"\n [matAutocomplete]=\"autocomplete\"\n [matAutocompletePosition]=\"matAutocompletePosition\"\n [formControl]=\"formControl\"\n [placeholder]=\"placeholder\"\n [appAutofocus]=\"autofocus\"\n [tabindex]=\"_tabindex\"\n [required]=\"required\"\n (click)=\"onClick.emit($event)\"\n (focus)=\"filterInputTextFocusInEvent($event)\"\n (blur)=\"filterInputTextBlurEvent($event)\"\n (keydown.escape)=\"onKeydownEscape.emit($event)\"\n (keyup.enter)=\"onKeyupEnter.emit($event)\"\n (keyup.arrowdown)=\"!autocomplete.opened && onDropButtonClick.emit($event)\">\n\n <!-- autocomplete -->\n <mat-autocomplete #autocomplete=\"matAutocomplete\"\n autoActiveFirstOption\n [displayWith]=\"displayWith\"\n [class]=\"classList\"\n [class.cdk-visually-hidden]=\"!searchable\"\n [panelWidth]=\"panelWidth\">\n\n <ion-row class=\"mat-autocomplete-header ion-no-padding\">\n <ion-col *ngFor=\"let attr of displayAttributes; index as i; last as last\" [size]=\"displayColumnSizes[i]\">\n <ion-label [innerHTML]=\"displayColumnNames[i] | translate\"></ion-label>\n <ion-text color=\"light\" *ngIf=\"last && itemCount; let count \" class=\"ion-float-end ion-padding-end\"><i>{{'COMMON.RESULT_COUNT'|translate: {count: count} }}</i></ion-text>\n </ion-col>\n </ion-row>\n\n <ng-container *ngIf=\"$filteredItems | async; else loadingTemplate; let items\">\n <!-- No item option -->\n <mat-option *ngIf=\"items | isEmptyArray\" class=\"ion-padding text-italic\" disabled>\n <ion-label>{{noResultMessage | translate}}</ion-label>\n </mat-option>\n\n <mat-option *ngFor=\"let item of items\" [value]=\"item\" class=\"ion-no-padding\">\n <ion-row [classList]=\"item.classList\">\n <ion-col *ngFor=\"let path of displayAttributes; index as i;\"\n [size]=\"displayColumnSizes[i]\">\n <ion-text [innerHTML]=\"item | propertyGet: path | highlight: { search: formControl.value, withAccent: highlightAccent }\"></ion-text>\n </ion-col>\n </ion-row>\n </mat-option>\n\n <!-- More item -->\n <mat-option *ngIf=\"_moreItemsCount\" (ngInit)=\"_initAutocompleteInfiniteScroll()\"\n class=\"ion-padding\" disabled>\n <ion-skeleton-text [animated]=\"true\" style=\"width: 100%\"></ion-skeleton-text>\n </mat-option>\n </ng-container>\n\n <!-- Loading spinner -->\n <ng-template #loadingTemplate>\n <mat-option *ngIf=\"(matInputText.value | strLength) >= suggestLengthThreshold; else waitMoreCharacterTemplate\"\n class=\"ion-padding\" disabled>\n <ion-skeleton-text [animated]=\"true\" style=\"width: 100%\"></ion-skeleton-text>\n </mat-option>\n </ng-template>\n\n <!-- Need more characters -->\n <ng-template #waitMoreCharacterTemplate>\n <mat-option class=\"ion-padding text-italic\" disabled>\n <ion-label>\n {{'INFO.PLEASE_TYPE_MORE_CHARACTERS'|translate:{minLength: suggestLengthThreshold} }}\n </ion-label>\n </mat-option>\n </ng-template>\n </mat-autocomplete>\n\n </ng-template>\n\n <div matSuffix>\n <ng-container *ngTemplateOutlet=\"matSuffixTemplate\"></ng-container>\n </div>\n\n <mat-error *ngIf=\"formControl.hasError('required')\" translate>ERROR.FIELD_REQUIRED</mat-error>\n <mat-error *ngIf=\"formControl.hasError('entity')\" translate>ERROR.FIELD_INVALID</mat-error>\n <ng-content select=\"[matError]\"></ng-content>\n\n </mat-form-field>\n </ng-template>\n </ion-col>\n <ion-col size=\"auto\" class=\"ion-align-self-center\">\n <ng-content select=\"[matAfter]\"></ng-content>\n </ion-col>\n </ion-row>\n\n</ion-grid>\n\n<ng-template #matPrefixTemplate>\n <ng-content select=\"[matPrefix]\"></ng-content>\n</ng-template>\n\n\n<ng-template #matSuffixTemplate>\n <button matSuffix mat-icon-button tabindex=\"-1\"\n type=\"button\"\n *ngIf=\"searchable\"\n (click)=\"onDropButtonClick.emit($event)\"\n [hidden]=\"disabled\">\n <mat-icon class=\"large select-arrow\">arrow_drop_down</mat-icon>\n </button>\n <button matSuffix type=\"button\" mat-icon-button tabindex=\"-1\"\n *ngIf=\"enabled && mobile && !searchable && !multiple\"\n [title]=\"'COMMON.BTN_SEARCH'|translate\"\n (click)=\"toggleSearch($event)\">\n <mat-icon>search</mat-icon>\n </button>\n <button matSuffix mat-icon-button tabindex=\"-1\"\n type=\"button\"\n *ngIf=\"clearable\"\n (click)=\"clearValue($event)\"\n [hidden]=\"disabled || !formControl.value\">\n <mat-icon>close</mat-icon>\n </button>\n\n <ng-content select=\"[matSuffix]\"></ng-content>\n</ng-template>\n",
1620
- providers: [DEFAULT_VALUE_ACCESSOR$6],
1616
+ template: "<ion-grid class=\"ion-no-padding\">\n <ion-row>\n <ion-col class=\"ion-no-padding\">\n <mat-form-field *ngIf=\"_readonly; else writable\"\n [floatLabel]=\"floatLabel\"\n [title]=\"displayValue\"\n class=\"mat-form-field-disabled\">\n\n <div matPrefix>\n <ng-container *ngTemplateOutlet=\"matPrefixTemplate\"></ng-container>\n </div>\n\n <!-- readonly -->\n <ng-container>\n <input matInput hidden type=\"text\"\n [placeholder]=\"placeholder\"\n [formControl]=\"formControl\" readonly>\n <ion-text>{{displayWith(value)}}</ion-text>\n </ng-container>\n\n <div matSuffix>\n <ng-container *ngTemplateOutlet=\"matSuffixTemplate\"></ng-container>\n </div>\n\n </mat-form-field>\n\n <ng-template #writable>\n <mat-form-field [floatLabel]=\"floatLabel\"\n [class.mat-form-field-disabled]=\"disabled\"\n [title]=\"displayValue\">\n\n <div matPrefix>\n <ng-container *ngTemplateOutlet=\"matPrefixTemplate\"></ng-container>\n </div>\n\n <!-- Basic select -->\n <mat-select #matSelect\n *ngIf=\"!searchable; else searchableTemplate\"\n [placeholder]=\"placeholder\"\n [formControl]=\"formControl\"\n [tabindex]=\"_tabindex\"\n [panelClass]=\"classList\"\n [style.width]=\"panelWidth\"\n (focus)=\"filterMatSelectFocusEvent($event)\"\n (blur)=\"filterMatSelectBlurEvent($event)\"\n (keydown.escape)=\"keydownEscape.emit($event)\"\n (keyup.enter)=\"keyupEnter.emit($event)\"\n [compareWith]=\"equals\"\n [multiple]=\"multiple\"\n [required]=\"required\">\n <mat-select-trigger>{{ displayWith(value) }}</mat-select-trigger>\n\n <!-- header -->\n <ion-row class=\"mat-autocomplete-header ion-no-padding\">\n <ion-col *ngFor=\"let attr of displayAttributes; index as i; last as isLast\" [size]=\"displayColumnSizes[i]\">\n <ion-label [innerHTML]=\"displayColumnNames[i] | translate\"></ion-label>\n </ion-col>\n </ion-row>\n\n <!-- None option -->\n <mat-option *ngIf=\"!required\" class=\"ion-no-padding\">\n <ion-row class=\"ion-no-padding\">\n <ion-col class=\"ion-no-padding\">\n <ion-label><i translate>COMMON.EMPTY_OPTION</i></ion-label>\n </ion-col>\n </ion-row>\n </mat-option>\n\n <ng-container *ngIf=\"$filteredItems | async; let items\">\n <!-- No item option -->\n <mat-option *ngIf=\"items | isEmptyArray\" class=\"ion-padding text-italic\" disabled>\n <ion-label>{{noResultMessage | translate}}</ion-label>\n </mat-option>\n\n <!-- options -->\n <mat-option *ngFor=\"let item of items\" [value]=\"item\" class=\"ion-no-padding\">\n <ion-row [classList]=\"item.classList\">\n <ion-col *ngFor=\"let path of displayAttributes; index as i;\"\n [size]=\"displayColumnSizes[i]\">\n <ion-text>{{item | propertyGet: path }}</ion-text>\n </ion-col>\n </ion-row>\n </mat-option>\n\n <!-- More item -->\n <mat-option *ngIf=\"_moreItemsCount\" (ngInit)=\"_initMatSelectInfiniteScroll()\"\n class=\"ion-padding\" disabled>\n <ion-skeleton-text [animated]=\"true\" style=\"width: 100%\"></ion-skeleton-text>\n </mat-option>\n </ng-container>\n </mat-select>\n\n\n <!--\n NOTE :\n - \"selectInputContent($event) || onFocus.emit($event)\" : call onFocus only when to the input is empty (nothing to select)\n -->\n <ng-template #searchableTemplate>\n <input matInput #matInputText type=\"text\"\n [matAutocomplete]=\"autocomplete\"\n [matAutocompletePosition]=\"matAutocompletePosition\"\n [formControl]=\"formControl\"\n [placeholder]=\"placeholder\"\n [appAutofocus]=\"autofocus\"\n [tabindex]=\"_tabindex\"\n [required]=\"required\"\n (click)=\"clicked.emit($event)\"\n (focus)=\"filterInputTextFocusEvent($event)\"\n (blur)=\"filterInputTextBlurEvent($event)\"\n (keydown.escape)=\"keydownEscape.emit($event)\"\n (keyup.enter)=\"keyupEnter.emit($event)\"\n (keyup.arrowdown)=\"dropButtonClick.emit($event)\">\n\n <!-- autocomplete -->\n <mat-autocomplete #autocomplete=\"matAutocomplete\"\n autoActiveFirstOption\n [displayWith]=\"displayWith\"\n [class]=\"classList\"\n [class.cdk-visually-hidden]=\"!searchable\"\n [panelWidth]=\"panelWidth\">\n\n <ion-row class=\"mat-autocomplete-header ion-no-padding\">\n <ion-col *ngFor=\"let attr of displayAttributes; index as i; last as last\" [size]=\"displayColumnSizes[i]\">\n <ion-label [innerHTML]=\"displayColumnNames[i] | translate\"></ion-label>\n <ion-text color=\"light\" *ngIf=\"last && itemCount; let count\" class=\"ion-float-end ion-padding-end\"><i>{{'COMMON.RESULT_COUNT'|translate: {count: count} }}</i></ion-text>\n </ion-col>\n </ion-row>\n\n <ng-container *ngIf=\"$filteredItems | async; else loadingTemplate; let items\">\n <!-- No item option -->\n <mat-option *ngIf=\"items | isEmptyArray\" class=\"ion-padding text-italic\" disabled>\n <ion-label><i>{{noResultMessage | translate}}</i></ion-label>\n </mat-option>\n\n <mat-option *ngFor=\"let item of items\" [value]=\"item\" class=\"ion-no-padding\">\n <ion-row [classList]=\"item.classList\">\n <ion-col *ngFor=\"let path of displayAttributes; index as i;\"\n [size]=\"displayColumnSizes[i]\">\n <ion-text [innerHTML]=\"item | propertyGet: path | highlight: { search: formControl.value, withAccent: highlightAccent }\"></ion-text>\n </ion-col>\n </ion-row>\n </mat-option>\n\n <!-- More item -->\n <mat-option *ngIf=\"_moreItemsCount\" (ngInit)=\"_initAutocompleteInfiniteScroll()\"\n class=\"ion-padding\" disabled>\n <ion-skeleton-text [animated]=\"true\" style=\"width: 100%\"></ion-skeleton-text>\n </mat-option>\n </ng-container>\n\n <!-- Loading spinner -->\n <ng-template #loadingTemplate>\n <mat-option *ngIf=\"(matInputText.value | strLength) >= suggestLengthThreshold; else waitMoreCharacterTemplate\"\n class=\"ion-padding\" disabled>\n <ion-skeleton-text [animated]=\"true\" style=\"width: 100%\"></ion-skeleton-text>\n </mat-option>\n </ng-template>\n\n <!-- Need more characters -->\n <ng-template #waitMoreCharacterTemplate>\n <mat-option class=\"ion-padding text-italic\" disabled>\n <ion-label>\n {{'INFO.PLEASE_TYPE_MORE_CHARACTERS'|translate:{minLength: suggestLengthThreshold} }}\n </ion-label>\n </mat-option>\n </ng-template>\n </mat-autocomplete>\n\n </ng-template>\n\n <div matSuffix>\n <ng-container *ngTemplateOutlet=\"matSuffixTemplate\"></ng-container>\n </div>\n\n <mat-error *ngIf=\"formControl.hasError('required')\" translate>ERROR.FIELD_REQUIRED</mat-error>\n <mat-error *ngIf=\"formControl.hasError('entity')\" translate>ERROR.FIELD_INVALID</mat-error>\n <ng-content select=\"[matError]\"></ng-content>\n\n </mat-form-field>\n </ng-template>\n </ion-col>\n <ion-col size=\"auto\" class=\"ion-align-self-center\">\n <ng-content select=\"[matAfter]\"></ng-content>\n </ion-col>\n </ion-row>\n</ion-grid>\n\n<ng-template #matPrefixTemplate>\n <ng-content select=\"[matPrefix]\"></ng-content>\n</ng-template>\n\n\n<ng-template #matSuffixTemplate>\n <button matSuffix mat-icon-button tabindex=\"-1\"\n type=\"button\"\n *ngIf=\"searchable\"\n (click)=\"dropButtonClick.emit($event)\"\n [hidden]=\"disabled\">\n <mat-icon class=\"large select-arrow\">arrow_drop_down</mat-icon>\n </button>\n <button matSuffix type=\"button\" mat-icon-button tabindex=\"-1\"\n *ngIf=\"enabled && mobile && !searchable && !multiple\"\n [title]=\"'COMMON.BTN_SEARCH'|translate\"\n (click)=\"toggleSearch($event)\">\n <mat-icon>search</mat-icon>\n </button>\n <button matSuffix mat-icon-button tabindex=\"-1\"\n type=\"button\"\n *ngIf=\"clearable\"\n (click)=\"clearValue($event)\"\n [hidden]=\"disabled || !formControl.value\">\n <mat-icon>close</mat-icon>\n </button>\n\n <ng-content select=\"[matSuffix]\"></ng-content>\n</ng-template>\n",
1617
+ providers: [{
1618
+ provide: NG_VALUE_ACCESSOR,
1619
+ multi: true,
1620
+ useExisting: forwardRef(() => MatAutocompleteField)
1621
+ }],
1621
1622
  changeDetection: ChangeDetectionStrategy.OnPush,
1622
- styles: [":host{display:inline-block;position:relative}:host.ion-text-wrap ion-label{white-space:normal!important}button[hidden]{display:none}mat-icon.select-arrow{color:rgba(0,0,0,.47);font-size:150%}.fact-scroll-viewport{height:100%;width:100%}.fact-item{height:54px;display:flex}.fact-item .fact-date{min-width:100px;text-align:center;font-size:24pt}.fact-item .fact-date,.fact-item .fact-text{height:100%;align-items:center;justify-content:center;display:flex}.fact-item .fact-text{border-radius:10px}cdk-virtual-scroll-viewport{border:1px solid #000}cdk-virtual-scroll-viewport li{list-style:none}"]
1623
+ styles: [":host{display:inline-block;position:relative}:host.ion-text-wrap ion-label{white-space:normal!important}button[hidden]{display:none}mat-icon.select-arrow{color:rgba(0,0,0,.47);font-size:150%}"]
1623
1624
  },] }
1624
1625
  ];
1625
1626
  MatAutocompleteField.ctorParameters = () => [
@@ -1627,7 +1628,7 @@ MatAutocompleteField.ctorParameters = () => [
1627
1628
  { type: FormGroupDirective, decorators: [{ type: Optional }] }
1628
1629
  ];
1629
1630
  MatAutocompleteField.propDecorators = {
1630
- compareWith: [{ type: Input }],
1631
+ equals: [{ type: Input }],
1631
1632
  logPrefix: [{ type: Input }],
1632
1633
  formControl: [{ type: Input }],
1633
1634
  formControlName: [{ type: Input }],
@@ -1657,21 +1658,21 @@ MatAutocompleteField.propDecorators = {
1657
1658
  fetchMoreThreshold: [{ type: Input }],
1658
1659
  suggestLengthThreshold: [{ type: Input }],
1659
1660
  showLoadingSpinner: [{ type: Input }],
1661
+ clicked: [{ type: Output, args: ['click',] }],
1662
+ blurred: [{ type: Output, args: ['blur',] }],
1663
+ focused: [{ type: Output, args: ['focus',] }],
1664
+ dropButtonClick: [{ type: Output }],
1665
+ keydownEscape: [{ type: Output, args: ['keydown.escape',] }],
1666
+ keyupEnter: [{ type: Output, args: ['keyup.enter',] }],
1667
+ matSelect: [{ type: ViewChild, args: ['matSelect',] }],
1668
+ matInputText: [{ type: ViewChild, args: ['matInputText',] }],
1669
+ autocomplete: [{ type: ViewChild, args: [MatAutocomplete,] }],
1670
+ autocompleteTrigger: [{ type: ViewChild, args: [MatAutocompleteTrigger,] }],
1660
1671
  animationsDisabled: [{ type: HostBinding, args: ['@.disabled',] }],
1661
1672
  filter: [{ type: Input }],
1662
1673
  readonly: [{ type: Input }],
1663
1674
  tabindex: [{ type: Input }],
1664
- items: [{ type: Input }],
1665
- onClick: [{ type: Output, args: ['click',] }],
1666
- onBlur: [{ type: Output, args: ['blur',] }],
1667
- onFocus: [{ type: Output, args: ['focus',] }],
1668
- onDropButtonClick: [{ type: Output, args: ['dropButtonClick',] }],
1669
- onKeydownEscape: [{ type: Output, args: ['keydown.escape',] }],
1670
- onKeyupEnter: [{ type: Output, args: ['keyup.enter',] }],
1671
- matSelect: [{ type: ViewChild, args: ['matSelect',] }],
1672
- matInputText: [{ type: ViewChild, args: ['matInputText',] }],
1673
- matAutocomplete: [{ type: ViewChild, args: [MatAutocomplete,] }],
1674
- matAutocompleteTrigger: [{ type: ViewChild, args: [MatAutocompleteTrigger,] }]
1675
+ items: [{ type: Input }]
1675
1676
  };
1676
1677
 
1677
1678
  class DateFormatPipe {
@@ -6552,7 +6553,7 @@ class MatSwipeField {
6552
6553
  // Default values
6553
6554
  this.displayAttributes = this.displayAttributes || ['label', 'name'];
6554
6555
  this.displayWith = this.displayWith || ((obj) => obj && joinPropertiesPath(obj, this.displayAttributes));
6555
- this.compareWithFn = this.compareWithFn || ((o1, o2) => o1 && o2 && o1.id === o2.id);
6556
+ this.equals = this.equals || ((o1, o2) => o1 && o2 && o1.id === o2.id);
6556
6557
  this.mobile = toBoolean(this.mobile, false);
6557
6558
  }
6558
6559
  ngOnDestroy() {
@@ -6612,7 +6613,7 @@ class MatSwipeField {
6612
6613
  if (this._writing)
6613
6614
  return;
6614
6615
  this._writing = true;
6615
- if (!this.compareWithFn(value, this._value)) {
6616
+ if (!this.equals(value, this._value)) {
6616
6617
  this._value = value;
6617
6618
  this.showSlides = isNotNil(this._value);
6618
6619
  this.updateSlides();
@@ -6696,7 +6697,7 @@ class MatSwipeField {
6696
6697
  }
6697
6698
  updateSlides() {
6698
6699
  if (this.showSlides) {
6699
- const itemIndex = (this.$items.getValue() || []).findIndex(value => this.compareWithFn(value, this._value));
6700
+ const itemIndex = (this.$items.getValue() || []).findIndex(value => this.equals(value, this._value));
6700
6701
  if (itemIndex >= 0) {
6701
6702
  this.slideTo(itemIndex);
6702
6703
  }
@@ -6760,7 +6761,7 @@ MatSwipeField.propDecorators = {
6760
6761
  required: [{ type: Input }],
6761
6762
  mobile: [{ type: Input }],
6762
6763
  clearable: [{ type: Input }],
6763
- compareWithFn: [{ type: Input }],
6764
+ equals: [{ type: Input }],
6764
6765
  displayWith: [{ type: Input }],
6765
6766
  displayAttributes: [{ type: Input }],
6766
6767
  appAutofocus: [{ type: Input }],
@@ -6806,36 +6807,48 @@ class MatChipsField {
6806
6807
  this._onChangeCallback = (_) => { };
6807
6808
  this._onTouchedCallback = () => { };
6808
6809
  this.logPrefix = '[mat-chips] ';
6810
+ this.formControlName = null;
6809
6811
  this.required = false;
6810
6812
  this.readonly = false;
6811
6813
  this.clearable = false;
6812
- this.debounceTime = 250;
6813
- this.highlightAccent = false;
6814
+ this.debounceTime = null;
6814
6815
  this.i18nPrefix = 'REFERENTIAL.';
6815
6816
  this.noResultMessage = 'COMMON.NO_RESULT';
6816
6817
  this.debug = false;
6818
+ this.itemSize = '48px';
6819
+ this.fetchMoreThreshold = '15%';
6820
+ this.suggestLengthThreshold = null;
6821
+ this.showLoadingSpinner = true;
6822
+ this.chipColor = null;
6817
6823
  this.clicked = new EventEmitter();
6818
6824
  this.blurred = new EventEmitter();
6819
6825
  this.focused = new EventEmitter();
6826
+ this.dropButtonClick = new EventEmitter(true);
6827
+ this.keydownEscape = new EventEmitter();
6828
+ this.keyupEnter = new EventEmitter();
6820
6829
  this.$inputItems = new BehaviorSubject(undefined);
6821
- this.onDropButtonClick = new EventEmitter(true);
6830
+ this.$filteredItems = new BehaviorSubject(undefined);
6822
6831
  this.inputControl = new FormControl();
6823
- this.getPropertyByPath = getPropertyByPath;
6832
+ this._fetchMore$ = new EventEmitter();
6824
6833
  this._subscription = new Subscription();
6825
6834
  this._$filter = new BehaviorSubject(undefined);
6835
+ this._reload$ = new Subject();
6826
6836
  }
6827
6837
  get itemCount() {
6828
6838
  return this._itemCount;
6829
6839
  }
6840
+ get canFetchMore() {
6841
+ return this._onFetchMoreCallback && this._moreItemsCount > 0;
6842
+ }
6830
6843
  set filter(value) {
6831
- if (this.debug)
6832
- console.debug(this.logPrefix + ' Setting filter:', value);
6833
- if (value !== this._$filter.getValue()) {
6844
+ if (value !== this._$filter.value) {
6845
+ // DEBUG
6846
+ //console.debug(this.logPrefix + " Setting filter:", value);
6834
6847
  this._$filter.next(value);
6835
6848
  }
6836
6849
  }
6837
6850
  get filter() {
6838
- return this._$filter.getValue();
6851
+ return this._$filter.value;
6839
6852
  }
6840
6853
  set tabindex(value) {
6841
6854
  this._tabindex = value;
@@ -6847,19 +6860,25 @@ class MatChipsField {
6847
6860
  set items(value) {
6848
6861
  // Remove previous subscription on items, (if exits)
6849
6862
  if (this._itemsSubscription) {
6850
- console.warn('Items received twice !');
6863
+ console.warn(this.logPrefix + ' Items received twice !');
6851
6864
  this._subscription.remove(this._itemsSubscription);
6852
6865
  this._itemsSubscription.unsubscribe();
6853
6866
  }
6854
6867
  if (isObservable(value)) {
6855
- this._itemsSubscription = this._subscription.add(value.subscribe(v => this.$inputItems.next(v)));
6868
+ this._itemsSubscription = this._subscription.add(value.subscribe(v => {
6869
+ //console.warn(this.logPrefix + " Received items: ", v);
6870
+ this.$inputItems.next(v);
6871
+ }));
6856
6872
  }
6857
6873
  else {
6858
- if (value !== this.$inputItems.getValue()) {
6874
+ if (value !== this.$inputItems.value) {
6859
6875
  this.$inputItems.next(value);
6860
6876
  }
6861
6877
  }
6862
6878
  }
6879
+ get items() {
6880
+ return this.$inputItems;
6881
+ }
6863
6882
  get value() {
6864
6883
  return this.formControl.value || [];
6865
6884
  }
@@ -6870,6 +6889,7 @@ class MatChipsField {
6870
6889
  return !this.readonly && this.formControl.enabled;
6871
6890
  }
6872
6891
  ngOnInit() {
6892
+ var _a, _b;
6873
6893
  this.formControl = this.formControl || this.formControlName && this.formGroupDir && this.formGroupDir.form.get(this.formControlName);
6874
6894
  if (!this.formControl)
6875
6895
  throw new Error('Missing mandatory attribute \'formControl\' or \'formControlName\' in <mat-chips-field>.');
@@ -6884,85 +6904,212 @@ class MatChipsField {
6884
6904
  this.displayColumnSizes = this.displayColumnSizes || this.config.columnSizes;
6885
6905
  this.displayColumnNames = this.displayColumnNames || this.config.columnNames;
6886
6906
  this.displayWith = this.displayWith || this.config.displayWith;
6887
- this.compareWith = this.compareWith || this.config.compareWith;
6907
+ this.equals = this.equals || this.config.equals;
6888
6908
  this.mobile = toBoolean(this.mobile, this.config.mobile);
6889
- this.showAllOnFocus = toBoolean(this.showAllOnFocus, toBoolean(this.config.showAllOnFocus, true));
6909
+ this.suggestLengthThreshold = toNumber(this.suggestLengthThreshold, this.config.suggestLengthThreshold || 0);
6910
+ this.showAllOnFocus = toBoolean(this.showAllOnFocus, toBoolean(this.config.showAllOnFocus, this.suggestLengthThreshold <= 0));
6890
6911
  this.showPanelOnFocus = toBoolean(this.showPanelOnFocus, toBoolean(this.config.showPanelOnFocus, true));
6891
- this.class = this.class || this.config.class;
6912
+ this.classList = this.classList || this.config.class;
6913
+ this.debounceTime = toNumber(this.debounceTime, this.config.debounceTime);
6914
+ this.highlightAccent = toBoolean(this.highlightAccent, toBoolean(this.config.highlightAccent));
6892
6915
  }
6893
6916
  // Default values
6894
- this.displayAttributes = this.displayAttributes || (this.filter && this.filter.attributes) || ['label', 'name'];
6917
+ this.displayAttributes = this.displayAttributes || ((_a = this.filter) === null || _a === void 0 ? void 0 : _a.attributes) || ['label', 'name'];
6895
6918
  this.displayWith = this.displayWith || ((obj) => obj && joinPropertiesPath(obj, this.displayAttributes));
6896
- this.compareWith = this.compareWith || ((o1, o2) => o1 && o2 && o1.id === o2.id);
6897
- this.displayColumnSizes = this.displayColumnSizes || this.displayAttributes.map(attr => (
6898
- // If label then col size = 2
6899
- attr && attr.endsWith('label')) ? 2 :
6900
- // If rankOrder then col size = 1
6901
- (attr && attr.endsWith('rankOrder') ? 1 :
6902
- // Else, auto size
6903
- undefined));
6919
+ this.displayColumnSizes = this.displayColumnSizes
6920
+ // if only column: auto size
6921
+ || (this.displayAttributes.length === 1 && [undefined])
6922
+ // Computed column size:
6923
+ // - if label then col size = 2
6924
+ // - if rankOrder then col size = 1
6925
+ // - ese => auto size
6926
+ || this.displayAttributes.map(attr => (attr && attr.endsWith('label')) ? 2
6927
+ : ((attr && attr.endsWith('rankOrder')) ? 1
6928
+ : undefined));
6904
6929
  this.displayColumnNames = this.displayAttributes.map((attr, index) => this.displayColumnNames && this.displayColumnNames[index] ||
6905
6930
  (this.i18nPrefix + changeCaseToUnderscore(attr).toUpperCase()));
6906
6931
  this.mobile = toBoolean(this.mobile, false);
6932
+ this.equals = this.equals || ((o1, o2) => o1 && o2 && o1.id === o2.id);
6933
+ const classList = (_b = this.classList) === null || _b === void 0 ? void 0 : _b.split(' ');
6934
+ this.panelWidth = this.panelWidth || (classList && ((classList.includes('min-width-medium') && '300px')
6935
+ || (classList.includes('min-width-large') && '400px')
6936
+ || (classList.includes('min-width-xlarge') && '450px')
6937
+ || (classList.includes('mat-autocomplete-panel-full-size') && '100vw')));
6938
+ this.showPanelOnFocus = toBoolean(this.showPanelOnFocus, true);
6939
+ this.debounceTime = toNumber(this.debounceTime, 250);
6940
+ this.highlightAccent = toBoolean(this.highlightAccent, false);
6941
+ this.suggestLengthThreshold = this.suggestLengthThreshold || 0;
6942
+ this.matAutocompletePosition = this.matAutocompletePosition || (this.mobile ? 'above' : 'auto'); // On mobile, set position to above (because of bottom keyboard)
6907
6943
  // No suggestFn: filter on the given items
6908
6944
  if (!this.suggestFn) {
6909
6945
  const suggestFromArrayFn = (value, filter) => __awaiter(this, void 0, void 0, function* () {
6910
6946
  if (this.debug)
6911
6947
  console.debug(this.logPrefix + ' Calling suggestFromArray with value=', value);
6912
- const { data, total } = yield suggestFromArray(this.$inputItems.getValue(), value, Object.assign({ searchAttributes: this.displayAttributes }, filter));
6913
- this._itemCount = toNumber(total, data && data.length || 0);
6914
- return { data, total };
6948
+ return suggestFromArray(this.$inputItems.value, value, Object.assign({ searchAttributes: this.displayAttributes }, filter));
6915
6949
  });
6916
6950
  // Wait (once) that items are loaded, then call suggest from array fn
6917
6951
  this.suggestFn = (value, filter) => __awaiter(this, void 0, void 0, function* () {
6918
- if (isNil(this.$inputItems.getValue())) {
6952
+ if (isNil(this.$inputItems.value)) {
6919
6953
  if (this.debug)
6920
6954
  console.debug('[mat-chips] Waiting items to be set...');
6921
6955
  yield firstNotNilPromise(this.$inputItems);
6922
6956
  if (this.debug)
6923
- console.debug('[mat-chips] Received items:', this.$inputItems.getValue());
6957
+ console.debug('[mat-chips] Received items:', this.$inputItems.value);
6924
6958
  }
6925
6959
  this.suggestFn = suggestFromArrayFn;
6926
- return this.suggest(value, filter); // Loop
6960
+ return this.suggestFn(value, filter); // Loop
6927
6961
  });
6928
6962
  }
6929
- // this.matSelectItems$ = this.$inputItems
6930
- // .pipe(
6931
- // filter(isNotNil),
6932
- // map((items) => {
6933
- // // Make sure control value is inside
6934
- // const value = this.inputControl.value;
6935
- // // If form value is missing: add it to the list
6936
- // if (isNotNil(value) && typeof value === 'object' && items.findIndex(item => this.compareWith(item, value)) === -1) {
6937
- // // console.debug(this.logPrefix + " Add form value to items, because missed in items", value);
6938
- // return items.concat(value);
6939
- // }
6940
- // return items;
6941
- // })
6942
- // );
6943
- this._subscription.add(this.onDropButtonClick
6944
- .pipe(filter(event => (!event || !event.defaultPrevented) && this.formControl.enabled))
6945
- .subscribe(() => {
6946
- this.matInputText.nativeElement.focus();
6947
- }));
6948
- const updateFilteredItemsEvents$ = merge(
6963
+ // Send value to suggest when :
6964
+ // - is nil and '*', and no min length threshold
6965
+ // - is not nil and not a string type (e.g. object)
6966
+ // - is not nil and is a string, and has enough character
6967
+ const thresholdFilterFn = (value) => (isNilOrBlank(value) || (value === '*')
6968
+ ? (this.suggestLengthThreshold <= 0)
6969
+ : (!(typeof value === 'string')
6970
+ || value.length >= this.suggestLengthThreshold));
6971
+ const filteredItemsChanges = merge(
6972
+ // Reload event (no distinctUntilChanged() pipe, to force reload even if same value)
6973
+ this._reload$, this.inputControl.valueChanges
6974
+ .pipe(filter(value => this.enabled && isNotNilString(value)),
6975
+ // Mark as loading (to show loading spinner) - earlier as possible, so before the debounce time
6976
+ tap(_ => this.showLoadingSpinner && this.markAsLoading()),
6977
+ // Filter if not enough character
6978
+ filter(value => value.length >= this.suggestLengthThreshold),
6979
+ // Wait a debounce time
6980
+ debounceTime(this.debounceTime)
6981
+ // DEBUG
6982
+ //tap((value) => console.debug(this.logPrefix + ' Search text changes:', value))
6983
+ ),
6984
+ // Object set: use it (no distinctUntilChanged() pipe need)
6985
+ this.inputControl.valueChanges
6986
+ .pipe(startWith(this.inputControl.value), filter(value => isNotNil(value) && typeof value === 'object'), tap(value => {
6987
+ // DEBUG
6988
+ //console.debug(this.logPrefix + ' object changes:', value)
6989
+ // Update the display value (if need)
6990
+ const displayValue = this.displayWith(value) || '';
6991
+ // Apply to the input[type=text] field
6992
+ if (this.matInputText && this.matInputText.nativeElement) {
6993
+ this.matInputText.nativeElement.value = displayValue;
6994
+ }
6995
+ })
6996
+ // DEBUG
6997
+ //tap(value => console.debug(this.logPrefix + ' Received an object value: ', value))
6998
+ ),
6999
+ // On dropdown button click (no distinctUntilChanged(), nor debounceTime)
7000
+ this.dropButtonClick
7001
+ .pipe(filter(event => this.enabled && (!event || !event.defaultPrevented)), tap(_ => this.matInputText.nativeElement.focus()), map(_ => this.showAllOnFocus ? '*' : this.formControl.value),
7002
+ // Make sure there is enough characters
7003
+ filter(thresholdFilterFn)),
7004
+ // Merge some observables, with a common distinctUntilChanged() pipe, and NO debounceTime
7005
+ merge(
6949
7006
  // Focus or click => Load all
6950
7007
  merge(this.focused, this.clicked)
6951
- .pipe(filter(_ => this.enabled), map((_) => this.showAllOnFocus ? '*' : this.inputControl.value)), this.inputControl.valueChanges
6952
- .pipe(startWith(this.inputControl.value), filter(value => (isNotNil(value) && typeof value === 'object') || (isNotNilString(value) && !value.includes('**'))), tap((value) => {
6953
- if (this.debug)
6954
- console.debug(this.logPrefix + ' valueChanges:', value);
6955
- }), debounceTime(this.debounceTime)), this.$inputItems
6956
- .pipe(map(() => this.formControl.value)), this._$filter
6957
- .pipe(map(() => this.formControl.value)));
6958
- this.filteredItems$ = updateFilteredItemsEvents$
6959
- .pipe(distinctUntilChanged(),
6960
- //tap(value => this.debug && console.debug(this.logPrefix + " Received update event: ", value)),
6961
- switchMap((value) => this.suggest(value, this.filter)));
7008
+ .pipe(filter(_ => this.enabled), map((_) => this.showAllOnFocus ? '*' : this.inputControl.value)), merge(this.$inputItems, this._$filter)
7009
+ .pipe(filter(_ => this.enabled), map(_ => this.inputControl.value)))
7010
+ .pipe(
7011
+ // Make sure there is enough characters
7012
+ filter(thresholdFilterFn),
7013
+ // Distinguish changes
7014
+ distinctUntilChanged()))
7015
+ .pipe(
7016
+ // Suggest values
7017
+ mergeMap(value => this.suggest(value, this.filter)),
7018
+ // DEBUG
7019
+ //tap(items => console.debug(this.logPrefix + ' Received from suggest: ', items)),
7020
+ // Store implicit value (will use it onBlur if not other value selected)
7021
+ tap(items => this.updateImplicitValue(items)));
7022
+ // Fetch more events
7023
+ const fetchMoreItemsChanges = this._fetchMore$
7024
+ .pipe(tap(event => event === null || event === void 0 ? void 0 : event.preventDefault()), // Avoid to close the mat-select
7025
+ filter(() => this.canFetchMore), mergeMap(() => this.fetchMore()),
7026
+ // DEBUG
7027
+ //tap(moreItems => console.debug(this.logPrefix + ' Received from fetch More: ', moreItems)),
7028
+ // Concat to existing items
7029
+ map(moreItems => (this.$filteredItems.value || []).concat(moreItems)));
7030
+ // Update filtered items
7031
+ this._subscription.add(merge(this.$inputItems,
7032
+ // Update items events
7033
+ filteredItemsChanges,
7034
+ // Fetch more events
7035
+ fetchMoreItemsChanges)
7036
+ // Emit items
7037
+ .subscribe(items => this.$filteredItems.next(items)));
7038
+ // Applying implicit value, on blur
7039
+ this._subscription.add(this.blurred
7040
+ .pipe(map(_ => this.formControl.value))
7041
+ .subscribe(value => {
7042
+ // When leave component without object, use implicit value if :
7043
+ // - an explicit value
7044
+ // - field is not empty (user fill something)
7045
+ // - OR field empty but is required
7046
+ if (this._implicitValue && ((this.required && isNilOrBlank(value)) || (isNotNilOrBlank(value) && typeof value !== 'object'))) {
7047
+ this.writeValue(this._implicitValue);
7048
+ this.formControl.markAsPending({ emitEvent: false, onlySelf: true });
7049
+ this.formControl.updateValueAndValidity({ emitEvent: false, onlySelf: true });
7050
+ this.checkIfTouched();
7051
+ this.$filteredItems.next([this._implicitValue]);
7052
+ }
7053
+ else if (isNilOrBlank(value)) {
7054
+ this.writeValue(null);
7055
+ this.formControl.markAsPending({ emitEvent: false, onlySelf: true });
7056
+ this.formControl.updateValueAndValidity({ emitEvent: false, onlySelf: true });
7057
+ this.checkIfTouched();
7058
+ this.$filteredItems.next(undefined);
7059
+ }
7060
+ this._implicitValue = null; // reset the implicit value
7061
+ }));
7062
+ // Listen form control status change
7063
+ this._subscription.add(this.formControl.statusChanges
7064
+ .pipe(
7065
+ //filter(() => this.enabled),
7066
+ distinctUntilChanged())
7067
+ // Update component state
7068
+ .subscribe(() => this.checkIfTouched()));
7069
+ this._subscription.add(this.keydownEscape.subscribe(event => {
7070
+ if (this.isOpen) {
7071
+ event === null || event === void 0 ? void 0 : event.stopPropagation();
7072
+ event === null || event === void 0 ? void 0 : event.preventDefault();
7073
+ // DEBUG
7074
+ //console.debug(this.logPrefix + " Closing the panel (keydown.escape)");
7075
+ this.closePanel();
7076
+ }
7077
+ }));
6962
7078
  }
6963
7079
  ngOnDestroy() {
6964
7080
  this._subscription.unsubscribe();
7081
+ if (this._itemsSubscription) {
7082
+ this._itemsSubscription.unsubscribe();
7083
+ }
7084
+ if (this._openedSubscription) {
7085
+ this._openedSubscription.unsubscribe();
7086
+ }
6965
7087
  this.$inputItems.complete();
7088
+ this.$filteredItems.complete();
7089
+ this._reload$.complete();
7090
+ this._$filter.complete();
7091
+ this.dropButtonClick.complete();
7092
+ this.clicked.complete();
7093
+ this.blurred.complete();
7094
+ this.focused.complete();
7095
+ this.keydownEscape.complete();
7096
+ this.keyupEnter.complete();
7097
+ this._fetchMore$.complete();
7098
+ this._implicitValue = undefined;
7099
+ this.formControl = null;
7100
+ this.displayWith = null;
7101
+ this.equals = null;
7102
+ this.suggestFn = null;
7103
+ this.$filteredItems = null;
7104
+ this.config = null;
7105
+ this.classList = null;
7106
+ this._onChangeCallback = null;
7107
+ this._onTouchedCallback = null;
7108
+ this._onFetchMoreCallback = null;
7109
+ // Destroy the 'autocomplete' instance, to avoid memory leak
7110
+ if (this.autocomplete) {
7111
+ this.autocomplete.ngOnDestroy();
7112
+ }
6966
7113
  }
6967
7114
  /**
6968
7115
  * Add item to chips
@@ -6983,19 +7130,21 @@ class MatChipsField {
6983
7130
  * @param item
6984
7131
  */
6985
7132
  remove(item) {
6986
- const index = this.value && this.value.indexOf(item);
6987
- if (index >= 0) {
6988
- const value = this.value.slice();
6989
- value.splice(index, 1);
6990
- this.writeValue(value);
6991
- this.inputControl.setValue('');
7133
+ const value = this.value;
7134
+ const index = value ? value.indexOf(item) : -1;
7135
+ if (index !== -1) {
7136
+ const newValue = value.slice();
7137
+ newValue.splice(index, 1);
7138
+ this.writeValue(newValue);
7139
+ this.closePanel();
6992
7140
  }
6993
7141
  }
6994
7142
  /**
6995
7143
  * Allow to reload content. Useful when filter has been changed but not detected
6996
7144
  */
6997
- reloadItems() {
6998
- this.onDropButtonClick.emit();
7145
+ reloadItems(value) {
7146
+ // Force a refresh
7147
+ this._reload$.next(value || '*');
6999
7148
  }
7000
7149
  writeValue(value) {
7001
7150
  if (this.debug)
@@ -7012,6 +7161,10 @@ class MatChipsField {
7012
7161
  this._onTouchedCallback = fn;
7013
7162
  }
7014
7163
  setDisabledState(isDisabled) {
7164
+ var _a;
7165
+ if (isDisabled && ((_a = this.autocomplete) === null || _a === void 0 ? void 0 : _a.isOpen)) {
7166
+ this.closePanel();
7167
+ }
7015
7168
  this.cd.markForCheck();
7016
7169
  }
7017
7170
  focus() {
@@ -7031,6 +7184,7 @@ class MatChipsField {
7031
7184
  return false;
7032
7185
  }
7033
7186
  const hasContent = selectInputContent(event);
7187
+ // If combo is empty, or if has content but should force to show panel on focus
7034
7188
  if (!hasContent || (this.showPanelOnFocus && this.showAllOnFocus)) {
7035
7189
  if (this.debug)
7036
7190
  console.debug(this.logPrefix + ' Emit focus event');
@@ -7039,10 +7193,68 @@ class MatChipsField {
7039
7193
  }
7040
7194
  return false;
7041
7195
  }
7196
+ filterInputTextBlurEvent(event) {
7197
+ if (!event || event.defaultPrevented)
7198
+ return;
7199
+ // Ignore event from mat-option
7200
+ if (event.relatedTarget instanceof HTMLElement && event.relatedTarget.tagName === 'MAT-OPTION') {
7201
+ event.preventDefault();
7202
+ if (event.stopPropagation)
7203
+ event.stopPropagation();
7204
+ event.returnValue = false;
7205
+ //DEBUG console.debug(this.logPrefix + " Cancelling blur event");
7206
+ return false;
7207
+ }
7208
+ //DEBUG
7209
+ //console.debug(this.logPrefix + ' Blur: move caret to the beginning');
7210
+ // Move caret to the beginning (fix issue IMAGINE-469)
7211
+ selectInputRange(event.target, 0, 0);
7212
+ this.blurred.emit(event);
7213
+ return true;
7214
+ }
7215
+ filterChipsFocusEvent(event) {
7216
+ // DEBUG
7217
+ if (this.debug)
7218
+ console.debug(this.logPrefix + ' Focus in chips list', event);
7219
+ // Ignore event in button
7220
+ this.matInputText.nativeElement.focus();
7221
+ }
7042
7222
  clearValue(event) {
7043
7223
  this.writeValue(null);
7044
7224
  event.stopPropagation();
7045
7225
  }
7226
+ _initAutocompleteInfiniteScroll(threshold) {
7227
+ if (!this.autocomplete)
7228
+ return; // Skip
7229
+ // DEBUG
7230
+ //console.debug(this.logPrefix + ' Init autocomplete scroll listener');
7231
+ if (this._openedSubscription) {
7232
+ this._openedSubscription.unsubscribe();
7233
+ this._subscription.remove(this._openedSubscription);
7234
+ }
7235
+ const opened$ = this.autocomplete._isOpen
7236
+ ? timer(0)
7237
+ : this.autocomplete.opened.pipe(debounceTime(200));
7238
+ const scrollEnd$ = opened$
7239
+ .pipe(map((_) => this.getAutocompletePanel()), filter(isNotNil), switchMap((panel) => fromScrollEndEvent(panel, threshold)
7240
+ .pipe(takeUntil(this.autocomplete.closed))));
7241
+ // Trigger fetch more, end end scroll reached
7242
+ this._openedSubscription = scrollEnd$
7243
+ .subscribe(() => this._fetchMore$.emit());
7244
+ // Register subscription
7245
+ this._subscription.add(this._openedSubscription);
7246
+ }
7247
+ get isOpen() {
7248
+ var _a;
7249
+ return ((_a = this.autocomplete) === null || _a === void 0 ? void 0 : _a.isOpen) || false;
7250
+ }
7251
+ closePanel() {
7252
+ var _a, _b;
7253
+ if ((_a = this.autocomplete) === null || _a === void 0 ? void 0 : _a.isOpen) {
7254
+ (_b = this.autocompleteTrigger) === null || _b === void 0 ? void 0 : _b.closePanel();
7255
+ return;
7256
+ }
7257
+ }
7046
7258
  /* -- private method -- */
7047
7259
  suggest(value, filter) {
7048
7260
  return __awaiter(this, void 0, void 0, function* () {
@@ -7064,7 +7276,7 @@ class MatChipsField {
7064
7276
  this._itemCount = toNumber(total, data && data.length || 0);
7065
7277
  }
7066
7278
  // Filter out existing items
7067
- const filteredData = (res || []).slice().filter(item1 => this.value.findIndex(item2 => this.compareWith(item1, item2)) === -1);
7279
+ const filteredData = (res || []).slice().filter(item1 => this.value.findIndex(item2 => this.equals(item1, item2)) === -1);
7068
7280
  // Update items count
7069
7281
  this._itemCount = this._itemCount - (((res === null || res === void 0 ? void 0 : res.length) || 0) - ((filteredData === null || filteredData === void 0 ? void 0 : filteredData.length) || 0));
7070
7282
  if (this.debug)
@@ -7072,15 +7284,64 @@ class MatChipsField {
7072
7284
  return res;
7073
7285
  });
7074
7286
  }
7287
+ fetchMore() {
7288
+ return __awaiter(this, void 0, void 0, function* () {
7289
+ if (!this._onFetchMoreCallback)
7290
+ return [];
7291
+ // Save then remove fetch function (to avoid multi-call)
7292
+ const fetchMoreFn = this._onFetchMoreCallback;
7293
+ this._onFetchMoreCallback = undefined;
7294
+ const { data, total, fetchMore } = yield fetchMoreFn();
7295
+ this._itemCount = total || this._itemCount;
7296
+ this._onFetchMoreCallback = fetchMore;
7297
+ this._moreItemsCount = Math.max(0, this._moreItemsCount - data.length); // Borne min = 0
7298
+ return data;
7299
+ });
7300
+ }
7301
+ updateImplicitValue(items) {
7302
+ // Store implicit value (will use it onBlur if not other value selected)
7303
+ if (items && items.length === 1) {
7304
+ this._implicitValue = items[0];
7305
+ //this.formControl.setErrors(null);
7306
+ }
7307
+ else {
7308
+ this._implicitValue = undefined;
7309
+ }
7310
+ }
7311
+ getAutocompletePanel() {
7312
+ var _a, _b;
7313
+ const ele = (_b = (_a = this.autocomplete) === null || _a === void 0 ? void 0 : _a.options.last) === null || _b === void 0 ? void 0 : _b._getHostElement().parentElement;
7314
+ if (!ele) {
7315
+ // DEBUG
7316
+ console.warn(this.logPrefix + ' Unable to find the autocomplete div...');
7317
+ }
7318
+ return ele;
7319
+ }
7320
+ checkIfTouched() {
7321
+ if (this.formControl.touched) {
7322
+ this.cd.markForCheck();
7323
+ this._onTouchedCallback();
7324
+ }
7325
+ }
7075
7326
  markForCheck() {
7076
7327
  this.cd.markForCheck();
7077
7328
  }
7329
+ markAsLoading() {
7330
+ if (this.$filteredItems.value) {
7331
+ this.$filteredItems.next(undefined);
7332
+ this._itemCount = undefined;
7333
+ this._implicitValue = undefined;
7334
+ this._onFetchMoreCallback = undefined;
7335
+ this._moreItemsCount = 0;
7336
+ this.markForCheck();
7337
+ }
7338
+ }
7078
7339
  }
7079
7340
  MatChipsField.decorators = [
7080
7341
  { type: Component, args: [{
7081
7342
  // eslint-disable-next-line @angular-eslint/component-selector
7082
7343
  selector: 'mat-chips-field',
7083
- template: "<ion-grid class=\"ion-no-padding\">\n <ion-row>\n <ion-col class=\"ion-no-padding\">\n <mat-form-field [floatLabel]=\"floatLabel\" class=\"material-chips\" [class.mat-form-field-disabled]=\"disabled\">\n <div matPrefix>\n <ng-content select=\"[matPrefix]\"></ng-content>\n </div>\n <mat-label>{{placeholder}}</mat-label>\n <mat-chip-list #chipList>\n <mat-chip\n *ngFor=\"let item of value\"\n [selectable]=\"false\"\n [removable]=\"!disabled\"\n (removed)=\"remove(item)\">\n {{displayWith(item)}}\n <mat-icon matChipRemove *ngIf=\"!disabled\">cancel</mat-icon>\n </mat-chip>\n\n <input matInput #matInputText type=\"text\"\n [matAutocomplete]=\"autoCombo\"\n [formControl]=\"inputControl\"\n [appAutofocus]=\"appAutofocus\"\n [tabindex]=\"_tabindex\"\n [readonly]=\"disabled\"\n [hidden]=\"disabled\"\n [required]=\"required\"\n (click)=\"clicked.emit($event)\"\n (blur)=\"blurred.emit($event)\"\n (focus)=\"filterInputTextFocusEvent($event)\"\n (keyup.arrowdown)=\"!autoCombo.showPanel && onDropButtonClick.emit($event)\"\n [matChipInputFor]=\"chipList\"\n >\n </mat-chip-list>\n\n <!-- autocomplete -->\n <mat-autocomplete #autoCombo=\"matAutocomplete\"\n (optionSelected)=\"add($event)\"\n autoActiveFirstOption\n [displayWith]=\"displayWith\"\n [class]=\"class\">\n <ng-container *ngIf=\"(filteredItems$ | async) as items\">\n <ion-row class=\"mat-autocomplete-header ion-no-padding\">\n <ion-col *ngFor=\"let attr of displayAttributes; index as i; last as isLast\" [size]=\"displayColumnSizes[i]\">\n <ion-label [innerHTML]=\"displayColumnNames[i] | translate\"></ion-label>\n <ion-text color=\"light\" *ngIf=\"isLast\" class=\"ion-float-end ion-padding-end\"><i>{{'COMMON.RESULT_COUNT'|translate: {count: itemCount} }}</i></ion-text>\n </ion-col>\n </ion-row>\n\n <!-- No item option -->\n <mat-option *ngIf=\"!items.length\" class=\"ion-no-padding\" [disabled]=\"true\">\n <ion-row class=\"ion-no-padding\">\n <ion-col class=\"ion-no-padding\">\n <ion-label><i>{{noResultMessage | translate}}</i></ion-label>\n </ion-col>\n </ion-row>\n </mat-option>\n\n <mat-option *ngFor=\"let item of items\" [value]=\"item\" class=\"ion-no-padding\">\n <ion-row>\n <ion-col *ngFor=\"let attr of displayAttributes; index as i;\"\n [size]=\"displayColumnSizes[i]\">\n <ion-text [innerHTML]=\"getPropertyByPath(item, attr) | highlight: { search: inputControl.value, withAccent: highlightAccent } \"></ion-text>\n </ion-col>\n </ion-row>\n </mat-option>\n </ng-container>\n </mat-autocomplete>\n\n <button matSuffix mat-icon-button tabindex=\"-1\"\n type=\"button\"\n (click)=\"onDropButtonClick.emit($event)\"\n [hidden]=\"disabled\">\n <mat-icon class=\"large select-arrow\">arrow_drop_down</mat-icon>\n </button>\n <button matSuffix mat-icon-button tabindex=\"-1\"\n type=\"button\"\n *ngIf=\"clearable\"\n (click)=\"clearValue($event)\"\n [hidden]=\"disabled || !formControl.value\">\n <mat-icon>close</mat-icon>\n </button>\n <ng-content matSuffix select=\"[matSuffix]\"></ng-content>\n\n <mat-error *ngIf=\"formControl.hasError('required')\" translate>ERROR.FIELD_REQUIRED</mat-error>\n\n </mat-form-field>\n\n </ion-col>\n <ion-col size=\"auto\" class=\"ion-align-self-center\">\n <ng-content select=\"[matAfter]\"></ng-content>\n </ion-col>\n </ion-row>\n</ion-grid>\n",
7344
+ template: "<ion-grid class=\"ion-no-padding\">\n <ion-row>\n <ion-col class=\"ion-no-padding\">\n <mat-form-field [floatLabel]=\"floatLabel\" class=\"material-chips\" [class.mat-form-field-disabled]=\"disabled\">\n <div matPrefix>\n <ng-container *ngTemplateOutlet=\"matPrefixTemplate\"></ng-container>\n </div>\n <mat-label>{{placeholder}}</mat-label>\n <mat-chip-list #chipList\n (focus)=\"filterChipsFocusEvent($event)\" >\n <mat-chip\n *ngFor=\"let item of value\"\n [selectable]=\"false\"\n [removable]=\"!disabled\"\n (removed)=\"remove(item)\"\n [color]=\"chipColor\"\n >\n {{displayWith(item)}}\n <mat-icon matChipRemove *ngIf=\"!disabled\">cancel</mat-icon>\n </mat-chip>\n\n <input #matInputText type=\"text\"\n [matAutocomplete]=\"autocomplete\"\n [matAutocompletePosition]=\"matAutocompletePosition\"\n [formControl]=\"inputControl\"\n [appAutofocus]=\"autofocus\"\n [tabindex]=\"_tabindex\"\n [readonly]=\"disabled\"\n [hidden]=\"disabled\"\n [required]=\"required\"\n (click)=\"clicked.emit($event)\"\n (focus)=\"filterInputTextFocusEvent($event)\"\n (blur)=\"filterInputTextBlurEvent($event)\"\n (keydown.escape)=\"keydownEscape.emit($event)\"\n (keyup.enter)=\"keyupEnter.emit($event)\"\n (keyup.arrowdown)=\"!autocomplete.showPanel && dropButtonClick.emit($event)\"\n [matChipInputFor]=\"chipList\"\n >\n </mat-chip-list>\n\n <!-- autocomplete -->\n <mat-autocomplete #autocomplete=\"matAutocomplete\"\n (optionSelected)=\"add($event)\"\n autoActiveFirstOption\n [displayWith]=\"displayWith\"\n [class]=\"classList\"\n [panelWidth]=\"panelWidth\">\n <ion-row class=\"mat-autocomplete-header ion-no-padding\">\n <ion-col *ngFor=\"let attr of displayAttributes; index as i; last as last\" [size]=\"displayColumnSizes[i]\">\n <ion-label [innerHTML]=\"displayColumnNames[i] | translate\"></ion-label>\n <ion-text color=\"light\" *ngIf=\"last && itemCount; let count\" class=\"ion-float-end ion-padding-end\"><i>{{'COMMON.RESULT_COUNT'|translate: {count: count} }}</i></ion-text>\n </ion-col>\n </ion-row>\n\n <ng-container *ngIf=\"$filteredItems | async; else loadingTemplate; let items\">\n <!-- No item option -->\n <mat-option *ngIf=\"items | isEmptyArray\" class=\"ion-padding text-italic\" disabled>\n <ion-label>{{noResultMessage | translate}}</ion-label>\n </mat-option>\n\n <mat-option *ngFor=\"let item of items\" [value]=\"item\" class=\"ion-no-padding\">\n <ion-row [classList]=\"item.classList\">\n <ion-col *ngFor=\"let path of displayAttributes; index as i;\"\n [size]=\"displayColumnSizes[i]\">\n <ion-text [innerHTML]=\"item | propertyGet: path | highlight: { search: inputControl.value, withAccent: highlightAccent } \"></ion-text>\n </ion-col>\n </ion-row>\n </mat-option>\n\n <!-- More item -->\n <mat-option *ngIf=\"_moreItemsCount\" (ngInit)=\"_initAutocompleteInfiniteScroll()\"\n class=\"ion-padding\" disabled>\n <ion-skeleton-text [animated]=\"true\" style=\"width: 100%\"></ion-skeleton-text>\n </mat-option>\n </ng-container>\n\n <!-- Loading spinner -->\n <ng-template #loadingTemplate>\n <mat-option *ngIf=\"(matInputText.value | strLength) >= suggestLengthThreshold; else waitMoreCharacterTemplate\"\n class=\"ion-padding\" disabled>\n <ion-skeleton-text [animated]=\"true\" style=\"width: 100%\"></ion-skeleton-text>\n </mat-option>\n </ng-template>\n\n <!-- Need more characters -->\n <ng-template #waitMoreCharacterTemplate>\n <mat-option class=\"ion-padding text-italic\" disabled>\n <ion-label>\n {{'INFO.PLEASE_TYPE_MORE_CHARACTERS'|translate:{minLength: suggestLengthThreshold} }}\n </ion-label>\n </mat-option>\n </ng-template>\n </mat-autocomplete>\n\n <div matSuffix>\n <ng-container *ngTemplateOutlet=\"matSuffixTemplate\"></ng-container>\n </div>\n\n <mat-error *ngIf=\"formControl.hasError('required')\" translate>ERROR.FIELD_REQUIRED</mat-error>\n <ng-content select=\"[matError]\"></ng-content>\n\n </mat-form-field>\n\n </ion-col>\n <ion-col size=\"auto\" class=\"ion-align-self-center\">\n <ng-content select=\"[matAfter]\"></ng-content>\n </ion-col>\n </ion-row>\n</ion-grid>\n\n<ng-template #matPrefixTemplate>\n <ng-content select=\"[matPrefix]\"></ng-content>\n</ng-template>\n\n\n<ng-template #matSuffixTemplate>\n <button mat-icon-button tabindex=\"-1\"\n type=\"button\"\n (click)=\"dropButtonClick.emit($event)\"\n [hidden]=\"disabled\">\n <mat-icon class=\"large select-arrow\">arrow_drop_down</mat-icon>\n </button>\n <button mat-icon-button tabindex=\"-1\"\n type=\"button\"\n *ngIf=\"clearable\"\n (click)=\"clearValue($event)\"\n [hidden]=\"disabled || !formControl.value\">\n <mat-icon>close</mat-icon>\n </button>\n\n <ng-content select=\"[matSuffix]\"></ng-content>\n</ng-template>\n",
7084
7345
  providers: [
7085
7346
  {
7086
7347
  provide: NG_VALUE_ACCESSOR,
@@ -7088,8 +7349,9 @@ MatChipsField.decorators = [
7088
7349
  useExisting: forwardRef(() => MatChipsField)
7089
7350
  }
7090
7351
  ],
7352
+ encapsulation: ViewEncapsulation.None,
7091
7353
  changeDetection: ChangeDetectionStrategy.OnPush,
7092
- styles: [":host{position:relative}:host.ion-text-wrap ion-label{white-space:normal!important}button[hidden]{display:none}mat-icon.select-arrow{color:rgba(0,0,0,.47);font-size:150%}"]
7354
+ styles: [":host{width:100%;display:inline-block;position:relative}:host.ion-text-wrap ion-label{white-space:normal!important}button[hidden]{display:none}mat-icon.select-arrow{color:rgba(0,0,0,.47);font-size:150%}.mat-form-field-suffix{position:absolute!important;right:0!important;top:calc(100% - 50px)!important}.mat-chip.mat-standard-chip.mat-primary{background-color:var(--ion-color-primary);color:var(--ion-color-primary-contrast)}.mat-chip.mat-standard-chip.mat-secondary{background-color:var(--ion-color-secondary);color:var(--ion-color-secondary-contrast)}.mat-chip.mat-standard-chip.mat-tertiary{background-color:var(--ion-color-tertiary);color:var(--ion-color-tertiary-contrast)}.mat-chip.mat-standard-chip.mat-accent{background-color:var(--ion-color-accent);color:var(--ion-color-accent-contrast)}.mat-chip.mat-standard-chip.mat-warning{background-color:var(--ion-color-warning);color:var(--ion-color-warning-contrast)}"]
7093
7355
  },] }
7094
7356
  ];
7095
7357
  MatChipsField.ctorParameters = () => [
@@ -7097,7 +7359,7 @@ MatChipsField.ctorParameters = () => [
7097
7359
  { type: FormGroupDirective, decorators: [{ type: Optional }] }
7098
7360
  ];
7099
7361
  MatChipsField.propDecorators = {
7100
- compareWith: [{ type: Input }],
7362
+ equals: [{ type: Input }],
7101
7363
  logPrefix: [{ type: Input }],
7102
7364
  formControl: [{ type: Input }],
7103
7365
  formControlName: [{ type: Input }],
@@ -7116,16 +7378,27 @@ MatChipsField.propDecorators = {
7116
7378
  highlightAccent: [{ type: Input }],
7117
7379
  showAllOnFocus: [{ type: Input }],
7118
7380
  showPanelOnFocus: [{ type: Input }],
7119
- appAutofocus: [{ type: Input }],
7381
+ autofocus: [{ type: Input }],
7120
7382
  config: [{ type: Input }],
7121
7383
  i18nPrefix: [{ type: Input }],
7122
7384
  noResultMessage: [{ type: Input }],
7123
- class: [{ type: Input }],
7385
+ classList: [{ type: Input, args: ['class',] }],
7386
+ panelWidth: [{ type: Input }],
7387
+ matAutocompletePosition: [{ type: Input }],
7124
7388
  debug: [{ type: Input }],
7389
+ itemSize: [{ type: Input }],
7390
+ fetchMoreThreshold: [{ type: Input }],
7391
+ suggestLengthThreshold: [{ type: Input }],
7392
+ showLoadingSpinner: [{ type: Input }],
7393
+ chipColor: [{ type: Input }],
7125
7394
  clicked: [{ type: Output }],
7126
7395
  blurred: [{ type: Output }],
7127
7396
  focused: [{ type: Output }],
7397
+ dropButtonClick: [{ type: Output }],
7398
+ keydownEscape: [{ type: Output, args: ['keydown.escape',] }],
7399
+ keyupEnter: [{ type: Output, args: ['keyup.enter',] }],
7128
7400
  matInputText: [{ type: ViewChild, args: ['matInputText',] }],
7401
+ autocomplete: [{ type: ViewChild, args: [MatAutocomplete,] }],
7129
7402
  autocompleteTrigger: [{ type: ViewChild, args: [MatAutocompleteTrigger,] }],
7130
7403
  filter: [{ type: Input }],
7131
7404
  tabindex: [{ type: Input }],
@@ -16367,7 +16640,7 @@ class AutocompleteTestPage {
16367
16640
  AutocompleteTestPage.decorators = [
16368
16641
  { type: Component, args: [{
16369
16642
  selector: 'app-autocomplete-test',
16370
- template: "<ion-header>\n <ion-toolbar color=\"primary\">\n\n <ion-buttons slot=\"start\">\n <ion-back-button></ion-back-button>\n </ion-buttons>\n\n <ion-title>Autocomplete field test page</ion-title>\n </ion-toolbar>\n</ion-header>\n\n<ion-content>\n\n <!-- Tab nav - mobile/desktop mode -->\n <nav mat-tab-nav-bar>\n <a mat-tab-link\n [active]=\"mode==='mobile'\"\n (click)=\"toggleMode('mobile')\">\n <mat-label>Mobile</mat-label>\n </a>\n <a mat-tab-link [active]=\"mode==='desktop'\"\n (click)=\"toggleMode('desktop')\">\n <mat-label>Desktop</mat-label>\n </a>\n <a mat-tab-link [active]=\"mode==='memory'\"\n (click)=\"toggleMode('memory')\">\n <mat-label>Memory leak debug</mat-label>\n </a>\n <a mat-tab-link\n [active]=\"mode==='temp'\"\n (click)=\"toggleMode('temp')\">\n <mat-label>Temporary</mat-label>\n </a>\n </nav>\n\n <form class=\"form-container\" [formGroup]=\"form\" (ngSubmit)=\"doSubmit($event)\">\n\n <!--<ion-grid *ngIf=\"mode === 'temp'\">\n <ion-row>\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Items is an Observable\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>items: Observable&lt;any[]&gt;</pre></small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [logPrefix]=\"'[combo-desktop-2]'\"\n [compareWith]=\"compareWithFn\"\n [mobile]=\"false\"\n ></mat-autocomplete-field>\n </ion-card-content>\n\n </ion-card>\n </ion-col>\n </ion-row>\n\n\n </ion-grid>-->\n\n\n <ion-grid *ngIf=\"mode === 'memory'\">\n\n <!-- debugging memory leak -->\n <ion-row>\n <ion-col>\n <ion-text><h4>Debug memory leak</h4></ion-text>\n </ion-col>\n </ion-row>\n <ion-row>\n <ion-col size=\"2\">\n <mat-form-field floatLabel=\"always\">\n <input matInput type=\"text\" hidden placeholder=\"Items type\">\n <mat-select (selectionChange)=\"memoryAutocompleteFieldName=$event.value\" [value]=\"memoryAutocompleteFieldName\">\n <mat-option value=\"entity-$items\">Observable</mat-option>\n <mat-option value=\"entity-suggestFn\">Suggest function</mat-option>\n </mat-select>\n </mat-form-field>\n </ion-col>\n <ion-col size=\"1\" class=\"ion-no-padding\">\n <mat-form-field floatLabel=\"always\">\n <input matInput type=\"text\" hidden placeholder=\"Mobile ?\">\n <mat-checkbox (change)=\"memoryMobile=$event.checked\" [checked]=\"memoryMobile\">\n </mat-checkbox>\n </mat-form-field>\n </ion-col>\n\n <ion-col size=\"2\">\n <ion-button *ngIf=\"!memoryTimer\" (click)=\"startMemoryTimer()\" color=\"tertiary\">Start</ion-button>\n <ion-button *ngIf=\"memoryTimer\" (click)=\"stopMemoryTimer()\" color=\"tertiary\">Stop</ion-button>\n </ion-col>\n </ion-row>\n\n <ion-row>\n <ion-col>\n <mat-autocomplete-field formControlName=\"entity\"\n *ngIf=\"!memoryHide && memoryAutocompleteFieldName\"\n [config]=\"autocompleteFields.get(memoryAutocompleteFieldName)\"\n [mobile]=\"memoryMobile\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[combo-memory-leak]'\">\n </mat-autocomplete-field>\n </ion-col>\n </ion-row>\n\n </ion-grid>\n\n <!-- Mobile mode -->\n <ion-grid *ngIf=\"mode === 'mobile'\">\n\n <ion-row>\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Suggest() function\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>mobile: true, suggestFn: (searchText, filter) =&gt; any[]</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-suggestFn')\"\n [mobile]=\"true\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[combo-mobile-1]'\"\n [required]=\"true\"\n placeholder=\"Suggest field\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Items is an Observable\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>mobile: true, items: Observable&lt;any[]&gt;</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"true\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[combo-mobile-observable]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Control value if missing in items\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>mobile: true, items: Observable&lt;any[]&gt;</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"missingEntity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"true\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[combo-mobile-2]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Disabled control\n </ion-label>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"disableEntity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"true\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[combo-mobile-2]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n </ion-row>\n\n <ion-row>\n <ion-col size=\"9\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Change filter\n </ion-label>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre></pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <ion-grid class=\"ion-no-padding\">\n <ion-row>\n <ion-col>\n <ion-button (click)=\"updateFilter('entity-items-filter')\">Filter on: {{autocompleteFields.get('entity-items-filter').filter?.searchAttribute }}</ion-button>\n </ion-col>\n <ion-col>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-filter')\"\n [mobile]=\"true\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[combo-mobile-filter]'\"\n ></mat-autocomplete-field>\n </ion-col>\n </ion-row>\n </ion-grid>\n\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col size=\"3\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Large combo\n </ion-label>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>class=\"min-width-large\"</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-large')\"\n [mobile]=\"true\"\n class=\"min-width-large\"\n panelWidth=\"400px\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[combo-mobile-large]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n </ion-row>\n <ion-row>\n <ion-col size=\"6\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Readonly toggle\n </ion-text>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <ion-grid>\n <ion-row>\n <ion-col size=\"3\">\n <mat-checkbox (change)=\"readonlyField.readonly=$event.checked\" [checked]=\"readonlyField.readonly\">\n Readonly ?\n </mat-checkbox>\n </ion-col>\n </ion-row>\n <ion-row>\n <ion-col>\n <mat-autocomplete-field #readonlyField formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items')\"\n [placeholder]=\"'Entity'\"\n [mobile]=\"true\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[combo-mobile-readonly]'\"\n [readonly]=\"true\"\n [clearable]=\"!readonlyField.readonly\">\n </mat-autocomplete-field>\n </ion-col>\n </ion-row>\n </ion-grid>\n </ion-card-content>\n </ion-card>\n </ion-col>\n </ion-row>\n </ion-grid>\n\n <!-- Desktop mode -->\n <ion-grid *ngIf=\"mode === 'desktop'\">\n <ion-row>\n <ion-col>\n <ion-text><h4>Desktop mode</h4></ion-text>\n </ion-col>\n </ion-row>\n <ion-row>\n\n\n <ion-col size=\"6\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n items from suggest function\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>suggest: (value, filter) => LoadResult&lt;any&gt;\nsuggestLengthThreshold: '3'</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-filter')\"\n [placeholder]=\"'Entity'\"\n [mobile]=\"false\"\n [compareWith]=\"compareWithFn\"\n suggestLengthThreshold=\"3\"\n [logPrefix]=\"'[combo-desktop-suggest]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col size=\"6\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Items is an array\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>items: [], value: {{stringify(form.controls.entity.value)}}</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items')\"\n [logPrefix]=\"'[combo-desktop-array-1]'\"\n [compareWith]=\"compareWithFn\"\n [mobile]=\"false\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Items is an Observable\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>items: Observable&lt;any[]&gt;</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [logPrefix]=\"'[combo-desktop-2]'\"\n [compareWith]=\"compareWithFn\"\n [mobile]=\"false\"\n ></mat-autocomplete-field>\n </ion-card-content>\n\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Control value if missing in items\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>items: Observable&lt;any[]&gt;</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"missingEntity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[combo-desktop-missing]'\"\n [mobile]=\"false\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Full size panel\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>class: 'mat-autocomplete-panel-full-size'</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"false\"\n [class]=\"'mat-autocomplete-panel-full-size'\"\n [compareWith]=\"compareWithFn\"\n [matAutocompletePosition]=\"'above'\"\n [logPrefix]=\"'[combo-desktop-full-size]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Disabled control\n </ion-label>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"disableEntity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"false\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[combo-desktop-disable]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Readonly control\n </ion-label>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"false\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[combo-desktop-readonly]'\"\n [readonly]=\"true\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n </ion-row>\n\n <ion-row>\n <ion-col size=\"9\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Full size combo\n </ion-label>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>class=\"mat-autocomplete-panel-full-size\"</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-large')\"\n [mobile]=\"false\"\n class=\"mat-autocomplete-panel-full-size\"\n panelWidth=\"100vw\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[combo-desktop-full-size]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col size=\"3\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Large combo\n </ion-label>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>panelWidth: string</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-large')\"\n [mobile]=\"false\"\n panelWidth=\"400px\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[combo-desktop-large]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col size=\"6\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Readonly toggle\n </ion-text>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <ion-grid>\n <ion-row>\n <ion-col size=\"3\">\n <mat-checkbox (change)=\"readonlyField.readonly=$event.checked\" [checked]=\"readonlyField.readonly\">\n Readonly ?\n </mat-checkbox>\n </ion-col>\n <ion-col size=\"3\">\n <mat-checkbox #showIcons [checked]=\"false\">\n Icons ?\n </mat-checkbox>\n </ion-col>\n </ion-row>\n <ion-row>\n <ion-col>\n <mat-autocomplete-field #readonlyField formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-filter')\"\n [placeholder]=\"'Entity'\"\n [mobile]=\"false\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[combo-desktop-readonly]'\"\n [readonly]=\"true\"\n [clearable]=\"!readonlyField.readonly\">\n <mat-icon matPrefix *ngIf=\"showIcons.checked\">keyboard_arrow_right</mat-icon>\n <mat-icon matSuffix *ngIf=\"showIcons.checked\">keyboard_arrow_left</mat-icon>\n </mat-autocomplete-field>\n </ion-col>\n </ion-row>\n </ion-grid>\n </ion-card-content>\n </ion-card>\n </ion-col>\n </ion-row>\n\n </ion-grid>\n\n </form>\n\n</ion-content>\n"
16643
+ template: "<ion-header>\n <ion-toolbar color=\"primary\">\n\n <ion-buttons slot=\"start\">\n <ion-back-button></ion-back-button>\n </ion-buttons>\n\n <ion-title>Autocomplete field test page</ion-title>\n </ion-toolbar>\n</ion-header>\n\n<ion-content>\n\n <!-- Tab nav - mobile/desktop mode -->\n <nav mat-tab-nav-bar>\n <a mat-tab-link\n [active]=\"mode==='mobile'\"\n (click)=\"toggleMode('mobile')\">\n <mat-label>Mobile</mat-label>\n </a>\n <a mat-tab-link [active]=\"mode==='desktop'\"\n (click)=\"toggleMode('desktop')\">\n <mat-label>Desktop</mat-label>\n </a>\n <a mat-tab-link [active]=\"mode==='memory'\"\n (click)=\"toggleMode('memory')\">\n <mat-label>Memory leak debug</mat-label>\n </a>\n <a mat-tab-link\n [active]=\"mode==='temp'\"\n (click)=\"toggleMode('temp')\">\n <mat-label>Temporary</mat-label>\n </a>\n </nav>\n\n <form class=\"form-container\" [formGroup]=\"form\" (ngSubmit)=\"doSubmit($event)\">\n\n <!--<ion-grid *ngIf=\"mode === 'temp'\">\n <ion-row>\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Items is an Observable\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>items: Observable&lt;any[]&gt;</pre></small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [logPrefix]=\"'[combo-desktop-2]'\"\n [compareWith]=\"compareWithFn\"\n [mobile]=\"false\"\n ></mat-autocomplete-field>\n </ion-card-content>\n\n </ion-card>\n </ion-col>\n </ion-row>\n\n\n </ion-grid>-->\n\n\n <ion-grid *ngIf=\"mode === 'memory'\">\n\n <!-- debugging memory leak -->\n <ion-row>\n <ion-col>\n <ion-text><h4>Debug memory leak</h4></ion-text>\n </ion-col>\n </ion-row>\n <ion-row>\n <ion-col size=\"2\">\n <mat-form-field floatLabel=\"always\">\n <input matInput type=\"text\" hidden placeholder=\"Items type\">\n <mat-select (selectionChange)=\"memoryAutocompleteFieldName=$event.value\" [value]=\"memoryAutocompleteFieldName\">\n <mat-option value=\"entity-$items\">Observable</mat-option>\n <mat-option value=\"entity-suggestFn\">Suggest function</mat-option>\n </mat-select>\n </mat-form-field>\n </ion-col>\n <ion-col size=\"1\" class=\"ion-no-padding\">\n <mat-form-field floatLabel=\"always\">\n <input matInput type=\"text\" hidden placeholder=\"Mobile ?\">\n <mat-checkbox (change)=\"memoryMobile=$event.checked\" [checked]=\"memoryMobile\">\n </mat-checkbox>\n </mat-form-field>\n </ion-col>\n\n <ion-col size=\"2\">\n <ion-button *ngIf=\"!memoryTimer\" (click)=\"startMemoryTimer()\" color=\"tertiary\">Start</ion-button>\n <ion-button *ngIf=\"memoryTimer\" (click)=\"stopMemoryTimer()\" color=\"tertiary\">Stop</ion-button>\n </ion-col>\n </ion-row>\n\n <ion-row>\n <ion-col>\n <mat-autocomplete-field formControlName=\"entity\"\n *ngIf=\"!memoryHide && memoryAutocompleteFieldName\"\n [config]=\"autocompleteFields.get(memoryAutocompleteFieldName)\"\n [mobile]=\"memoryMobile\"\n [equals]=\"compareWithFn\"\n [logPrefix]=\"'[combo-memory-leak]'\">\n </mat-autocomplete-field>\n </ion-col>\n </ion-row>\n\n </ion-grid>\n\n <!-- Mobile mode -->\n <ion-grid *ngIf=\"mode === 'mobile'\">\n\n <ion-row>\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Suggest() function\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>mobile: true, suggestFn: (searchText, filter) =&gt; any[]</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-suggestFn')\"\n [mobile]=\"true\"\n [equals]=\"compareWithFn\"\n [logPrefix]=\"'[combo-mobile-1]'\"\n [required]=\"true\"\n placeholder=\"Suggest field\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Items is an Observable\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>mobile: true, items: Observable&lt;any[]&gt;</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"true\"\n [equals]=\"compareWithFn\"\n [logPrefix]=\"'[combo-mobile-observable]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Control value if missing in items\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>mobile: true, items: Observable&lt;any[]&gt;</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"missingEntity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"true\"\n [equals]=\"compareWithFn\"\n [logPrefix]=\"'[combo-mobile-2]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Disabled control\n </ion-label>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"disableEntity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"true\"\n [equals]=\"compareWithFn\"\n [logPrefix]=\"'[combo-mobile-2]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n </ion-row>\n\n <ion-row>\n <ion-col size=\"9\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Change filter\n </ion-label>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre></pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <ion-grid class=\"ion-no-padding\">\n <ion-row>\n <ion-col>\n <ion-button (click)=\"updateFilter('entity-items-filter')\">Filter on: {{autocompleteFields.get('entity-items-filter').filter?.searchAttribute }}</ion-button>\n </ion-col>\n <ion-col>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-filter')\"\n [mobile]=\"true\"\n [equals]=\"compareWithFn\"\n [logPrefix]=\"'[combo-mobile-filter]'\"\n ></mat-autocomplete-field>\n </ion-col>\n </ion-row>\n </ion-grid>\n\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col size=\"3\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Large combo\n </ion-label>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>class=\"min-width-large\"</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-large')\"\n [mobile]=\"true\"\n class=\"min-width-large\"\n panelWidth=\"400px\"\n [equals]=\"compareWithFn\"\n [logPrefix]=\"'[combo-mobile-large]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n </ion-row>\n <ion-row>\n <ion-col size=\"6\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Readonly toggle\n </ion-text>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <ion-grid>\n <ion-row>\n <ion-col size=\"3\">\n <mat-checkbox (change)=\"readonlyField.readonly=$event.checked\" [checked]=\"readonlyField.readonly\">\n Readonly ?\n </mat-checkbox>\n </ion-col>\n </ion-row>\n <ion-row>\n <ion-col>\n <mat-autocomplete-field #readonlyField formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items')\"\n [placeholder]=\"'Entity'\"\n [mobile]=\"true\"\n [equals]=\"compareWithFn\"\n [logPrefix]=\"'[combo-mobile-readonly]'\"\n [readonly]=\"true\"\n [clearable]=\"!readonlyField.readonly\">\n </mat-autocomplete-field>\n </ion-col>\n </ion-row>\n </ion-grid>\n </ion-card-content>\n </ion-card>\n </ion-col>\n </ion-row>\n </ion-grid>\n\n <!-- Desktop mode -->\n <ion-grid *ngIf=\"mode === 'desktop'\">\n <ion-row>\n <ion-col>\n <ion-text><h4>Desktop mode</h4></ion-text>\n </ion-col>\n </ion-row>\n <ion-row>\n\n\n <ion-col size=\"6\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n items from suggest function\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>suggest: (value, filter) => LoadResult&lt;any&gt;</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-filter')\"\n [placeholder]=\"'Entity'\"\n [mobile]=\"false\"\n [equals]=\"compareWithFn\"\n [logPrefix]=\"'[combo-desktop-suggest]'\">\n </mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col size=\"6\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n search with a suggestLengthThreshold\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small><pre>suggest: (value, filter) => LoadResult&lt;any&gt;\nsuggestLengthThreshold: '3'</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-filter')\"\n [placeholder]=\"'Entity'\"\n [mobile]=\"false\"\n [equals]=\"compareWithFn\"\n suggestLengthThreshold=\"3\"\n [logPrefix]=\"'[combo-desktop-suggest]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col size=\"6\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Items is an array\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>items: [], value: {{stringify(form.controls.entity.value)}}</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items')\"\n [logPrefix]=\"'[combo-desktop-array-1]'\"\n [equals]=\"compareWithFn\"\n [mobile]=\"false\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Items is an Observable\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>items: Observable&lt;any[]&gt;</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [logPrefix]=\"'[combo-desktop-2]'\"\n [equals]=\"compareWithFn\"\n [mobile]=\"false\"\n ></mat-autocomplete-field>\n </ion-card-content>\n\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Control value if missing in items\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>items: Observable&lt;any[]&gt;</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"missingEntity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [equals]=\"compareWithFn\"\n [logPrefix]=\"'[combo-desktop-missing]'\"\n [mobile]=\"false\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Full size panel\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>class: 'mat-autocomplete-panel-full-size'</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"false\"\n [class]=\"'mat-autocomplete-panel-full-size'\"\n [equals]=\"compareWithFn\"\n [matAutocompletePosition]=\"'above'\"\n [logPrefix]=\"'[combo-desktop-full-size]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Disabled control\n </ion-label>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"disableEntity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"false\"\n [equals]=\"compareWithFn\"\n [logPrefix]=\"'[combo-desktop-disable]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Readonly control\n </ion-label>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-$items')\"\n [mobile]=\"false\"\n [equals]=\"compareWithFn\"\n [logPrefix]=\"'[combo-desktop-readonly]'\"\n [readonly]=\"true\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n </ion-row>\n\n <ion-row>\n <ion-col size=\"9\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Full size combo\n </ion-label>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>class=\"mat-autocomplete-panel-full-size\"</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-large')\"\n [mobile]=\"false\"\n class=\"mat-autocomplete-panel-full-size\"\n panelWidth=\"100vw\"\n [equals]=\"compareWithFn\"\n [logPrefix]=\"'[combo-desktop-full-size]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col size=\"3\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Large combo\n </ion-label>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <small>\n <pre>panelWidth: string</pre>\n </small>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-autocomplete-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-large')\"\n [mobile]=\"false\"\n panelWidth=\"400px\"\n [equals]=\"compareWithFn\"\n [logPrefix]=\"'[combo-desktop-large]'\"\n ></mat-autocomplete-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col size=\"6\">\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Readonly toggle\n </ion-text>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <ion-grid>\n <ion-row>\n <ion-col size=\"3\">\n <mat-checkbox (change)=\"readonlyField.readonly=$event.checked\" [checked]=\"readonlyField.readonly\">\n Readonly ?\n </mat-checkbox>\n </ion-col>\n <ion-col size=\"3\">\n <mat-checkbox #showIcons [checked]=\"false\">\n Icons ?\n </mat-checkbox>\n </ion-col>\n </ion-row>\n <ion-row>\n <ion-col>\n <mat-autocomplete-field #readonlyField formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-items-filter')\"\n [placeholder]=\"'Entity'\"\n [mobile]=\"false\"\n [equals]=\"compareWithFn\"\n [logPrefix]=\"'[combo-desktop-readonly]'\"\n [readonly]=\"true\"\n [clearable]=\"!readonlyField.readonly\">\n <mat-icon matPrefix *ngIf=\"showIcons.checked\">keyboard_arrow_right</mat-icon>\n <mat-icon matSuffix *ngIf=\"showIcons.checked\">keyboard_arrow_left</mat-icon>\n </mat-autocomplete-field>\n </ion-col>\n </ion-row>\n </ion-grid>\n </ion-card-content>\n </ion-card>\n </ion-col>\n </ion-row>\n\n </ion-grid>\n\n </form>\n\n</ion-content>\n"
16371
16644
  },] }
16372
16645
  ];
16373
16646
  AutocompleteTestPage.ctorParameters = () => [
@@ -16522,7 +16795,7 @@ class SwipeTestPage {
16522
16795
  SwipeTestPage.decorators = [
16523
16796
  { type: Component, args: [{
16524
16797
  selector: 'app-swipe-test',
16525
- template: "<ion-header>\n <ion-toolbar color=\"primary\">\n\n <ion-buttons slot=\"start\">\n <ion-back-button></ion-back-button>\n </ion-buttons>\n\n <ion-title>Swipe test page</ion-title>\n </ion-toolbar>\n</ion-header>\n\n<ion-content class=\"ion-padding\">\n\n <h1>Swipe page:</h1>\n\n <form class=\"form-container\" [formGroup]=\"form\" (ngSubmit)=\"doSubmit($event)\">\n\n <ion-grid>\n\n <ion-row>\n <!-- empty -->\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Empty\n </ion-label>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-swipe-field formControlName=\"empty\"\n placeholder=\"placeholder\"\n [items]=\"$dates\"\n [displayWith]=\"displayDate()\"\n [compareWithFn]=\"compareDate()\"\n [tabindex]=\"1\"\n [debug]=\"true\"\n >\n </mat-swipe-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <!-- date -->\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Date\n </ion-label>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-swipe-field formControlName=\"date\"\n placeholder=\"placeholder\"\n [items]=\"$dates\"\n [displayWith]=\"displayDate()\"\n [compareWithFn]=\"compareDate()\"\n [clearable]=\"true\"\n [tabindex]=\"2\"\n [debug]=\"true\"\n >\n </mat-swipe-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n </ion-row>\n\n <ion-row>\n <!-- disable -->\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Disabled empty\n </ion-label>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-swipe-field formControlName=\"disabledEmpty\"\n placeholder=\"placeholder\"\n [items]=\"$dates\"\n [displayWith]=\"displayDate()\"\n [compareWithFn]=\"compareDate()\"\n [clearable]=\"true\"\n [tabindex]=\"3\"\n [debug]=\"true\"\n >\n </mat-swipe-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n<!-- read-only-->\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Disabled with date\n </ion-label>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-swipe-field formControlName=\"disabledDate\"\n placeholder=\"placeholder\"\n [items]=\"$dates\"\n [displayWith]=\"displayDate()\"\n [compareWithFn]=\"compareDate()\"\n [clearable]=\"true\"\n [tabindex]=\"4\"\n [debug]=\"true\"\n >\n </mat-swipe-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n </ion-row>\n\n </ion-grid>\n\n </form>\n\n</ion-content>\n"
16798
+ template: "<ion-header>\n <ion-toolbar color=\"primary\">\n\n <ion-buttons slot=\"start\">\n <ion-back-button></ion-back-button>\n </ion-buttons>\n\n <ion-title>Swipe test page</ion-title>\n </ion-toolbar>\n</ion-header>\n\n<ion-content class=\"ion-padding\">\n\n <h1>Swipe page:</h1>\n\n <form class=\"form-container\" [formGroup]=\"form\" (ngSubmit)=\"doSubmit($event)\">\n\n <ion-grid>\n\n <ion-row>\n <!-- empty -->\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Empty\n </ion-label>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-swipe-field formControlName=\"empty\"\n placeholder=\"placeholder\"\n [items]=\"$dates\"\n [displayWith]=\"displayDate()\"\n [equals]=\"compareDate()\"\n [tabindex]=\"1\"\n [debug]=\"true\"\n >\n </mat-swipe-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <!-- date -->\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Date\n </ion-label>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-swipe-field formControlName=\"date\"\n placeholder=\"placeholder\"\n [items]=\"$dates\"\n [displayWith]=\"displayDate()\"\n [equals]=\"compareDate()\"\n [clearable]=\"true\"\n [tabindex]=\"2\"\n [debug]=\"true\"\n >\n </mat-swipe-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n </ion-row>\n\n <ion-row>\n <!-- disable -->\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Disabled empty\n </ion-label>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-swipe-field formControlName=\"disabledEmpty\"\n placeholder=\"placeholder\"\n [items]=\"$dates\"\n [displayWith]=\"displayDate()\"\n [equals]=\"compareDate()\"\n [clearable]=\"true\"\n [tabindex]=\"3\"\n [debug]=\"true\"\n >\n </mat-swipe-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n<!-- read-only-->\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Disabled with date\n </ion-label>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-swipe-field formControlName=\"disabledDate\"\n placeholder=\"placeholder\"\n [items]=\"$dates\"\n [displayWith]=\"displayDate()\"\n [equals]=\"compareDate()\"\n [clearable]=\"true\"\n [tabindex]=\"4\"\n [debug]=\"true\"\n >\n </mat-swipe-field>\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n </ion-row>\n\n </ion-grid>\n\n </form>\n\n</ion-content>\n"
16526
16799
  },] }
16527
16800
  ];
16528
16801
  SwipeTestPage.ctorParameters = () => [
@@ -16628,7 +16901,11 @@ class EntityTestClass {
16628
16901
  const FAKE_ENTITIES = [
16629
16902
  { id: 1, label: 'AAA', name: 'Item A' },
16630
16903
  { id: 2, label: 'BBB', name: 'Item B' },
16631
- { id: 3, label: 'CCC', name: 'Item C' }
16904
+ { id: 3, label: 'CCC', name: 'Item C' },
16905
+ { id: 4, label: 'DDD', name: 'Item D' },
16906
+ { id: 5, label: 'EEE', name: 'Item E' },
16907
+ { id: 6, label: 'FFF', name: 'Item F' },
16908
+ { id: 7, label: 'GGG', name: 'Item G' }
16632
16909
  ];
16633
16910
  function deepCopy(values) {
16634
16911
  return (values || FAKE_ENTITIES).map(entity => Object.assign({}, entity));
@@ -16651,13 +16928,15 @@ class ChipsTestPage {
16651
16928
  this.autocompleteFields.add('entity-suggestFn', {
16652
16929
  suggestFn: (value, filter) => this.suggest(value, filter),
16653
16930
  attributes: ['label', 'name'],
16654
- displayWith: this.entityToString
16931
+ displayWith: this.entityToString,
16932
+ equals: (o1, o2) => EntityUtils.compare(o1, o2, 1, 'id') === 0
16655
16933
  });
16656
16934
  // From items
16657
16935
  this.autocompleteFields.add('entity-items', {
16658
16936
  items: FAKE_ENTITIES.slice(),
16659
16937
  attributes: ['label', 'name'],
16660
- displayWith: this.entityToString
16938
+ displayWith: this.entityToString,
16939
+ equals: (o1, o2) => EntityUtils.compare(o1, o2, 1, 'id') === 0
16661
16940
  });
16662
16941
  // From items
16663
16942
  this.autocompleteFields.add('entity-$items', {
@@ -16698,7 +16977,7 @@ class ChipsTestPage {
16698
16977
  doSubmit(event) {
16699
16978
  console.debug('Validate form: ', this.form.value);
16700
16979
  }
16701
- compareWithFn(o1, o2) {
16980
+ equals(o1, o2) {
16702
16981
  return o1 && o2 && o1.id === o2.id;
16703
16982
  }
16704
16983
  /* -- protected methods -- */
@@ -16709,7 +16988,7 @@ class ChipsTestPage {
16709
16988
  ChipsTestPage.decorators = [
16710
16989
  { type: Component, args: [{
16711
16990
  selector: 'app-chips-test',
16712
- template: "<ion-header>\n <ion-toolbar color=\"primary\">\n\n <ion-buttons slot=\"start\">\n <ion-back-button></ion-back-button>\n </ion-buttons>\n\n <ion-title>Chips field test page</ion-title>\n </ion-toolbar>\n</ion-header>\n\n<ion-content class=\"ion-padding\">\n\n <form class=\"form-container\" [formGroup]=\"form\" (ngSubmit)=\"doSubmit($event)\">\n\n <ion-grid>\n\n <ion-row>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Suggest() function\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <pre><small>suggestFn: (searchText, filter) =&gt; any[]</small></pre>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-chips-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-suggestFn')\"\n [logPrefix]=\"'[mat-chips-empty] '\"\n [compareWith]=\"compareWithFn\"\n ></mat-chips-field>\n {{stringify(form.controls.entity.value)}}\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Suggest() function with initial value\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <pre><small>suggestFn: (searchText, filter) =&gt; any[]</small></pre>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-chips-field formControlName=\"entityInitial\"\n [config]=\"autocompleteFields.get('entity-suggestFn')\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[mat-chips-initialized] '\"\n ></mat-chips-field>\n {{stringify(form.controls.entityInitial.value)}}\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Disabled control\n </ion-label>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-chips-field formControlName=\"disableEntity\"\n [config]=\"autocompleteFields.get('entity-suggestFn')\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[mat-chips-disabled] '\"\n ></mat-chips-field>\n {{stringify(form.controls.disableEntity.value)}}\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Readonly control\n </ion-label>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-chips-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-suggestFn')\"\n [compareWith]=\"compareWithFn\"\n [logPrefix]=\"'[mat-chips-readonly] '\"\n [readonly]=\"true\"\n ></mat-chips-field>\n {{stringify(form.controls.entity.value)}}\n </ion-card-content>\n </ion-card>\n </ion-col>\n </ion-row>\n </ion-grid>\n\n </form>\n\n</ion-content>\n"
16991
+ template: "<ion-header>\n <ion-toolbar color=\"primary\">\n\n <ion-buttons slot=\"start\">\n <ion-back-button></ion-back-button>\n </ion-buttons>\n\n <ion-title>Chips field test page</ion-title>\n </ion-toolbar>\n</ion-header>\n\n<ion-content class=\"ion-padding\">\n\n <form class=\"form-container\" [formGroup]=\"form\" (ngSubmit)=\"doSubmit($event)\">\n\n <ion-grid>\n\n <ion-row>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-text color=\"primary\">\n Suggest() function\n </ion-text>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <pre><small>suggestFn: (searchText, filter) =&gt; any[]</small></pre>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-chips-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-suggestFn')\"\n [logPrefix]=\"'[mat-chips-empty] '\"\n chipColor=\"accent\"\n [debug]=\"true\"\n ></mat-chips-field>\n {{stringify(form.controls.entity.value)}}\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Suggest() function with initial value\n </ion-label>\n </ion-card-title>\n <ion-card-subtitle>\n <ion-text color=\"medium\">\n <pre><small>suggestFn: (searchText, filter) =&gt; any[]</small></pre>\n </ion-text>\n </ion-card-subtitle>\n </ion-card-header>\n <ion-card-content>\n <mat-chips-field formControlName=\"entityInitial\"\n [config]=\"autocompleteFields.get('entity-suggestFn')\"\n [logPrefix]=\"'[mat-chips-initialized] '\"\n chipColor=\"primary\"\n [debug]=\"true\"\n ></mat-chips-field>\n {{stringify(form.controls.entityInitial.value)}}\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Disabled control\n </ion-label>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-chips-field formControlName=\"disableEntity\"\n [config]=\"autocompleteFields.get('entity-suggestFn')\"\n [equals]=\"equals\"\n [logPrefix]=\"'[mat-chips-disabled] '\"\n chipColor=\"warning\"\n ></mat-chips-field>\n {{stringify(form.controls.disableEntity.value)}}\n </ion-card-content>\n </ion-card>\n </ion-col>\n\n <ion-col>\n <ion-card>\n <ion-card-header>\n <ion-card-title>\n <ion-label color=\"primary\">\n Readonly control\n </ion-label>\n </ion-card-title>\n </ion-card-header>\n <ion-card-content>\n <mat-chips-field formControlName=\"entity\"\n [config]=\"autocompleteFields.get('entity-suggestFn')\"\n [equals]=\"equals\"\n [logPrefix]=\"'[mat-chips-readonly] '\"\n [readonly]=\"true\"\n ></mat-chips-field>\n {{stringify(form.controls.entity.value)}}\n </ion-card-content>\n </ion-card>\n </ion-col>\n </ion-row>\n </ion-grid>\n\n </form>\n\n</ion-content>\n"
16713
16992
  },] }
16714
16993
  ];
16715
16994
  ChipsTestPage.ctorParameters = () => [
@@ -17253,11 +17532,18 @@ AuthForm.propDecorators = {
17253
17532
  };
17254
17533
 
17255
17534
  class AuthModal {
17256
- constructor(accountService, viewCtrl, changeDetectorRef) {
17535
+ constructor(accountService, viewCtrl, cd) {
17257
17536
  this.accountService = accountService;
17258
17537
  this.viewCtrl = viewCtrl;
17259
- this.changeDetectorRef = changeDetectorRef;
17260
- this.loading = false;
17538
+ this.cd = cd;
17539
+ }
17540
+ get loading() {
17541
+ var _a;
17542
+ return (_a = this.form) === null || _a === void 0 ? void 0 : _a.loading;
17543
+ }
17544
+ ngOnInit() {
17545
+ this.form.markAsReady({ emitEvent: false });
17546
+ this.form.markAsLoaded();
17261
17547
  }
17262
17548
  cancel() {
17263
17549
  this.viewCtrl.dismiss();
@@ -17267,10 +17553,10 @@ class AuthModal {
17267
17553
  if (this.form.disabled)
17268
17554
  return;
17269
17555
  if (!this.form.valid) {
17270
- this.form.markAsTouched();
17556
+ this.form.markAllAsTouched();
17271
17557
  return;
17272
17558
  }
17273
- this.loading = true;
17559
+ this.markAsLoading();
17274
17560
  try {
17275
17561
  const data = this.form.value;
17276
17562
  // Disable the form
@@ -17279,9 +17565,8 @@ class AuthModal {
17279
17565
  return this.viewCtrl.dismiss(account);
17280
17566
  }
17281
17567
  catch (err) {
17282
- this.loading = false;
17283
17568
  this.form.error = err && err.message || err;
17284
- this.markForCheck();
17569
+ this.markAsLoaded();
17285
17570
  // Enable the form
17286
17571
  this.form.enable();
17287
17572
  // Reset form error on next changes
@@ -17294,7 +17579,13 @@ class AuthModal {
17294
17579
  });
17295
17580
  }
17296
17581
  markForCheck() {
17297
- this.changeDetectorRef.markForCheck();
17582
+ this.cd.markForCheck();
17583
+ }
17584
+ markAsLoading(opts) {
17585
+ this.form.markAsLoading(opts);
17586
+ }
17587
+ markAsLoaded(opts) {
17588
+ this.form.markAsLoaded(opts);
17298
17589
  }
17299
17590
  }
17300
17591
  AuthModal.decorators = [
@@ -20478,6 +20769,7 @@ class EntitiesTableDataSource extends TableDataSource {
20478
20769
  this._useValidator = false;
20479
20770
  this._stopWatching$ = new Subject();
20480
20771
  this._editingRowCount = 0;
20772
+ this._fetchMoreFn = null;
20481
20773
  this.loadingSubject = new BehaviorSubject(undefined);
20482
20774
  this._options = Object.assign({ dataServiceOptions: {}, debug: config && config.suppressErrors === false, keepOriginalDataAfterConfirm: false }, config);
20483
20775
  this._useValidator = isNotNil(validatorService);
@@ -20510,6 +20802,7 @@ class EntitiesTableDataSource extends TableDataSource {
20510
20802
  watchAll(offset, size, sortBy, sortDirection, filter) {
20511
20803
  this._stopWatching$.next();
20512
20804
  this._editingRowCount = 0;
20805
+ this._fetchMoreFn = null;
20513
20806
  this.markAsLoading();
20514
20807
  return this.dataService.watchAll(offset, size, sortBy, sortDirection, filter, this.serviceOptions)
20515
20808
  .pipe(
@@ -20523,6 +20816,7 @@ class EntitiesTableDataSource extends TableDataSource {
20523
20816
  }
20524
20817
  else {
20525
20818
  this.updateDatasource((res.data || []));
20819
+ this._fetchMoreFn = res.fetchMore;
20526
20820
  }
20527
20821
  return res;
20528
20822
  }));
@@ -20751,6 +21045,37 @@ class EntitiesTableDataSource extends TableDataSource {
20751
21045
  this._creating = false;
20752
21046
  });
20753
21047
  }
21048
+ getData() {
21049
+ return __awaiter(this, void 0, void 0, function* () {
21050
+ const rows = yield this.getRows();
21051
+ return this.getDataFromRows(rows);
21052
+ });
21053
+ }
21054
+ fetchMore(opts) {
21055
+ const _super = Object.create(null, {
21056
+ updateDatasource: { get: () => super.updateDatasource }
21057
+ });
21058
+ return __awaiter(this, void 0, void 0, function* () {
21059
+ if (!this._fetchMoreFn)
21060
+ return false; // Avoid multiple call
21061
+ if (this._editingRowCount > 0) {
21062
+ console.warn('Cannot fetch more because still editing row');
21063
+ return;
21064
+ }
21065
+ // Fetch next page
21066
+ this._fetchMoreFn = null; // Avoid multiple call
21067
+ const res = yield this._fetchMoreFn();
21068
+ // Skip if empty (no more data)
21069
+ if (isNotEmptyArray(res === null || res === void 0 ? void 0 : res.data))
21070
+ return false;
21071
+ // Update the data source
21072
+ const existingData = yield this.getData();
21073
+ _super.updateDatasource.call(this, existingData.concat(...res.data), opts);
21074
+ // Remember fetchMore
21075
+ this._fetchMoreFn = res.fetchMore;
21076
+ return true;
21077
+ });
21078
+ }
20754
21079
  /* -- protected method -- */
20755
21080
  markAsLoading() {
20756
21081
  if (this.loadingSubject.value !== true) {