@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.
@@ -1530,10 +1530,6 @@
1530
1530
  * @ignore
1531
1531
  */
1532
1532
  _this.onTouched = function () { }; //placeholder on touched function
1533
- //******************** Funzione di throttling per non spammare richieste in caso di animazioni attivate
1534
- //TODO: spostarla in un metodo di utilità (esfaenza/extensions)
1535
- /** @ignore */
1536
- _this.executionTimers = {};
1537
1533
  return _this;
1538
1534
  }
1539
1535
  /**
@@ -1617,15 +1613,6 @@
1617
1613
  ValidationAutocompleteComponent.prototype.registerOnTouched = function (fn) {
1618
1614
  this.onTouched = fn;
1619
1615
  };
1620
- /** @ignore */
1621
- ValidationAutocompleteComponent.prototype.throttla = function (id, func, throttleTime) {
1622
- var _this = this;
1623
- //Se ho la funzione che vuole eseguire ripulisco quel timeout
1624
- if (this.executionTimers[id])
1625
- clearTimeout(this.executionTimers[id]);
1626
- //Ricreo il timeout per eseguire quella funzione dopo throttleTime millisecondi
1627
- this.executionTimers[id] = setTimeout(function () { func(); _this.executionTimers[id] = null; }, throttleTime);
1628
- };
1629
1616
  return ValidationAutocompleteComponent;
1630
1617
  }(BaseValidation));
1631
1618
  ValidationAutocompleteComponent.decorators = [
@@ -1784,6 +1771,14 @@
1784
1771
  * Evento chiamato alla modifica del valore collegato a questo campo
1785
1772
  */
1786
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 = [];
1787
1782
  if (ngControl == null) {
1788
1783
  if (!this.handleNullNgControl())
1789
1784
  console.error("ngControl nullo per qualche motivo! Il 90% delle funzionalità di questo input saranno disabilitate");
@@ -1887,35 +1882,65 @@
1887
1882
  // Cache locale per evitare di rifare dei regex.match ogni santa volta
1888
1883
  if (this.Source.length > 0) {
1889
1884
  this.BoundSource = [];
1890
- // Blocco per tirare fuori le condizioni scritte tipo --> :prop?(Roba con {prop})
1891
- var iffedMatches = this.Display.match(/:([a-z]+)\?\(([^\(\)]+)\)/g);
1892
- var bindCheckingGroups = [];
1893
- if (iffedMatches) {
1894
- iffedMatches.forEach(function (m) {
1895
- // Stessa regex di sopra ma il tag "i" serve per tirare fuori i singoli capturing group e per qualche motivo
1896
- // new RegExp(baseRegex, "g") non funziona quindi non ho potuto razionalizzarlo
1897
- var groups = m.match(/:([a-z]+)\?\(([^\(\)]+)\)/i);
1898
- bindCheckingGroups.push({ global: m, prop: groups[1], whenexists: groups[2] });
1899
- });
1900
- }
1901
- // Blocco per tirare fuori le proprietà scritte tipo --> {prop}
1902
- var bindProperties = [];
1903
- var matches = this.Display.match(/{[a-z]+}/gi);
1904
- if (matches)
1905
- 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();
1906
1889
  // Blocco per generare la descrizione finale di un elemento
1907
1890
  this.Source.forEach(function (s) {
1908
- // Parto sempre dalla variabile di Display, poi sostituisco pezzo per pezzo
1909
- var desc = _this.Display;
1910
- // Taglio o mantengo le condizioni in base alla proprietà su cui fare check
1911
- // Dopodiché scrivo tutte le proprietà
1912
- bindCheckingGroups.forEach(function (t) { desc = desc.replace(t.global, (s[t.prop] != null && s[t.prop] != undefined) ? t.whenexists : ""); });
1913
- bindProperties.forEach(function (t) { desc = desc.replace(t.global, s[t.prop]); });
1914
1891
  // Aggiungo alla BoundSource in formato standard KeyValue
1915
- _this.BoundSource.push({ id: s[_this.IdField], description: desc });
1892
+ _this.BoundSource.push(_this.transformSourceItem(s));
1916
1893
  });
1917
1894
  }
1918
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
+ };
1919
1944
  /**
1920
1945
  * Indica se il comopnente in questione è in grado di gestire ngControl nulli.
1921
1946
  * Di default è **false**
@@ -2324,10 +2349,23 @@
2324
2349
  * Numero minimo di caratteri con cui cercare
2325
2350
  */
2326
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;
2327
2356
  /**
2328
2357
  * Variabile interna che gestisce se effettuare il riallineamento dei dati o meno
2329
2358
  */
2330
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 = [];
2331
2369
  //******************** Funzione di throttling per non spammare richieste in caso di animazioni attivate
2332
2370
  //TODO: spostarla in un metodo di utilità (esfaenza/extensions)
2333
2371
  /** @ignore */
@@ -2339,13 +2377,21 @@
2339
2377
  * @ignore
2340
2378
  */
2341
2379
  FormAdaptiveComponent.prototype.ngOnChanges = function (changes) {
2380
+ var _a;
2342
2381
  return __awaiter(this, void 0, void 0, function () {
2343
2382
  var newSource, newType;
2344
- return __generator(this, function (_a) {
2383
+ return __generator(this, function (_b) {
2345
2384
  newSource = changes["Source"];
2346
2385
  newType = changes["Type"];
2347
- if (newSource)
2386
+ if (newSource) {
2348
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
+ }
2349
2395
  if (newType && this.Model)
2350
2396
  this.writeValue(this.Model);
2351
2397
  return [2 /*return*/];
@@ -2368,20 +2414,22 @@
2368
2414
  this.alignValues = true;
2369
2415
  }
2370
2416
  if (this.Type == "time") {
2371
- 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];
2372
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);
2373
2419
  }
2374
2420
  if (this.Type == "file")
2375
2421
  obj = this.ModelFile;
2376
2422
  if (this.Type == "autocomplete") {
2377
- this.SearchFunction(obj, true).subscribe(function (t) {
2378
- _this.Source = t;
2379
- setTimeout(function () {
2380
- var val = _this.Source.find(function (t) { return t.id == obj; });
2381
- _this.propagateChange(val ? val.id : obj);
2382
- _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); });
2383
2428
  });
2384
- });
2429
+ return;
2430
+ }
2431
+ else
2432
+ this.finalizeValue(obj);
2385
2433
  }
2386
2434
  _super.prototype.writeValue.call(this, obj);
2387
2435
  if (this.alignValues) {
@@ -2389,6 +2437,16 @@
2389
2437
  this.cdr.markForCheck();
2390
2438
  }
2391
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
+ };
2392
2450
  /**
2393
2451
  * Evento di filtro della sorgente dati in base all'input utente
2394
2452
  *
@@ -2396,18 +2454,40 @@
2396
2454
  */
