@esfaenza/forms-and-validations 12.2.26 → 12.2.31

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.
@@ -1179,10 +1179,6 @@ class ValidationAutocompleteComponent extends BaseValidation {
1179
1179
  * @ignore
1180
1180
  */
1181
1181
  this.onTouched = () => { }; //placeholder on touched function
1182
- //******************** Funzione di throttling per non spammare richieste in caso di animazioni attivate
1183
- //TODO: spostarla in un metodo di utilità (esfaenza/extensions)
1184
- /** @ignore */
1185
- this.executionTimers = {};
1186
1182
  }
1187
1183
  /**
1188
1184
  * @ignore
@@ -1264,14 +1260,6 @@ class ValidationAutocompleteComponent extends BaseValidation {
1264
1260
  registerOnTouched(fn) {
1265
1261
  this.onTouched = fn;
1266
1262
  }
1267
- /** @ignore */
1268
- throttla(id, func, throttleTime) {
1269
- //Se ho la funzione che vuole eseguire ripulisco quel timeout
1270
- if (this.executionTimers[id])
1271
- clearTimeout(this.executionTimers[id]);
1272
- //Ricreo il timeout per eseguire quella funzione dopo throttleTime millisecondi
1273
- this.executionTimers[id] = setTimeout(() => { func(); this.executionTimers[id] = null; }, throttleTime);
1274
- }
1275
1263
  }
