@esfaenza/forms-and-validations 12.2.25 → 12.2.29

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.
@@ -961,8 +961,10 @@
961
961
  var date = null;
962
962
  if (this.useJsDates) {
963
963
  date = this.dateExts.getDateConvertion(value);
964
- this.propagateChange(date);
965
- this.inputChange.emit(date);
964
+ if (date) {
965
+ this.propagateChange(date.toDate());
966
+ this.inputChange.emit(date.toDate());
967
+ }
966
968
  }
967
969
  else
968
970
  date = dayjs(value, this.lc.token("getSmallDateFormat"));
@@ -1528,10 +1530,6 @@
1528
1530
  * @ignore
1529
1531
  */
1530
1532
  _this.onTouched = function () { }; //placeholder on touched function
1531
- //******************** Funzione di throttling per non spammare richieste in caso di animazioni attivate
1532
- //TODO: spostarla in un metodo di utilità (esfaenza/extensions)
1533
- /** @ignore */
1534
- _this.executionTimers = {};
1535
1533
  return _this;
1536
1534
  }
1537
1535
  /**
@@ -1615,15 +1613,6 @@
1615
1613
  ValidationAutocompleteComponent.prototype.registerOnTouched = function (fn) {
1616
1614
  this.onTouched = fn;
1617
1615
  };
1618
- /** @ignore */
1619
- ValidationAutocompleteComponent.prototype.throttla = function (id, func, throttleTime) {
1620
- var _this = this;
1621
- //Se ho la funzione che vuole eseguire ripulisco quel timeout
1622
- if (this.executionTimers[id])
1623
- clearTimeout(this.executionTimers[id]);
1624
- //Ricreo il timeout per eseguire quella funzione dopo throttleTime millisecondi
1625
- this.executionTimers[id] = setTimeout(function () { func(); _this.executionTimers[id] = null; }, throttleTime);
1626
- };
1627
1616
  return ValidationAutocompleteComponent;
1628
1617
  }(BaseValidation));