2397
2455
  FormAdaptiveComponent.prototype.filterSource = function (event) {
2398
2456
  var _this = this;
2399
- if (event.length < this.MinChars) {
2400
- 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;
2465
+ return;
2466
+ }
2467
+ if (!event || event.length < this.MinChars) {
2468
+ this.FilteredBoundSource = [];
2401
2469
  return;
2402
2470
  }
2403
- if (!this.SearchFunction)
2471
+ if (!this.SearchFunction && (!this.Source || this.Source.length == 0))
2404
2472
  throw "Impossibile filtrare gli elementi senza una funzione di ricerca che restituisca una lista di { id: string, description: string }";
2405
- this.throttla("filtersource", function () {
2406
- _this.SearchFunction(event, false).subscribe(function (t) {
2407
- _this.Source = t;
2408
- _this.tryBindSourceDisplay();
2409
- });
2410
- }, 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
+ }
2411
2491
  };
2412
2492
  /**
2413
2493
  * Metodo richiamato quando viene modificato il modello del campo di input
@@ -2422,6 +2502,8 @@
2422
2502
  var toEmit = this.dateAdapter.clone(this.Model);
2423
2503
  toEmit = toEmit.format("HH:mm:ss");
2424
2504
  }
2505
+ if (this.Type == "autocomplete")
2506
+ this.ignoreNextWriteValue = true;
2425
2507
  _super.prototype.changed.call(this, toEmit);
2426
2508
  };
2427
2509
  /**
@@ -2476,7 +2558,7 @@
2476
2558
  { type: core.Component, args: [{
2477
2559
  selector: "form-adaptive",
2478
2560
  providers: [{ provide: localizations.LocalizationService, useClass: FormAdaptiveComponentLoc }],
2479
- 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>",
2480
2562
  changeDetection: core.ChangeDetectionStrategy.OnPush,
2481
2563
  styles: [".frm-padding-left-22{padding-left:22px}\n"]
2482
2564
  },] }
@@ -2501,7 +2583,8 @@
2501
2583
  Precision: [{ type: core.Input }],
2502
2584
  Alignment: [{ type: core.Input }],
2503
2585
  SearchFunction: [{ type: core.Input }],
2504
- MinChars: [{ type: core.Input }]
2586
+ MinChars: [{ type: core.Input }],
2587
+ CaseSensitive: [{ type: core.Input }]
2505
2588
  };
2506
2589
 
2507
2590
  // Angular
@@ -2591,7 +2674,7 @@
2591
2674
  FormTemplateComponent.decorators = [
2592
2675
  { type: core.Component, args: [{
2593
2676
  selector: "form-template",
2594
- 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>",
2677
+ 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>",
2595
2678
  changeDetection: core.ChangeDetectionStrategy.OnPush
2596
2679
  },] }
2597
2680
  ];
@@ -3088,7 +3171,8 @@
3088
3171
  text: this.SelectLabel || this.lc.loc("Select one or more values..."),
3089
3172
  enableCheckAll: true,
3090
3173
  disabled: disabled,
3091
- labelKey: "description"
3174
+ labelKey: "description",
3175
+ tagToBody: false
3092
3176
  };
3093
3177
  };
3094
3178
  /**
@@ -3248,11 +3332,19 @@
3248
3332
  * Override del placeholder per select requried
3249
3333
  */