1276
1264
  ValidationAutocompleteComponent.decorators = [
1277
1265
  { type: Component, args: [{
@@ -1428,6 +1416,14 @@ class BaseFormControl {
1428
1416
  * Evento chiamato alla modifica del valore collegato a questo campo
1429
1417
  */
1430
1418
  this.inputChange = new EventEmitter();
1419
+ /**
1420
+ * Cache delle condizioni scritte tipo :prop?(Roba con {prop})
1421
+ */
1422
+ this.BindCheckingGroups = [];
1423
+ /**
1424
+ * Cache delle proprietà scritte tipo --> {prop}
1425
+ */
1426
+ this.BindProperties = [];
1431
1427
  if (ngControl == null) {
1432
1428
  if (!this.handleNullNgControl())
1433
1429
  console.error("ngControl nullo per qualche motivo! Il 90% delle funzionalità di questo input saranno disabilitate");
@@ -1514,35 +1510,63 @@ class BaseFormControl {
1514
1510
  // Cache locale per evitare di rifare dei regex.match ogni santa volta
1515
1511
  if (this.Source.length > 0) {
1516
1512
  this.BoundSource = [];
1517
- // Blocco per tirare fuori le condizioni scritte tipo --> :prop?(Roba con {prop})
1518
- var iffedMatches = this.Display.match(/:([a-z]+)\?\(([^\(\)]+)\)/g);
1519
- var bindCheckingGroups = [];
1520
- if (iffedMatches) {
1521
- iffedMatches.forEach(m => {
1522
- // Stessa regex di sopra ma il tag "i" serve per tirare fuori i singoli capturing group e per qualche motivo
1523
- // new RegExp(baseRegex, "g") non funziona quindi non ho potuto razionalizzarlo
1524
- let groups = m.match(/:([a-z]+)\?\(([^\(\)]+)\)/i);
1525
- bindCheckingGroups.push({ global: m, prop: groups[1], whenexists: groups[2] });
1526
- });
1527
- }
1528
- // Blocco per tirare fuori le proprietà scritte tipo --> {prop}
1529
- var bindProperties = [];
1530
- var matches = this.Display.match(/{[a-z]+}/gi);
1531
- if (matches)
1532
- matches.forEach(m => { bindProperties.push({ global: m, prop: m.substring(1, m.length - 1) }); });
1513
+ if (this.BindCheckingGroups.length == 0)
1514
+ this.evaluateBindCheckingGroups();
1515
+ if (this.BindProperties.length == 0)
1516
+ this.evaluateBindProperties();
1533
1517
  // Blocco per generare la descrizione finale di un elemento
1534
1518
  this.Source.forEach(s => {
1535
- // Parto sempre dalla variabile di Display, poi sostituisco pezzo per pezzo
1536
- let desc = this.Display;
1537
- // Taglio o mantengo le condizioni in base alla proprietà su cui fare check
1538
- // Dopodiché scrivo tutte le proprietà
1539
- bindCheckingGroups.forEach(t => { desc = desc.replace(t.global, (s[t.prop] != null && s[t.prop] != undefined) ? t.whenexists : ""); });
1540
- bindProperties.forEach(t => { desc = desc.replace(t.global, s[t.prop]); });
1541
1519
  // Aggiungo alla BoundSource in formato standard KeyValue
1542
- this.BoundSource.push({ id: s[this.IdField], description: desc });
1520
+ this.BoundSource.push(this.transformSourceItem(s));
1543
1521
  });
1544
1522
  }
1545
1523
  }
1524
+ /**
1525
+ * Valuta il contenuto della variabile BindCheckingGroups
1526
+ */
1527
+ evaluateBindCheckingGroups() {
1528
+ // Blocco per tirare fuori le condizioni scritte tipo --> :prop?(Roba con {prop})
1529
+ var iffedMatches = this.Display.match(/:([a-z]+)\?\(([^\(\)]+)\)/g);
1530
+ if (iffedMatches) {
1531
+ iffedMatches.forEach(m => {
1532
+ // Stessa regex di sopra ma il tag "i" serve per tirare fuori i singoli capturing group e per qualche motivo
1533
+ // new RegExp(baseRegex, "g") non funziona quindi non ho potuto razionalizzarlo
1534
+ let groups = m.match(/:([a-z]+)\?\(([^\(\)]+)\)/i);
1535
+ this.BindCheckingGroups.push({ global: m, prop: groups[1], whenexists: groups[2] });
1536
+ });
1537
+ }
1538
+ }
1539
+ /**
1540
+ * Valuta il contenuto della variabile BindProperties
1541
+ */
1542
+ evaluateBindProperties() {
1543
+ // Blocco per tirare fuori le proprietà scritte tipo --> {prop}
1544
+ var matches = this.Display.match(/{[a-z]+}/gi);
1545
+ if (matches)
1546
+ matches.forEach(m => { this.BindProperties.push({ global: m, prop: m.substring(1, m.length - 1) }); });
1547
+ }
1548
+ /**
1549
+ * Trasforma un oggetto della Source alla sua versione "nuova" basandosi sulle informazioni su gruppi e proprietà
1550
+ *
1551
+ * @param {any} item Oggetto da trasformare
1552
+ *
1553
+ * @returns {{id: string, description: string}} Oggetto finale trasformato
1554
+ */
1555
+ transformSourceItem(s) {
1556
+ // Parto sempre dalla variabile di Display, poi sostituisco pezzo per pezzo
1557
+ let desc = this.Display;
1558
+ // Taglio o mantengo le condizioni in base alla proprietà su cui fare check
1559
+ // Dopodiché scrivo tutte le proprietà
1560
+ for (let i = 0; i < this.BindCheckingGroups.length; i++) {
1561
+ let t = this.BindCheckingGroups[i];
1562
+ desc = desc.replace(t.global, (s[t.prop] != null && s[t.prop] != undefined) ? t.whenexists : "");
1563
+ }
1564
+ for (let i = 0; i < this.BindProperties.length; i++) {
1565
+ let t = this.BindProperties[i];
1566
+ desc = desc.replace(t.global, s[t.prop]);
1567
+ }
1568
+ return { id: s[this.IdField], description: desc };
1569
+ }
1546
1570
  /**
1547
1571
  * Indica se il comopnente in questione è in grado di gestire ngControl nulli.
1548
1572
  * Di default è **false**
@@ -1934,10 +1958,23 @@ class FormAdaptiveComponent extends BaseFormControl {
1934
1958
  * Numero minimo di caratteri con cui cercare
1935
1959
  */
1936
1960
  this.MinChars = 3;
1961
+ /**
1962
+ * Indica se i controlli devono essere effettuati tenendo conto del Case o meno. Vale solo qualora la **Source** fosse fornita
1963
+ */
1964
+ this.CaseSensitive = false;
1937
1965
  /**
1938
1966
  * Variabile interna che gestisce se effettuare il riallineamento dei dati o meno
1939
1967
  */
1940
1968
  this.alignValues = false;
1969
+ /**
1970
+ * Indica se ignorare il prossimo evento writeValue che normalmente dovrebbe richiedere la nuova source. Serve per quando l'utente seleziona un elemento:
1971
+ * Subito dopo partirebbe un altro evento modelChange che ricaricherebbe nuovamente la source
1972
+ */
1973
+ this.ignoreNextWriteValue = false;
1974
+ /**
1975
+ * Cache della sorgente originale POST binding, in modo da poter fare filtri in locale qualora la **SearchFunction** non fosse definita e la **Source** fosse assegnata
1976
+ */
1977
+ this.FilteredBoundSource = [];
1941
1978
  //******************** Funzione di throttling per non spammare richieste in caso di animazioni attivate
1942
1979
  //TODO: spostarla in un metodo di utilità (esfaenza/extensions)
1943
1980
  /** @ignore */
@@ -1948,11 +1985,19 @@ class FormAdaptiveComponent extends BaseFormControl {
1948
1985
  * @ignore
1949
1986
  */
1950
1987
  ngOnChanges(changes) {
1988
+ var _a;
1951
1989
  return __awaiter(this, void 0, void 0, function* () {
1952
1990
  const newSource = changes["Source"];
1953
1991
  const newType = changes["Type"];
1954
- if (newSource)
1992
+ if (newSource) {
1955
1993
  this.tryBindSourceDisplay();
1994
+ // Prima assegnazione se cambia la source sotto (quindi se viene bindata direttamente da HTML)
1995
+ if (this.Type == "autocomplete" && (((_a = this.Model) === null || _a === void 0 ? void 0 : _a.length) || 0) >= this.MinChars && !this.SearchFunction)
1996
+ this.FilteredBoundSource = this.BoundSource;
1997
+ else
1998
+ this.FilteredBoundSource = [];
1999
+ this.cdr.markForCheck();
2000
+ }
1956
2001
  if (newType && this.Model)
1957
2002
  this.writeValue(this.Model);
1958
2003
  });
@@ -1978,14 +2023,16 @@ class FormAdaptiveComponent extends BaseFormControl {
1978
2023
  if (this.Type == "file")
1979
2024
  obj = this.ModelFile;
1980
2025
  if (this.Type == "autocomplete") {
1981
- this.SearchFunction(obj, true).subscribe(t => {
1982
- this.Source = t;
1983
- setTimeout(() => {
1984
- var val = this.Source.find(t => t.id == obj);
1985
- this.propagateChange(val ? val.id : obj);
1986
- this.Model = val ? val.description : obj;
2026
+ if (this.SearchFunction) {
2027
+ this.SearchFunction(obj, true).subscribe(t => {
2028
+ this.Source = t;
2029
+ this.tryBindSourceDisplay();
2030
+ setTimeout(() => { this.finalizeValue(obj); });
1987
2031
  });
1988
- });
2032
+ return;
2033
+ }
2034
+ else
2035
+ this.finalizeValue(obj);
1989
2036
  }
1990
2037
  super.writeValue(obj);
1991
2038
  if (this.alignValues) {
@@ -1993,24 +2040,56 @@ class FormAdaptiveComponent extends BaseFormControl {
1993
2040
  this.cdr.markForCheck();
1994
2041
  }
1995
2042
  }
2043
+ /**
2044
+ * Dato un valore verifica se può restituire le informazioni trovate in **Source** con id uguale a **value** o se deve restituire il valore in se
2045
+ *
2046
+ * @param {any} value Valore scritto nell'input di testo
2047
+ */
2048
+ finalizeValue(value) {
2049
+ var val = this.Source.find(t => t.id == value);
2050
+ this.propagateChange(val ? val.id : value);
2051
+ this.Model = val ? val.description : value;
2052
+ }
1996
2053
  /**
1997
2054
  * Evento di filtro della sorgente dati in base all'input utente
1998
2055
  *
1999
2056
  * @param {string} event Input utente
2000
2057
  */
2001
2058
  filterSource(event) {
2002
- if (event.length < this.MinChars) {
2003
- this.Source = [];
2059
+ if (this.ignoreNextWriteValue) {
2060
+ this.ignoreNextWriteValue = false;
2004
2061
  return;
2005
2062
  }
2006
- if (!this.SearchFunction)
2063
+ // Quando filtro la source, se non devo ignorare l'evento devo comunque assicurarmi di impostare il valore selezionato a null
2064
+ super.changed("");
2065
+ if (!event && this.MinChars == 0 && !this.SearchFunction) {
2066
+ this.FilteredBoundSource = this.BoundSource;
2067
+ return;
2068
+ }
2069
+ if (!event || event.length < this.MinChars) {
2070
+ this.FilteredBoundSource = [];
2071
+ return;
2072
+ }
2073
+ if (!this.SearchFunction && (!this.Source || this.Source.length == 0))
2007
2074
  throw "Impossibile filtrare gli elementi senza una funzione di ricerca che restituisca una lista di { id: string, description: string }";
2008
- this.throttla("filtersource", () => {
2009
- this.SearchFunction(event, false).subscribe(t => {
2010
- this.Source = t;
2011
- this.tryBindSourceDisplay();
2012
- });
2013
- }, 400);
2075
+ if (this.SearchFunction) {
2076
+ this.throttla("filtersource", () => {
2077
+ this.SearchFunction(event, false).subscribe(t => {
2078
+ this.Source = t;
2079
+ this.tryBindSourceDisplay();
2080
+ // In questo caso è già filtrata dalla SearchFunction
2081
+ this.FilteredBoundSource = this.BoundSource;
2082
+ this.cdr.markForCheck();
2083
+ });
2084
+ }, 400);
2085
+ }
2086
+ else {
2087
+ this.throttla("filtersource", () => {
2088
+ // In questo caso devo filtrare io in memoria
2089
+ this.FilteredBoundSource = this.BoundSource.filter(t => (this.CaseSensitive && t.description.includes(event)) || (!this.CaseSensitive && t.description.toLowerCase().includes(event.toLowerCase())));
2090
+ this.cdr.markForCheck();
2091
+ }, 100);
2092
+ }
2014
2093
  }
2015
2094
  /**
2016
2095
  * Metodo richiamato quando viene modificato il modello del campo di input
@@ -2025,6 +2104,8 @@ class FormAdaptiveComponent extends BaseFormControl {
2025
2104
  var toEmit = this.dateAdapter.clone(this.Model);
2026
2105
  toEmit = toEmit.format("HH:mm:ss");
2027
2106
  }
2107
+ if (this.Type == "autocomplete")
2108
+ this.ignoreNextWriteValue = true;
2028
2109
  super.changed(toEmit);
2029
2110
  }
2030
2111
  /**
@@ -2076,7 +2157,7 @@ FormAdaptiveComponent.decorators = [
2076
2157
  { type: Component, args: [{
2077
2158
  selector: "form-adaptive",
2078
2159
  providers: [{ provide: LocalizationService, useClass: FormAdaptiveComponentLoc }],
2079
- template: "<ng-container *ngIf=\"!FormLayout\">\r\n <ng-container *ngTemplateOutlet=\"controlTemplate\"></ng-container>\r\n</ng-container>\r\n\r\n<div *ngIf=\"FormLayout\" class=\"form-group row {{FormGroupClass}}\" [class.app-margin-bottom-0]=\"Last\">\r\n <label class=\"col-md-{{LabelColWidth}} m-t-5\">{{Label}}{{Required ? '*' : ''}}:</label>\r\n <div class=\"col-md-{{InputColWidth}}\">\r\n <ng-container *ngTemplateOutlet=\"controlTemplate\"></ng-container>\r\n </div>\r\n <div class=\"clearfix\"></div>\r\n</div>\r\n\r\n<ng-template #controlTemplate>\r\n <div *ngIf=\"!Type\" class=\"app-margin-top-5\">\r\n <em>{{TypeMissingMessage}}</em>\r\n </div>\r\n \r\n <!--Se currency-->\r\n <div *ngIf=\"Type == 'currency'\">\r\n <val-currency [forceInvalid]=\"ForcedError\"\r\n [CurrencyOptions]=\"{ prefix: '', thousands: '.', decimal: ',', precision: Precision, align: Alignment }\" [noValidate]=\"!Validation\"\r\n #validationControl=\"ngModel\" type=\"text\" [readonly]=\"Readonly\" [submitted]=\"Form?.submitted\" [required]=\"Required\" [(ngModel)]=\"Model\" name=\"{{GeneratedName}}\" autocomplete=\"off\" (inputChange)=\"changed();\"></val-currency>\r\n </div>\r\n <!--Se data-->\r\n <div *ngIf=\"Type == 'date'\">\r\n <val-date #validationControl=\"ngModel\" [forceInvalid]=\"ForcedError\" [noValidate]=\"!Validation\" [readonly]=\"Readonly\" [submitted]=\"Form?.submitted\" [required]=\"Required\" [(ngModel)]=\"Model\" name=\"{{GeneratedName}}\" autocomplete=\"off\" (inputChange)=\"changed();\"></val-date>\r\n </div>\r\n <!--Se stringa-->\r\n <div *ngIf=\"Type == 'string'\">\r\n <val-input #validationControl=\"ngModel\" [forceInvalid]=\"ForcedError\" [noValidate]=\"!Validation\" [readonly]=\"Readonly\" [submitted]=\"Form?.submitted\" type=\"text\" pattern=\"{{Pattern || ''}}\" [required]=\"Required\" [(ngModel)]=\"Model\" name=\"{{GeneratedName}}\" autocomplete=\"off\" (inputChange)=\"changed();\"></val-input>\r\n </div>\r\n <!--Se numero-->\r\n <div *ngIf=\"Type == 'float' || Type == 'number'\">\r\n <val-input #validationControl=\"ngModel\" [forceInvalid]=\"ForcedError\" [noValidate]=\"!Validation\" [readonly]=\"Readonly\" [submitted]=\"Form?.submitted\" type=\"text\" pattern=\"{{Pattern || '^([0-9]*[,])?[0-9]+$'}}\" [required]=\"Required\" [(ngModel)]=\"Model\" name=\"{{GeneratedName}}\" autocomplete=\"off\" (inputChange)=\"changed();\"></val-input>\r\n </div>\r\n <!--Se numero intero-->\r\n <div *ngIf=\"Type == 'int'\">\r\n <val-input #validationControl=\"ngModel\" [forceInvalid]=\"ForcedError\" [noValidate]=\"!Validation\" [readonly]=\"Readonly\" [submitted]=\"Form?.submitted\" type=\"text\" pattern=\"{{Pattern || '^[0-9]\\\\d*$'}}\" [required]=\"Required\" [(ngModel)]=\"Model\" name=\"{{GeneratedName}}\" autocomplete=\"off\" (inputChange)=\"changed();\"></val-input>\r\n </div>\r\n <!--Se boolean-->\r\n <div class=\"m-t-5\" *ngIf=\"Type == 'boolean'\">\r\n <input #validationControl=\"ngModel\" [readonly]=\"Readonly\" type=\"checkbox\" class=\"app-pointer\" [(ngModel)]=\"Model\" name=\"{{GeneratedName}}\" (ngModelChange)=\"changed();\" />\r\n </div>\r\n <!--Se enum-->\r\n <div *ngIf=\"Type == 'enum'\">\r\n <val-select #validationControl=\"ngModel\" [forceInvalid]=\"ForcedError\" [noValidate]=\"!Validation\" [readonly]=\"Readonly\" [submitted]=\"Form?.submitted\" [placeHolderValue]=\"''\" [placeholder]=\"Required ? ('Select' | localize : lc) + '...' : Placeholder\" [required]=\"Required\" [(ngModel)]=\"Model\" (inputChange)=\"changed();\" name=\"{{GeneratedName}}\">\r\n <option *ngFor=\"let val of BoundSource\" [value]=\"val.id\">{{val.description}}</option>\r\n </val-select>\r\n </div>\r\n <!--Se autocomplete-->\r\n <div *ngIf=\"Type == 'autocomplete'\">\r\n <val-autocomplete #validationControl=\"ngModel\" [forceInvalid]=\"ForcedError\" [noValidate]=\"!Validation\" [readonly]=\"Readonly\" [submitted]=\"Form?.submitted\" [required]=\"Required\" [placeholder]=\"Required ? ('Select' | localize : lc) + '...' : Placeholder\" [(ngModel)]=\"Model\" name=\"{{GeneratedName}}\" [FilteredSource]=\"BoundSource\" (inputChange)=\"filterSource($event); changed();\"></val-autocomplete>\r\n </div>\r\n <!--Se date time-->\r\n <div *ngIf=\"Type == 'datetime'\">\r\n <val-datetime #validationControl=\"ngModel\" [forceInvalid]=\"ForcedError\" [noValidate]=\"!Validation\" [readonly]=\"Readonly\" [submitted]=\"Form?.submitted\" [required]=\"Required\" [(ngModel)]=\"Model\" name=\"{{GeneratedName}}\" autocomplete=\"off\" (inputChange)=\"changed();\"></val-datetime>\r\n </div>\r\n <!--Se time-->\r\n <div *ngIf=\"Type == 'time'\">\r\n <ngx-mat-timepicker name=\"val-time\" #elementRef #baseInput=\"ngModel\" [(ngModel)]=\"Model\" [disabled]=\"Readonly\"\r\n [showSpinners]=\"false\" [stepHour]=\"2\" [stepMinute]=\"5\" [stepSecond]=\"30\"\r\n [showSeconds]=\"true\" (ngModelChange)=\"changed()\" #validationControl=\"ngModel\">\r\n </ngx-mat-timepicker>\r\n </div>\r\n <!--Se file-->\r\n <div *ngIf=\"Type == 'file'\">\r\n <div class=\"input-group file-upload\">\r\n <input type=\"file\" (change)=\"fileChange()\" #fileInput class=\"file-upload-btn app-pointer\" [multiple]=\"null\"/>\r\n <input type=\"text\" [class.frm-padding-left-22]=\"AllowDownload && ModelFile.filename && ModelFile.fileb64\" class=\"form-control checking-field\" placeholder=\"{{'Select a file' | localize : lc}}...\" [(ngModel)]=\"ModelFile.filename\" name=\"dsfile\" #validationControl=\"ngModel\" [required]=\"Required\"/>\r\n \r\n <a class=\"fa fa-download app-pointer app-input-icon\" *ngIf=\"AllowDownload && ModelFile.filename && ModelFile.fileb64\" (click)=\"downloadAttachment()\"></a>\r\n <i class=\"fa fa-times delete-file\" (click)=\"fileChange(true)\" *ngIf=\"ModelFile.filename\"></i>\r\n <span class=\"input-group-btn\">\r\n <button class=\"btn btn-primary btn-file-upload\" type=\"button\"><i class=\"fa fa-upload\"></i></button>\r\n </span>\r\n </div>\r\n </div>\r\n</ng-template>",
2160
+ template: "<ng-container *ngIf=\"!FormLayout\">\r\n <ng-container *ngTemplateOutlet=\"controlTemplate\"></ng-container>\r\n</ng-container>\r\n\r\n<div *ngIf=\"FormLayout\" class=\"form-group row {{FormGroupClass}}\" [class.app-margin-bottom-0]=\"Last\">\r\n <label class=\"col-md-{{LabelColWidth}} m-t-5\">{{Label}}{{Required ? '*' : ''}}:</label>\r\n <div class=\"col-md-{{InputColWidth}}\">\r\n <ng-container *ngTemplateOutlet=\"controlTemplate\"></ng-container>\r\n </div>\r\n <div class=\"clearfix\"></div>\r\n</div>\r\n\r\n<ng-template #controlTemplate>\r\n <div *ngIf=\"!Type\" class=\"app-margin-top-5\">\r\n <em>{{TypeMissingMessage}}</em>\r\n </div>\r\n \r\n <!--Se currency-->\r\n <div *ngIf=\"Type == 'currency'\">\r\n <val-currency [forceInvalid]=\"ForcedError\"\r\n [CurrencyOptions]=\"{ prefix: '', thousands: '.', decimal: ',', precision: Precision, align: Alignment }\" [noValidate]=\"!Validation\"\r\n #validationControl=\"ngModel\" type=\"text\" [readonly]=\"Readonly\" [submitted]=\"Form?.submitted\" [required]=\"Required\" [(ngModel)]=\"Model\" name=\"{{GeneratedName}}\" autocomplete=\"off\" (inputChange)=\"changed();\"></val-currency>\r\n </div>\r\n <!--Se data-->\r\n <div *ngIf=\"Type == 'date'\">\r\n <val-date #validationControl=\"ngModel\" [forceInvalid]=\"ForcedError\" [noValidate]=\"!Validation\" [readonly]=\"Readonly\" [submitted]=\"Form?.submitted\" [required]=\"Required\" [(ngModel)]=\"Model\" name=\"{{GeneratedName}}\" autocomplete=\"off\" (inputChange)=\"changed();\"></val-date>\r\n </div>\r\n <!--Se stringa-->\r\n <div *ngIf=\"Type == 'string'\">\r\n <val-input #validationControl=\"ngModel\" [forceInvalid]=\"ForcedError\" [noValidate]=\"!Validation\" [readonly]=\"Readonly\" [submitted]=\"Form?.submitted\" type=\"text\" pattern=\"{{Pattern || ''}}\" [required]=\"Required\" [(ngModel)]=\"Model\" name=\"{{GeneratedName}}\" autocomplete=\"off\" (inputChange)=\"changed();\"></val-input>\r\n </div>\r\n <!--Se numero-->\r\n <div *ngIf=\"Type == 'float' || Type == 'number'\">\r\n <val-input #validationControl=\"ngModel\" [forceInvalid]=\"ForcedError\" [noValidate]=\"!Validation\" [readonly]=\"Readonly\" [submitted]=\"Form?.submitted\" type=\"text\" pattern=\"{{Pattern || '^([0-9]*[,])?[0-9]+$'}}\" [required]=\"Required\" [(ngModel)]=\"Model\" name=\"{{GeneratedName}}\" autocomplete=\"off\" (inputChange)=\"changed();\"></val-input>\r\n </div>\r\n <!--Se numero intero-->\r\n <div *ngIf=\"Type == 'int'\">\r\n <val-input #validationControl=\"ngModel\" [forceInvalid]=\"ForcedError\" [noValidate]=\"!Validation\" [readonly]=\"Readonly\" [submitted]=\"Form?.submitted\" type=\"text\" pattern=\"{{Pattern || '^[0-9]\\\\d*$'}}\" [required]=\"Required\" [(ngModel)]=\"Model\" name=\"{{GeneratedName}}\" autocomplete=\"off\" (inputChange)=\"changed();\"></val-input>\r\n </div>\r\n <!--Se boolean-->\r\n <div class=\"m-t-5\" *ngIf=\"Type == 'boolean'\">\r\n <input #validationControl=\"ngModel\" [readonly]=\"Readonly\" type=\"checkbox\" class=\"app-pointer\" [(ngModel)]=\"Model\" name=\"{{GeneratedName}}\" (ngModelChange)=\"changed();\" />\r\n </div>\r\n <!--Se enum-->\r\n <div *ngIf=\"Type == 'enum'\">\r\n <val-select #validationControl=\"ngModel\" [forceInvalid]=\"ForcedError\" [noValidate]=\"!Validation\" [readonly]=\"Readonly\" [submitted]=\"Form?.submitted\" [placeHolderValue]=\"''\" [placeholder]=\"Required ? ('Select' | localize : lc) + '...' : Placeholder\" [required]=\"Required\" [(ngModel)]=\"Model\" (inputChange)=\"changed();\" name=\"{{GeneratedName}}\">\r\n <option *ngFor=\"let val of BoundSource\" [value]=\"val.id\">{{val.description}}</option>\r\n </val-select>\r\n </div>\r\n <!--Se autocomplete-->\r\n <div *ngIf=\"Type == 'autocomplete'\">\r\n <val-autocomplete #validationControl=\"ngModel\" [forceInvalid]=\"ForcedError\" [noValidate]=\"!Validation\" [readonly]=\"Readonly\" [submitted]=\"Form?.submitted\" [required]=\"Required\" [placeholder]=\"Required ? ('Select' | localize : lc) + '...' : Placeholder\" [(ngModel)]=\"Model\" name=\"{{GeneratedName}}\" [FilteredSource]=\"FilteredBoundSource\" (inputChange)=\"filterSource($event); changed();\"></val-autocomplete>\r\n </div>\r\n <!--Se date time-->\r\n <div *ngIf=\"Type == 'datetime'\">\r\n <val-datetime #validationControl=\"ngModel\" [forceInvalid]=\"ForcedError\" [noValidate]=\"!Validation\" [readonly]=\"Readonly\" [submitted]=\"Form?.submitted\" [required]=\"Required\" [(ngModel)]=\"Model\" name=\"{{GeneratedName}}\" autocomplete=\"off\" (inputChange)=\"changed();\"></val-datetime>\r\n </div>\r\n <!--Se time-->\r\n <div *ngIf=\"Type == 'time'\">\r\n <ngx-mat-timepicker name=\"val-time\" #elementRef #baseInput=\"ngModel\" [(ngModel)]=\"Model\" [disabled]=\"Readonly\"\r\n [showSpinners]=\"false\" [stepHour]=\"2\" [stepMinute]=\"5\" [stepSecond]=\"30\"\r\n [showSeconds]=\"true\" (ngModelChange)=\"changed()\" #validationControl=\"ngModel\">\r\n </ngx-mat-timepicker>\r\n </div>\r\n <!--Se file-->\r\n <div *ngIf=\"Type == 'file'\">\r\n <div class=\"input-group file-upload\">\r\n <input type=\"file\" (change)=\"fileChange()\" #fileInput class=\"file-upload-btn app-pointer\" [multiple]=\"null\"/>\r\n <input type=\"text\" [class.frm-padding-left-22]=\"AllowDownload && ModelFile.filename && ModelFile.fileb64\" class=\"form-control checking-field\" placeholder=\"{{'Select a file' | localize : lc}}...\" [(ngModel)]=\"ModelFile.filename\" name=\"dsfile\" #validationControl=\"ngModel\" [required]=\"Required\"/>\r\n \r\n <a class=\"fa fa-download app-pointer app-input-icon\" *ngIf=\"AllowDownload && ModelFile.filename && ModelFile.fileb64\" (click)=\"downloadAttachment()\"></a>\r\n <i class=\"fa fa-times delete-file\" (click)=\"fileChange(true)\" *ngIf=\"ModelFile.filename\"></i>\r\n <span class=\"input-group-btn\">\r\n <button class=\"btn btn-primary btn-file-upload\" type=\"button\"><i class=\"fa fa-upload\"></i></button>\r\n </span>\r\n </div>\r\n </div>\r\n</ng-template>",
2080
2161
  changeDetection: ChangeDetectionStrategy.OnPush,
2081
2162
  styles: [".frm-padding-left-22{padding-left:22px}\n"]
2082
2163
  },] }
@@ -2101,7 +2182,8 @@ FormAdaptiveComponent.propDecorators = {
2101
2182
  Precision: [{ type: Input }],
2102
2183
  Alignment: [{ type: Input }],
2103
2184
  SearchFunction: [{ type: Input }],
2104
- MinChars: [{ type: Input }]
2185
+ MinChars: [{ type: Input }],
2186
+ CaseSensitive: [{ type: Input }]
2105
2187
  };
2106
2188
 
2107
2189
  // Angular
@@ -2185,7 +2267,7 @@ class FormTemplateComponent {
2185
2267
  FormTemplateComponent.decorators = [
2186
2268
  { type: Component, args: [{
2187
2269
  selector: "form-template",
2188
- template: "<ng-container *ngIf=\"!FormLayout\">\r\n <ng-content></ng-content>\r\n</ng-container>\r\n\r\n<div *ngIf=\"FormLayout\" class=\"form-group row {{FormGroupClass}}\" [class.app-margin-bottom-0]=\"Last\">\r\n <label class=\"col-md-{{LabelColWidth}} m-t-5\">{{Label}}{{Required ? '*' : ''}}:</label>\r\n <div class=\"col-md-{{InputColWidth}}\">\r\n <ng-content></ng-content>\r\n </div>\r\n <div class=\"clearfix\"></div>\r\n</div>",
2270
+ template: "<ng-container *ngIf=\"!FormLayout\">\r\n <ng-content></ng-content>\r\n</ng-container>\r\n\r\n<div *ngIf=\"FormLayout\" class=\"form-group row {{FormGroupClass}}\" [class.app-margin-bottom-0]=\"Last\">\r\n <label class=\"col-md-{{LabelColWidth}} m-t-5\">{{Label}}{{Required ? '*' : ''}}<span *ngIf=\"Label\">:</span></label>\r\n <div class=\"col-md-{{InputColWidth}}\">\r\n <ng-content></ng-content>\r\n </div>\r\n <div class=\"clearfix\"></div>\r\n</div>",
2189
2271
  changeDetection: ChangeDetectionStrategy.OnPush
2190
2272
  },] }
2191
2273
  ];
@@ -2653,7 +2735,8 @@ class FormMultiSelectComponent extends BaseFormControl {
2653
2735
  text: this.SelectLabel || this.lc.loc("Select one or more values..."),
2654
2736
  enableCheckAll: true,
2655
2737
  disabled: disabled,
2656
- labelKey: "description"
2738
+ labelKey: "description",
2739
+ tagToBody: false
2657
2740
  };
2658
2741
  }
2659
2742
  /**
@@ -2808,11 +2891,19 @@ class FormAutocompleteComponent extends BaseFormControl {
2808
2891
  * Override del placeholder per select requried
2809
2892
  */
2810
2893
  this.RequiredPlaceholder = null;
2894
+ /**
2895
+ * Indica se i controlli devono essere effettuati tenendo conto del Case o meno. Vale solo qualora la **Source** fosse fornita
2896
+ */
2897
+ this.CaseSensitive = false;
2811
2898
  /**
2812
2899
  * Indica se ignorare il prossimo evento writeValue che normalmente dovrebbe richiedere la nuova source. Serve per quando l'utente seleziona un elemento:
2813
2900
  * Subito dopo partirebbe un altro evento modelChange che ricaricherebbe nuovamente la source
2814
2901
  */
2815
2902
  this.ignoreNextWriteValue = false;
2903
+ /**
2904
+ * Sorgente Bindata Filtrata in base al contenuto della casella di testo
2905
+ */
2906
+ this.FilteredBoundSource = [];
2816
2907
  //******************** Funzione di throttling per non spammare richieste in caso di animazioni attivate
2817
2908
  //TODO: spostarla in un metodo di utilità (esfaenza/extensions)
2818
2909
  /** @ignore */
@@ -2822,16 +2913,27 @@ class FormAutocompleteComponent extends BaseFormControl {
2822
2913
  * @ignore
2823
2914
  */
2824
2915
  writeValue(value) {
2825
- if (value) {
2916
+ if (!value)
2917
+ return;
2918
+ if (this.SearchFunction) {
2826
2919
  this.SearchFunction(value, true).subscribe(t => {
2827
2920
  this.Source = t;
2828
- setTimeout(() => {
2829
- var val = this.Source.find(t => t.id == value);
2830
- this.propagateChange(val ? val.id : value);
2831
- this.Model = val ? val.description : value;
2832
- });
2921
+ this.tryBindSourceDisplay();
2922
+ setTimeout(() => { this.finalizeValue(value); });
2833
2923
  });
2924
+ return;
2834
2925
  }
2926
+ this.finalizeValue(value);
2927
+ }
2928
+ /**
2929
+ * Dato un valore verifica se può restituire le informazioni trovate in **Source** con id uguale a **value** o se deve restituire il valore in se
2930
+ *
2931
+ * @param {any} value Valore scritto nell'input di testo
2932
+ */
2933
+ finalizeValue(value) {
2934
+ var val = this.Source.find(t => t.id == value);
2935
+ this.propagateChange(val ? val.id : value);
2936
+ this.Model = val ? val.description : value;
2835
2937
  }
2836
2938
  /**
2837
2939
  * Evento di filtro della sorgente dati in base all'input utente
@@ -2845,18 +2947,50 @@ class FormAutocompleteComponent extends BaseFormControl {
2845
2947
  }
2846
2948
  // Quando filtro la source, se non devo ignorare l'evento devo comunque assicurarmi di impostare il valore selezionato a null
2847
2949
  super.changed("");
2950
+ if (!event && this.MinChars == 0 && !this.SearchFunction) {
2951
+ this.FilteredBoundSource = this.BoundSource;
2952
+ return;
2953
+ }
2848
2954
  if (!event || event.length < this.MinChars) {
2849
- this.Source = [];
2955
+ this.FilteredBoundSource = [];
2850
2956
  return;
2851
2957
  }
2852
- if (!this.SearchFunction)
2958
+ if (!this.SearchFunction && (!this.Source || this.Source.length == 0))
2853
2959
  throw "Impossibile filtrare gli elementi senza una funzione di ricerca che restituisca una lista di { id: string, description: string }";
2854
- this.throttla("filtersource", () => {
2855
- this.SearchFunction(event, false).subscribe(t => {
2856
- this.Source = t;
2857
- this.tryBindSourceDisplay();
2858
- });
2859
- }, 400);
2960
+ if (this.SearchFunction) {
2961
+ this.throttla("filtersource", () => {
2962
+ this.SearchFunction(event, false).subscribe(t => {
2963
+ this.Source = t;
2964
+ this.tryBindSourceDisplay();
2965
+ // In questo caso è già filtrata dalla SearchFunction
2966
+ this.FilteredBoundSource = this.BoundSource;
2967
+ this.cdr.markForCheck();
2968
+ });
2969
+ }, 400);
2970
+ }
2971
+ else {
2972
+ this.throttla("filtersource", () => {
2973
+ // In questo caso devo filtrare io in memoria
2974
+ this.FilteredBoundSource = this.BoundSource.filter(t => (this.CaseSensitive && t.description.includes(event)) || (!this.CaseSensitive && t.description.toLowerCase().includes(event.toLowerCase())));
2975
+ this.cdr.markForCheck();
2976
+ }, 100);
2977
+ }
2978
+ }
2979
+ /**
2980
+ * @ignore
2981
+ */
2982
+ ngOnChanges(changes) {
2983
+ var _a;
2984
+ let newSource = changes["Source"];
2985
+ if (newSource) {
2986
+ this.tryBindSourceDisplay();
2987
+ // Prima assegnazione se cambia la source sotto (quindi se viene bindata direttamente da HTML)
2988
+ if ((((_a = this.Model) === null || _a === void 0 ? void 0 : _a.length) || 0) >= this.MinChars && !this.SearchFunction)
2989
+ this.FilteredBoundSource = this.BoundSource;
2990
+ else
2991
+ this.FilteredBoundSource = [];
2992
+ this.cdr.markForCheck();
2993
+ }
2860
2994
  }
2861
2995
  /**
2862
2996
  * @ignore
@@ -2883,7 +3017,7 @@ class FormAutocompleteComponent extends BaseFormControl {
2883
3017
  FormAutocompleteComponent.decorators = [
2884
3018
  { type: Component, args: [{
2885
3019
  selector: "form-autocomplete",
2886
- template: "<ng-container *ngIf=\"!FormLayout\">\r\n <ng-container *ngTemplateOutlet=\"controlTemplate\"></ng-container>\r\n</ng-container>\r\n\r\n<div *ngIf=\"FormLayout\" class=\"form-group row {{FormGroupClass}}\" [class.app-margin-bottom-0]=\"Last\">\r\n <label class=\"col-md-{{LabelColWidth}} m-t-5\">{{Label}}{{Required ? '*' : ''}}:</label>\r\n <div class=\"col-md-{{InputColWidth}}\">\r\n <ng-container *ngTemplateOutlet=\"controlTemplate\"></ng-container>\r\n </div>\r\n <div class=\"clearfix\"></div>\r\n</div>\r\n\r\n<ng-template #controlTemplate>\r\n <val-autocomplete [noValidate]=\"!Validation\"\r\n [submitted]=\"Form?.submitted\"\r\n [forceInvalid]=\"ForcedError\"\r\n [readonly]=\"Readonly\"\r\n [label]=\"SelectLabel\"\r\n type=\"text\"\r\n [(ngModel)]=\"Model\"\r\n name=\"{{GeneratedName}}\"\r\n #validationControl=\"ngModel\"\r\n (inputChange)=\"filterSource($event);\"\r\n (optionChange)=\"changed($event);\"\r\n [placeholder]=\"Required ? ((RequiredPlaceholder != null ? RequiredPlaceholder : ('Select' | localize : lc) + '...')) : Placeholder\"\r\n [validationFailed]=\"FailedValidationMessage\"\r\n [FilteredSource]=\"BoundSource\"\r\n >\r\n </val-autocomplete>\r\n</ng-template>",
3020
+ template: "<ng-container *ngIf=\"!FormLayout\">\r\n <ng-container *ngTemplateOutlet=\"controlTemplate\"></ng-container>\r\n</ng-container>\r\n\r\n<div *ngIf=\"FormLayout\" class=\"form-group row {{FormGroupClass}}\" [class.app-margin-bottom-0]=\"Last\">\r\n <label class=\"col-md-{{LabelColWidth}} m-t-5\">{{Label}}{{Required ? '*' : ''}}:</label>\r\n <div class=\"col-md-{{InputColWidth}}\">\r\n <ng-container *ngTemplateOutlet=\"controlTemplate\"></ng-container>\r\n </div>\r\n <div class=\"clearfix\"></div>\r\n</div>\r\n\r\n<ng-template #controlTemplate>\r\n <val-autocomplete [noValidate]=\"!Validation\"\r\n [submitted]=\"Form?.submitted\"\r\n [forceInvalid]=\"ForcedError\"\r\n [readonly]=\"Readonly\"\r\n [label]=\"SelectLabel\"\r\n type=\"text\"\r\n [(ngModel)]=\"Model\"\r\n name=\"{{GeneratedName}}\"\r\n #validationControl=\"ngModel\"\r\n (inputChange)=\"filterSource($event);\"\r\n (optionChange)=\"changed($event);\"\r\n [placeholder]=\"Required ? ((RequiredPlaceholder != null ? RequiredPlaceholder : ('Select' | localize : lc) + '...')) : Placeholder\"\r\n [validationFailed]=\"FailedValidationMessage\"\r\n [FilteredSource]=\"FilteredBoundSource\"\r\n >\r\n </val-autocomplete>\r\n</ng-template>",
2887
3021
  providers: [{ provide: LocalizationService, useClass: FormAutocompleteComponentLoc }],
2888
3022
  changeDetection: ChangeDetectionStrategy.OnPush
2889
3023
  },] }
@@ -2901,7 +3035,8 @@ FormAutocompleteComponent.propDecorators = {
2901
3035
  SelectLabel: [{ type: Input }],
2902
3036
  SearchFunction: [{ type: Input }],
2903
3037
  MinChars: [{ type: Input }],
2904
- RequiredPlaceholder: [{ type: Input }]
3038
+ RequiredPlaceholder: [{ type: Input }],
3039
+ CaseSensitive: [{ type: Input }]
2905
3040
  };
2906
3041
 
2907
3042
  // Angular