1629
1618
  ValidationAutocompleteComponent.decorators = [
@@ -1782,6 +1771,14 @@
1782
1771
  * Evento chiamato alla modifica del valore collegato a questo campo
1783
1772
  */
1784
1773
  this.inputChange = new core.EventEmitter();
1774
+ /**
1775
+ * Cache delle condizioni scritte tipo :prop?(Roba con {prop})
1776
+ */
1777
+ this.BindCheckingGroups = [];
1778
+ /**
1779
+ * Cache delle proprietà scritte tipo --> {prop}
1780
+ */
1781
+ this.BindProperties = [];
1785
1782
  if (ngControl == null) {
1786
1783
  if (!this.handleNullNgControl())
1787
1784
  console.error("ngControl nullo per qualche motivo! Il 90% delle funzionalità di questo input saranno disabilitate");
@@ -1885,35 +1882,65 @@
1885
1882
  // Cache locale per evitare di rifare dei regex.match ogni santa volta
1886
1883
  if (this.Source.length > 0) {
1887
1884
  this.BoundSource = [];
1888
- // Blocco per tirare fuori le condizioni scritte tipo --> :prop?(Roba con {prop})
1889
- var iffedMatches = this.Display.match(/:([a-z]+)\?\(([^\(\)]+)\)/g);
1890
- var bindCheckingGroups = [];
1891
- if (iffedMatches) {
1892
- iffedMatches.forEach(function (m) {
1893
- // Stessa regex di sopra ma il tag "i" serve per tirare fuori i singoli capturing group e per qualche motivo
1894
- // new RegExp(baseRegex, "g") non funziona quindi non ho potuto razionalizzarlo
1895
- var groups = m.match(/:([a-z]+)\?\(([^\(\)]+)\)/i);
1896
- bindCheckingGroups.push({ global: m, prop: groups[1], whenexists: groups[2] });
1897
- });
1898
- }
1899
- // Blocco per tirare fuori le proprietà scritte tipo --> {prop}
1900
- var bindProperties = [];
1901
- var matches = this.Display.match(/{[a-z]+}/gi);
1902
- if (matches)
1903
- matches.forEach(function (m) { bindProperties.push({ global: m, prop: m.substring(1, m.length - 1) }); });
1885
+ if (this.BindCheckingGroups.length == 0)
1886
+ this.evaluateBindCheckingGroups();
1887
+ if (this.BindProperties.length == 0)
1888
+ this.evaluateBindProperties();
1904
1889
  // Blocco per generare la descrizione finale di un elemento
1905
1890
  this.Source.forEach(function (s) {
1906
- // Parto sempre dalla variabile di Display, poi sostituisco pezzo per pezzo
1907
- var desc = _this.Display;
1908
- // Taglio o mantengo le condizioni in base alla proprietà su cui fare check
1909
- // Dopodiché scrivo tutte le proprietà
1910
- bindCheckingGroups.forEach(function (t) { desc = desc.replace(t.global, (s[t.prop] != null && s[t.prop] != undefined) ? t.whenexists : ""); });
1911
- bindProperties.forEach(function (t) { desc = desc.replace(t.global, s[t.prop]); });
1912
1891
  // Aggiungo alla BoundSource in formato standard KeyValue
1913
- _this.BoundSource.push({ id: s[_this.IdField], description: desc });
1892
+ _this.BoundSource.push(_this.transformSourceItem(s));
1914
1893
  });
1915
1894
  }
1916
1895
  };
1896
+ /**
1897
+ * Valuta il contenuto della variabile BindCheckingGroups
1898
+ */
1899
+ BaseFormControl.prototype.evaluateBindCheckingGroups = function () {
1900
+ var _this = this;
1901
+ // Blocco per tirare fuori le condizioni scritte tipo --> :prop?(Roba con {prop})
1902
+ var iffedMatches = this.Display.match(/:([a-z]+)\?\(([^\(\)]+)\)/g);
1903
+ if (iffedMatches) {
1904
+ iffedMatches.forEach(function (m) {
1905
+ // Stessa regex di sopra ma il tag "i" serve per tirare fuori i singoli capturing group e per qualche motivo
1906
+ // new RegExp(baseRegex, "g") non funziona quindi non ho potuto razionalizzarlo
1907
+ var groups = m.match(/:([a-z]+)\?\(([^\(\)]+)\)/i);
1908
+ _this.BindCheckingGroups.push({ global: m, prop: groups[1], whenexists: groups[2] });
1909
+ });
1910
+ }
1911
+ };
1912
+ /**
1913
+ * Valuta il contenuto della variabile BindProperties
1914
+ */
1915
+ BaseFormControl.prototype.evaluateBindProperties = function () {
1916
+ var _this = this;
1917
+ // Blocco per tirare fuori le proprietà scritte tipo --> {prop}
1918
+ var matches = this.Display.match(/{[a-z]+}/gi);
1919
+ if (matches)
1920
+ matches.forEach(function (m) { _this.BindProperties.push({ global: m, prop: m.substring(1, m.length - 1) }); });
1921
+ };
1922
+ /**
1923
+ * Trasforma un oggetto della Source alla sua versione "nuova" basandosi sulle informazioni su gruppi e proprietà
1924
+ *
1925
+ * @param {any} item Oggetto da trasformare
1926
+ *
1927
+ * @returns {{id: string, description: string}} Oggetto finale trasformato
1928
+ */
1929
+ BaseFormControl.prototype.transformSourceItem = function (s) {
1930
+ // Parto sempre dalla variabile di Display, poi sostituisco pezzo per pezzo
1931
+ var desc = this.Display;
1932
+ // Taglio o mantengo le condizioni in base alla proprietà su cui fare check
1933
+ // Dopodiché scrivo tutte le proprietà
1934
+ for (var i = 0; i < this.BindCheckingGroups.length; i++) {
1935
+ var t = this.BindCheckingGroups[i];
1936
+ desc = desc.replace(t.global, (s[t.prop] != null && s[t.prop] != undefined) ? t.whenexists : "");
1937
+ }
1938
+ for (var i = 0; i < this.BindProperties.length; i++) {
1939
+ var t = this.BindProperties[i];
1940
+ desc = desc.replace(t.global, s[t.prop]);
1941
+ }
1942
+ return { id: s[this.IdField], description: desc };
1943
+ };
1917
1944
  /**
1918
1945
  * Indica se il comopnente in questione è in grado di gestire ngControl nulli.
1919
1946
  * Di default è **false**
@@ -2322,10 +2349,23 @@
2322
2349
  * Numero minimo di caratteri con cui cercare
2323
2350
  */
2324
2351
  _this.MinChars = 3;
2352
+ /**
2353
+ * Indica se i controlli devono essere effettuati tenendo conto del Case o meno. Vale solo qualora la **Source** fosse fornita
2354
+ */
2355
+ _this.CaseSensitive = false;
2325
2356
  /**
2326
2357
  * Variabile interna che gestisce se effettuare il riallineamento dei dati o meno
2327
2358
  */
2328
2359
  _this.alignValues = false;
2360
+ /**
2361
+ * Indica se ignorare il prossimo evento writeValue che normalmente dovrebbe richiedere la nuova source. Serve per quando l'utente seleziona un elemento:
2362
+ * Subito dopo partirebbe un altro evento modelChange che ricaricherebbe nuovamente la source
2363
+ */
2364
+ _this.ignoreNextWriteValue = false;
2365
+ /**
2366
+ * 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
2367
+ */
2368
+ _this.FilteredBoundSource = [];
2329
2369
  //******************** Funzione di throttling per non spammare richieste in caso di animazioni attivate
2330
2370
  //TODO: spostarla in un metodo di utilità (esfaenza/extensions)
2331
2371
  /** @ignore */
@@ -2337,13 +2377,21 @@
2337
2377
  * @ignore
2338
2378
  */
2339
2379
  FormAdaptiveComponent.prototype.ngOnChanges = function (changes) {
2380
+ var _a;
2340
2381
  return __awaiter(this, void 0, void 0, function () {
2341
2382
  var newSource, newType;
2342
- return __generator(this, function (_a) {
2383
+ return __generator(this, function (_b) {
2343
2384
  newSource = changes["Source"];
2344
2385
  newType = changes["Type"];
2345
- if (newSource)
2386
+ if (newSource) {
2346
2387
  this.tryBindSourceDisplay();
2388
+ // Prima assegnazione se cambia la source sotto (quindi se viene bindata direttamente da HTML)
2389
+ if (this.Type == "autocomplete" && (((_a = this.Model) === null || _a === void 0 ? void 0 : _a.length) || 0) >= this.MinChars && !this.SearchFunction)
2390
+ this.FilteredBoundSource = this.BoundSource;
2391
+ else
2392
+ this.FilteredBoundSource = [];
2393
+ this.cdr.markForCheck();
2394
+ }
2347
2395
  if (newType && this.Model)
2348
2396
  this.writeValue(this.Model);
2349
2397
  return [2 /*return*/];
@@ -2366,20 +2414,22 @@
2366
2414
  this.alignValues = true;
2367
2415
  }
2368
2416
  if (this.Type == "time") {
2369
- var _a = __read((obj || "0:0:0").split(":"), 3), hours = _a[0], minutes = _a[1], seconds = _a[2];
2417
+ var _b = __read((obj || "0:0:0").split(":"), 3), hours = _b[0], minutes = _b[1], seconds = _b[2];
2370
2418
  obj = dayjs().hour(hours !== null && hours !== void 0 ? hours : 0).minute(minutes !== null && minutes !== void 0 ? minutes : 0).second(seconds !== null && seconds !== void 0 ? seconds : 0);
2371
2419
  }
2372
2420
  if (this.Type == "file")
2373
2421
  obj = this.ModelFile;
2374
2422
  if (this.Type == "autocomplete") {
2375
- this.SearchFunction(obj, true).subscribe(function (t) {
2376
- _this.Source = t;
2377
- setTimeout(function () {
2378
- var val = _this.Source.find(function (t) { return t.id == obj; });
2379
- _this.propagateChange(val ? val.id : obj);
2380
- _this.Model = val ? val.description : obj;
2423
+ if (this.SearchFunction) {
2424
+ this.SearchFunction(obj, true).subscribe(function (t) {
2425
+ _this.Source = t;
2426
+ _this.tryBindSourceDisplay();
2427
+ setTimeout(function () { _this.finalizeValue(obj); });
2381
2428
  });
2382
- });
2429
+ return;
2430
+ }
2431
+ else
2432
+ this.finalizeValue(obj);
2383
2433
  }
2384
2434
  _super.prototype.writeValue.call(this, obj);
2385
2435
  if (this.alignValues) {
@@ -2387,6 +2437,16 @@
2387
2437
  this.cdr.markForCheck();
2388
2438
  }
2389
2439
  };
2440
+ /**
2441
+ * 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
2442
+ *
2443
+ * @param {any} value Valore scritto nell'input di testo
2444
+ */
2445
+ FormAdaptiveComponent.prototype.finalizeValue = function (value) {
2446
+ var val = this.Source.find(function (t) { return t.id == value; });
2447
+ this.propagateChange(val ? val.id : value);
2448
+ this.Model = val ? val.description : value;
2449
+ };
2390
2450
  /**
2391
2451
  * Evento di filtro della sorgente dati in base all'input utente
2392
2452
  *
@@ -2394,18 +2454,40 @@
2394
2454
  */
2395
2455
  FormAdaptiveComponent.prototype.filterSource = function (event) {
2396
2456
  var _this = this;
2397
- if (event.length < this.MinChars) {
2398
- this.Source = [];
2457
+ if (this.ignoreNextWriteValue) {
2458
+ this.ignoreNextWriteValue = false;
2459
+ return;
2460
+ }
2461
+ // Quando filtro la source, se non devo ignorare l'evento devo comunque assicurarmi di impostare il valore selezionato a null
2462
+ _super.prototype.changed.call(this, "");
2463
+ if (!event && this.MinChars == 0 && !this.SearchFunction) {
2464
+ this.FilteredBoundSource = this.BoundSource;
2399
2465
  return;
2400
2466
  }
2401
- if (!this.SearchFunction)
2467
+ if (!event || event.length < this.MinChars) {
2468
+ this.FilteredBoundSource = [];
2469
+ return;
2470
+ }
2471
+ if (!this.SearchFunction && (!this.Source || this.Source.length == 0))
2402
2472
  throw "Impossibile filtrare gli elementi senza una funzione di ricerca che restituisca una lista di { id: string, description: string }";
2403
- this.throttla("filtersource", function () {
2404
- _this.SearchFunction(event, false).subscribe(function (t) {
2405
- _this.Source = t;
2406
- _this.tryBindSourceDisplay();
2407
- });
2408
- }, 400);
2473
+ if (this.SearchFunction) {
2474
+ this.throttla("filtersource", function () {
2475
+ _this.SearchFunction(event, false).subscribe(function (t) {
2476
+ _this.Source = t;
2477
+ _this.tryBindSourceDisplay();
2478
+ // In questo caso è già filtrata dalla SearchFunction
2479
+ _this.FilteredBoundSource = _this.BoundSource;
2480
+ _this.cdr.markForCheck();
2481
+ });
2482
+ }, 400);
2483
+ }
2484
+ else {
2485
+ this.throttla("filtersource", function () {
2486
+ // In questo caso devo filtrare io in memoria
2487
+ _this.FilteredBoundSource = _this.BoundSource.filter(function (t) { return (_this.CaseSensitive && t.description.includes(event)) || (!_this.CaseSensitive && t.description.toLowerCase().includes(event.toLowerCase())); });
2488
+ _this.cdr.markForCheck();
2489
+ }, 100);
2490
+ }
2409
2491
  };
2410
2492
  /**
2411
2493
  * Metodo richiamato quando viene modificato il modello del campo di input
@@ -2420,6 +2502,8 @@
2420
2502
  var toEmit = this.dateAdapter.clone(this.Model);
2421
2503
  toEmit = toEmit.format("HH:mm:ss");
2422
2504
  }
2505
+ if (this.Type == "autocomplete")
2506
+ this.ignoreNextWriteValue = true;
2423
2507
  _super.prototype.changed.call(this, toEmit);
2424
2508
  };
2425
2509
  /**
@@ -2474,7 +2558,7 @@
2474
2558
  { type: core.Component, args: [{
2475
2559
  selector: "form-adaptive",
2476
2560
  providers: [{ provide: localizations.LocalizationService, useClass: FormAdaptiveComponentLoc }],
2477
- 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>",
2561
+ 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>",
2478
2562
  changeDetection: core.ChangeDetectionStrategy.OnPush,
2479
2563
  styles: [".frm-padding-left-22{padding-left:22px}\n"]
2480
2564
  },] }
@@ -2499,7 +2583,8 @@
2499
2583
  Precision: [{ type: core.Input }],
2500
2584
  Alignment: [{ type: core.Input }],
2501
2585
  SearchFunction: [{ type: core.Input }],
2502
- MinChars: [{ type: core.Input }]
2586
+ MinChars: [{ type: core.Input }],
2587
+ CaseSensitive: [{ type: core.Input }]
2503
2588
  };
2504
2589
 
2505
2590
  // Angular
@@ -3086,7 +3171,8 @@
3086
3171
  text: this.SelectLabel || this.lc.loc("Select one or more values..."),
3087
3172
  enableCheckAll: true,
3088
3173
  disabled: disabled,
3089
- labelKey: "description"
3174
+ labelKey: "description",
3175
+ tagToBody: false
3090
3176
  };
3091
3177
  };
3092
3178
  /**
@@ -3246,11 +3332,19 @@
3246
3332
  * Override del placeholder per select requried
3247
3333
  */
3248
3334
  _this.RequiredPlaceholder = null;
3335
+ /**
3336
+ * Indica se i controlli devono essere effettuati tenendo conto del Case o meno. Vale solo qualora la **Source** fosse fornita
3337
+ */
3338
+ _this.CaseSensitive = false;
3249
3339
  /**
3250
3340
  * Indica se ignorare il prossimo evento writeValue che normalmente dovrebbe richiedere la nuova source. Serve per quando l'utente seleziona un elemento:
3251
3341
  * Subito dopo partirebbe un altro evento modelChange che ricaricherebbe nuovamente la source
3252
3342
  */
3253
3343
  _this.ignoreNextWriteValue = false;
3344
+ /**
3345
+ * Sorgente Bindata Filtrata in base al contenuto della casella di testo
3346
+ */
3347
+ _this.FilteredBoundSource = [];
3254
3348
  //******************** Funzione di throttling per non spammare richieste in caso di animazioni attivate
3255
3349
  //TODO: spostarla in un metodo di utilità (esfaenza/extensions)
3256
3350
  /** @ignore */
@@ -3262,16 +3356,27 @@
3262
3356
  */
3263
3357
  FormAutocompleteComponent.prototype.writeValue = function (value) {
3264
3358
  var _this = this;
3265
- if (value) {
3359
+ if (!value)
3360
+ return;
3361
+ if (this.SearchFunction) {
3266
3362
  this.SearchFunction(value, true).subscribe(function (t) {
3267
3363
  _this.Source = t;
3268
- setTimeout(function () {
3269
- var val = _this.Source.find(function (t) { return t.id == value; });
3270
- _this.propagateChange(val ? val.id : value);
3271
- _this.Model = val ? val.description : value;
3272
- });
3364
+ _this.tryBindSourceDisplay();
3365
+ setTimeout(function () { _this.finalizeValue(value); });
3273
3366
  });
3367
+ return;
3274
3368
  }
3369
+ this.finalizeValue(value);
3370
+ };
3371
+ /**
3372
+ * 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
3373
+ *
3374
+ * @param {any} value Valore scritto nell'input di testo
3375
+ */
3376
+ FormAutocompleteComponent.prototype.finalizeValue = function (value) {
3377
+ var val = this.Source.find(function (t) { return t.id == value; });
3378
+ this.propagateChange(val ? val.id : value);
3379
+ this.Model = val ? val.description : value;
3275
3380
  };
3276
3381
  /**
3277
3382
  * Evento di filtro della sorgente dati in base all'input utente
@@ -3286,18 +3391,50 @@
3286
3391
  }
3287
3392
  // Quando filtro la source, se non devo ignorare l'evento devo comunque assicurarmi di impostare il valore selezionato a null
3288
3393
  _super.prototype.changed.call(this, "");
3394
+ if (!event && this.MinChars == 0 && !this.SearchFunction) {
3395
+ this.FilteredBoundSource = this.BoundSource;
3396
+ return;
3397
+ }
3289
3398
  if (!event || event.length < this.MinChars) {
3290
- this.Source = [];
3399
+ this.FilteredBoundSource = [];
3291
3400
  return;
3292
3401
  }
3293
- if (!this.SearchFunction)
3402
+ if (!this.SearchFunction && (!this.Source || this.Source.length == 0))
3294
3403
  throw "Impossibile filtrare gli elementi senza una funzione di ricerca che restituisca una lista di { id: string, description: string }";
3295
- this.throttla("filtersource", function () {
3296
- _this.SearchFunction(event, false).subscribe(function (t) {
3297
- _this.Source = t;
3298
- _this.tryBindSourceDisplay();
3299
- });
3300
- }, 400);
3404
+ if (this.SearchFunction) {
3405
+ this.throttla("filtersource", function () {
3406
+ _this.SearchFunction(event, false).subscribe(function (t) {
3407
+ _this.Source = t;
3408
+ _this.tryBindSourceDisplay();
3409
+ // In questo caso è già filtrata dalla SearchFunction
3410
+ _this.FilteredBoundSource = _this.BoundSource;
3411
+ _this.cdr.markForCheck();
3412
+ });
3413
+ }, 400);
3414
+ }
3415
+ else {
3416
+ this.throttla("filtersource", function () {
3417
+ // In questo caso devo filtrare io in memoria
3418
+ _this.FilteredBoundSource = _this.BoundSource.filter(function (t) { return (_this.CaseSensitive && t.description.includes(event)) || (!_this.CaseSensitive && t.description.toLowerCase().includes(event.toLowerCase())); });
3419
+ _this.cdr.markForCheck();
3420
+ }, 100);
3421
+ }
3422
+ };
3423
+ /**
3424
+ * @ignore
3425
+ */
3426
+ FormAutocompleteComponent.prototype.ngOnChanges = function (changes) {
3427
+ var _a;
3428
+ var newSource = changes["Source"];
3429
+ if (newSource) {
3430
+ this.tryBindSourceDisplay();
3431
+ // Prima assegnazione se cambia la source sotto (quindi se viene bindata direttamente da HTML)
3432
+ if ((((_a = this.Model) === null || _a === void 0 ? void 0 : _a.length) || 0) >= this.MinChars && !this.SearchFunction)
3433
+ this.FilteredBoundSource = this.BoundSource;
3434
+ else
3435
+ this.FilteredBoundSource = [];
3436
+ this.cdr.markForCheck();
3437
+ }
3301
3438
  };
3302
3439
  /**
3303
3440
  * @ignore
@@ -3328,7 +3465,7 @@
3328
3465
  FormAutocompleteComponent.decorators = [
3329
3466
  { type: core.Component, args: [{
3330
3467
  selector: "form-autocomplete",
3331
- 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>",
3468
+ 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>",
3332
3469
  providers: [{ provide: localizations.LocalizationService, useClass: FormAutocompleteComponentLoc }],
3333
3470
  changeDetection: core.ChangeDetectionStrategy.OnPush
3334
3471
  },] }
@@ -3346,7 +3483,8 @@
3346
3483
  SelectLabel: [{ type: core.Input }],
3347
3484
  SearchFunction: [{ type: core.Input }],
3348
3485
  MinChars: [{ type: core.Input }],
3349
- RequiredPlaceholder: [{ type: core.Input }]
3486
+ RequiredPlaceholder: [{ type: core.Input }],
3487
+ CaseSensitive: [{ type: core.Input }]
3350
3488
  };
3351
3489
 
3352
3490
  /**