3250
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;
3251
3339
  /**
3252
3340
  * Indica se ignorare il prossimo evento writeValue che normalmente dovrebbe richiedere la nuova source. Serve per quando l'utente seleziona un elemento:
3253
3341
  * Subito dopo partirebbe un altro evento modelChange che ricaricherebbe nuovamente la source
3254
3342
  */
3255
3343
  _this.ignoreNextWriteValue = false;
3344
+ /**
3345
+ * Sorgente Bindata Filtrata in base al contenuto della casella di testo
3346
+ */
3347
+ _this.FilteredBoundSource = [];
3256
3348
  //******************** Funzione di throttling per non spammare richieste in caso di animazioni attivate
3257
3349
  //TODO: spostarla in un metodo di utilità (esfaenza/extensions)
3258
3350
  /** @ignore */
@@ -3264,16 +3356,27 @@
3264
3356
  */
3265
3357
  FormAutocompleteComponent.prototype.writeValue = function (value) {
3266
3358
  var _this = this;
3267
- if (value) {
3359
+ if (!value)
3360
+ return;
3361
+ if (this.SearchFunction) {
3268
3362
  this.SearchFunction(value, true).subscribe(function (t) {
3269
3363
  _this.Source = t;
3270
- setTimeout(function () {
3271
- var val = _this.Source.find(function (t) { return t.id == value; });
3272
- _this.propagateChange(val ? val.id : value);
3273
- _this.Model = val ? val.description : value;
3274
- });
3364
+ _this.tryBindSourceDisplay();
3365
+ setTimeout(function () { _this.finalizeValue(value); });
3275
3366
  });
3367
+ return;
3276
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;
3277
3380
  };
3278
3381
  /**
3279
3382
  * Evento di filtro della sorgente dati in base all'input utente
@@ -3288,18 +3391,50 @@
3288
3391
  }
3289
3392
  // Quando filtro la source, se non devo ignorare l'evento devo comunque assicurarmi di impostare il valore selezionato a null
3290
3393
  _super.prototype.changed.call(this, "");
3394
+ if (!event && this.MinChars == 0 && !this.SearchFunction) {
3395
+ this.FilteredBoundSource = this.BoundSource;
3396
+ return;
3397
+ }
3291
3398
  if (!event || event.length < this.MinChars) {
3292
- this.Source = [];
3399
+ this.FilteredBoundSource = [];
3293
3400
  return;
3294
3401
  }
3295
- if (!this.SearchFunction)
3402
+ if (!this.SearchFunction && (!this.Source || this.Source.length == 0))
3296
3403
  throw "Impossibile filtrare gli elementi senza una funzione di ricerca che restituisca una lista di { id: string, description: string }";
3297
- this.throttla("filtersource", function () {
3298
- _this.SearchFunction(event, false).subscribe(function (t) {
3299
- _this.Source = t;
3300
- _this.tryBindSourceDisplay();
3301
- });
3302
- }, 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
+ }
3303
3438
  };
3304
3439
  /**
3305
3440
  * @ignore
@@ -3330,7 +3465,7 @@
3330
3465
  FormAutocompleteComponent.decorators = [
3331
3466
  { type: core.Component, args: [{
3332
3467
  selector: "form-autocomplete",
3333
- 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>",
3334
3469
  providers: [{ provide: localizations.LocalizationService, useClass: FormAutocompleteComponentLoc }],
3335
3470
  changeDetection: core.ChangeDetectionStrategy.OnPush
3336
3471
  },] }
@@ -3348,7 +3483,8 @@
3348
3483
  SelectLabel: [{ type: core.Input }],
3349
3484
  SearchFunction: [{ type: core.Input }],
3350
3485
  MinChars: [{ type: core.Input }],
3351
- RequiredPlaceholder: [{ type: core.Input }]
3486
+ RequiredPlaceholder: [{ type: core.Input }],
3487
+ CaseSensitive: [{ type: core.Input }]
3352
3488
  };
3353
3489
 
3354
3490
  /**