@energycap/components 0.39.20 → 0.39.21-ECAP-25650-file-upload-validation-support.20240805-1645

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.
@@ -7,7 +7,7 @@ import { DOCUMENT, CommonModule } from '@angular/common';
7
7
  import * as i0 from '@angular/core';
8
8
  import { Injectable, EventEmitter, Component, HostBinding, Input, Output, ViewChild, Directive, Host, Pipe, HostListener, Inject, ElementRef, ViewEncapsulation, ContentChild, TemplateRef, ViewContainerRef, ContentChildren, NgModule, Injector } from '@angular/core';
9
9
  import * as i4 from '@angular/forms';
10
- import { Validators, UntypedFormControl, FormControlDirective, UntypedFormGroup, UntypedFormArray, FormsModule, ReactiveFormsModule } from '@angular/forms';
10
+ import { Validators, UntypedFormControl, FormControlDirective, UntypedFormGroup, UntypedFormArray, FormGroup, FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms';
11
11
  import * as i1$2 from '@angular/router';
12
12
  import { NavigationEnd, convertToParamMap, NavigationStart, Router, RouterModule, ActivatedRoute } from '@angular/router';
13
13
  import * as i2 from '@ngx-translate/core';
@@ -734,6 +734,9 @@ class ValidationMessageService {
734
734
  case 'domain':
735
735
  translationObservables.push(this.translate.get('DomainValidationMessage_SC'));
736
736
  break;
737
+ case 'invalidFile':
738
+ translationObservables.push(this.translate.get('is invalid'));
739
+ break;
737
740
  default:
738
741
  break;
739
742
  }
@@ -4103,18 +4106,28 @@ const FileTypeExtensions = {
4103
4106
  };
4104
4107
  class FileUploadComponent extends FormControlBase {
4105
4108
  // static class to create the form group from a parent component
4106
- static getFormModel(validators, disabled = false) {
4107
- let formGroup = new UntypedFormGroup({
4108
- file: new UntypedFormControl({ value: null, disabled: disabled }, validators),
4109
- name: new UntypedFormControl({ value: null, disabled: disabled }, validators),
4110
- base64FileString: new UntypedFormControl(null),
4111
- uploadResult: new UntypedFormControl(null),
4109
+ static getFormModel(validators, disabled = false, fileValidators) {
4110
+ let formGroup = new FormGroup({
4111
+ file: new FormControl({ value: null, disabled: disabled }, validators),
4112
+ name: new FormControl({ value: null, disabled: disabled }, { validators: validators, asyncValidators: fileValidators }),
4113
+ base64FileString: new FormControl(null),
4114
+ uploadResult: new FormControl(null),
4112
4115
  });
4113
4116
  if (disabled) {
4114
4117
  formGroup.disable();
4115
4118
  }
4116
4119
  return formGroup;
4117
4120
  }
4121
+ static getFileValidator(callback) {
4122
+ return async (nameControl) => {
4123
+ if (nameControl.value && nameControl.parent && nameControl.parent.get('file')?.value) {
4124
+ let file = nameControl.parent.get('file')?.value;
4125
+ let base64 = nameControl.parent.get('base64FileString')?.value;
4126
+ return await callback(file, base64);
4127
+ }
4128
+ return null;
4129
+ };
4130
+ }
4118
4131
  constructor(validationMessageService, formGroupHelper) {
4119
4132
  super(validationMessageService, formGroupHelper);
4120
4133
  this.validationMessageService = validationMessageService;
@@ -4155,7 +4168,6 @@ class FileUploadComponent extends FormControlBase {
4155
4168
  if (!value) {
4156
4169
  this.formModel.patchValue({
4157
4170
  file: null,
4158
- name: null,
4159
4171
  base64FileString: null,
4160
4172
  uploadResult: null
4161
4173
  });
@@ -4202,19 +4214,25 @@ class FileUploadComponent extends FormControlBase {
4202
4214
  * @param base64FileString Optional: Will have a value provided if the fileOutput is set to base64
4203
4215
  */
4204
4216
  async processFile(file, base64FileString) {
4205
- if (this.onFileSelected) {
4217
+ // Patch the file first to trigger any file validators
4218
+ this.patchProcessedFile(file, base64FileString);
4219
+ // If we have any async validators pending we need to wait for them to complete before we know if the form is valid
4220
+ if (this.formModel.pending) {
4221
+ await this.formModel.statusChanges.pipe(filter(status => status !== 'PENDING'), take(1), takeUntil(this.componentDestroyed)).toPromise();
4222
+ }
4223
+ // Mark the name control as touched so that any validation errors will show
4224
+ this.formModel.controls.name.markAsTouched();
4225
+ // Only call the onFileSelected callback to upload the file if the form is valid
4226
+ if (this.onFileSelected && this.formModel.valid) {
4206
4227
  try {
4207
4228
  let result = await this.onFileSelected(file);
4208
- this.patchFileResult(file, base64FileString, result);
4229
+ this.formModel.patchValue({ uploadResult: result ?? null });
4209
4230
  }
4210
4231
  catch (e) {
4211
4232
  // Bummer, we're not going to do anything about it though.
4212
4233
  // We are not patching any of the result so any existing information remains
4213
4234
  }
4214
4235
  }
4215
- else {
4216
- this.patchFileResult(file, base64FileString);
4217
- }
4218
4236
  }
4219
4237
  /**
4220
4238
  * Based on the fileOutput return whether this component is expected to deliver a base64 output
@@ -4230,18 +4248,13 @@ class FileUploadComponent extends FormControlBase {
4230
4248
  * @param base64FileString
4231
4249
  * @param onFileSelectedResult
4232
4250
  */
4233
- patchFileResult(file, base64FileString, onFileSelectedResult) {
4251
+ patchProcessedFile(file, base64FileString) {
4234
4252
  this.formModel?.patchValue({
4235
4253
  file: file,
4236
4254
  name: file?.name,
4237
- base64FileString: base64FileString ?? null
4255
+ base64FileString: base64FileString ?? null,
4256
+ uploadResult: null
4238
4257
  });
4239
- if (onFileSelectedResult) {
4240
- this.formModel.patchValue({ uploadResult: onFileSelectedResult });
4241
- }
4242
- else {
4243
- this.formModel.patchValue({ uploadResult: null });
4244
- }
4245
4258
  }
4246
4259
  /** Maps the files to an array of File objects and sends them along
4247
4260
  * to the derived onMultipleFileSelected method in the hosting component
@@ -4273,11 +4286,13 @@ class FileUploadComponent extends FormControlBase {
4273
4286
  }
4274
4287
  }
4275
4288
  FileUploadComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: FileUploadComponent, deps: [{ token: ValidationMessageService }, { token: FormGroupHelper }], target: i0.ɵɵFactoryTarget.Component });
4276
- FileUploadComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.9", type: FileUploadComponent, selector: "ec-file-upload", inputs: { placeholder: "placeholder", fileType: "fileType", fileOutput: "fileOutput", customExtensions: "customExtensions", onFileSelected: "onFileSelected", onMultipleFilesSelected: "onMultipleFilesSelected", displayType: "displayType", buttonLabel: "buttonLabel", buttonType: "buttonType", multiSelect: "multiSelect" }, viewQueries: [{ propertyName: "fileInput", first: true, predicate: ["fileInput"], descendants: true, read: ElementRef, static: true }], usesInheritance: true, usesOnChanges: true, ngImport: i0, template: "<ec-form-group [label]=\"label\"\r\n [formGroup]=\"formModel\"\r\n [helpPopover]=\"helpPopover\"\r\n [helpPopoverPosition]=\"helpPopoverPosition\"\r\n class=\"mb-0\">\r\n <div class=\"d-flex control-group\">\r\n <div class=\"d-flex flex-grow position-relative\">\r\n <input #fileInput\r\n id=\"{{inputId}}_input\"\r\n type=\"file\"\r\n tabindex=\"-1\"\r\n [attr.accept]=\"fileTypeAccept\"\r\n (change)=\"fileChange($event.target.files)\"\r\n [class.has-value]=\"displayType === 'file' ? formModel?.get('name').value : undefined\"\r\n [attr.multiple]=\"multiSelect ? 'multiple' : undefined\">\r\n <ec-form-control *ngIf=\"displayType === 'file'\"\r\n id=\"{{inputId}}_formControl\"\r\n class=\"text-truncate\"\r\n [required]=\"required\"\r\n [pending]=\"pending\">\r\n <input id=\"{{inputId}}_name\"\r\n [formControl]=\"formModel?.get('name')\"\r\n type=\"text\"\r\n [placeholder]=\"placeholder\"\r\n [tabindex]=\"-1\">\r\n </ec-form-control>\r\n </div>\r\n <ec-button *ngIf=\"displayType === 'file'\"\r\n #browseBtn\r\n id=\"{{inputId}}_browseBtn\"\r\n (clicked)=\"fileInput.click()\"\r\n type=\"secondary\"\r\n [tabindex]=\"tabindex\"\r\n [disabled]=\"formModel?.get('name').disabled\"\r\n label=\"Browse\"\r\n [autofocus]=\"autofocus\">\r\n </ec-button>\r\n </div>\r\n <ec-button *ngIf=\"displayType === 'button'\"\r\n id=\"{{inputId}}_btn\"\r\n [pending]=\"pending\"\r\n [type]=\"buttonType\"\r\n [label]=\"buttonLabel ?? 'Browse_TC' | translate\"\r\n (clicked)=\"fileInput.click()\"\r\n style=\"width: 100%;\">\r\n </ec-button>\r\n</ec-form-group>", styles: [":host{display:block;margin-bottom:1rem}ec-form-control{margin-bottom:0}ec-form-control ::ng-deep>.ec-focus-ring{display:none!important}input[type=file]{opacity:0;display:block;position:absolute;top:0;left:0;width:100%;height:100%;z-index:1;cursor:pointer}input[type=file].has-value{width:calc(100% - 1.5rem)}ec-button{--ec-button-border-color-secondary: var(--ec-form-control-border-color);--ec-button-color-icon-secondary: var(--ec-color-icon)}\n"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i4.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i4.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i4.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i4.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: ButtonComponent, selector: "ec-button", inputs: ["id", "disabled", "icon", "label", "badge", "tabindex", "type", "pending", "pendingIcon", "customTemplate", "isSubmit", "autofocus"], outputs: ["clicked"] }, { kind: "component", type: FormControlComponent, selector: "ec-form-control", inputs: ["id", "icon", "actionIcon", "showClear", "pending", "required", "readonly"], outputs: ["actionClicked"] }, { kind: "component", type: FormGroupComponent, selector: "ec-form-group", inputs: ["id", "label", "formGroup", "labelPosition", "overrideValidationError", "hideValidationMessage", "helpPopover", "helpPopoverPosition"] }, { kind: "pipe", type: i2.TranslatePipe, name: "translate" }] });
4289
+ FileUploadComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.9", type: FileUploadComponent, selector: "ec-file-upload", inputs: { formModel: "formModel", placeholder: "placeholder", fileType: "fileType", fileOutput: "fileOutput", customExtensions: "customExtensions", onFileSelected: "onFileSelected", onMultipleFilesSelected: "onMultipleFilesSelected", displayType: "displayType", buttonLabel: "buttonLabel", buttonType: "buttonType", multiSelect: "multiSelect" }, viewQueries: [{ propertyName: "fileInput", first: true, predicate: ["fileInput"], descendants: true, read: ElementRef, static: true }], usesInheritance: true, usesOnChanges: true, ngImport: i0, template: "<ec-form-group [label]=\"label\"\r\n [formGroup]=\"formModel\"\r\n [helpPopover]=\"helpPopover\"\r\n [helpPopoverPosition]=\"helpPopoverPosition\"\r\n class=\"mb-0\">\r\n <div class=\"d-flex control-group\">\r\n <div class=\"d-flex flex-grow position-relative\">\r\n <input #fileInput\r\n id=\"{{inputId}}_input\"\r\n type=\"file\"\r\n tabindex=\"-1\"\r\n [attr.accept]=\"fileTypeAccept\"\r\n (change)=\"fileChange($event.target.files)\"\r\n [class.has-value]=\"displayType === 'file' ? formModel?.get('name').value : undefined\"\r\n [attr.multiple]=\"multiSelect ? 'multiple' : undefined\">\r\n <ec-form-control *ngIf=\"displayType === 'file'\"\r\n id=\"{{inputId}}_formControl\"\r\n class=\"text-truncate\"\r\n [required]=\"required\"\r\n [pending]=\"pending || formModel?.pending\">\r\n <input id=\"{{inputId}}_name\"\r\n [formControl]=\"formModel?.get('name')\"\r\n type=\"text\"\r\n [placeholder]=\"placeholder\"\r\n [tabindex]=\"-1\">\r\n </ec-form-control>\r\n </div>\r\n <ec-button *ngIf=\"displayType === 'file'\"\r\n #browseBtn\r\n id=\"{{inputId}}_browseBtn\"\r\n (clicked)=\"fileInput.click()\"\r\n type=\"secondary\"\r\n [tabindex]=\"tabindex\"\r\n [disabled]=\"formModel?.get('name').disabled\"\r\n label=\"Browse\"\r\n [autofocus]=\"autofocus\">\r\n </ec-button>\r\n </div>\r\n <ec-button *ngIf=\"displayType === 'button'\"\r\n id=\"{{inputId}}_btn\"\r\n [pending]=\"pending\"\r\n [type]=\"buttonType\"\r\n [label]=\"buttonLabel ?? 'Browse_TC' | translate\"\r\n (clicked)=\"fileInput.click()\"\r\n style=\"width: 100%;\">\r\n </ec-button>\r\n</ec-form-group>", styles: [":host{display:block;margin-bottom:1rem}ec-form-control{margin-bottom:0}ec-form-control ::ng-deep>.ec-focus-ring{display:none!important}input[type=file]{opacity:0;display:block;position:absolute;top:0;left:0;width:100%;height:100%;z-index:1;cursor:pointer}input[type=file].has-value{width:calc(100% - 1.5rem)}ec-button{--ec-button-border-color-secondary: var(--ec-form-control-border-color);--ec-button-color-icon-secondary: var(--ec-color-icon)}\n"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i4.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i4.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i4.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i4.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: ButtonComponent, selector: "ec-button", inputs: ["id", "disabled", "icon", "label", "badge", "tabindex", "type", "pending", "pendingIcon", "customTemplate", "isSubmit", "autofocus"], outputs: ["clicked"] }, { kind: "component", type: FormControlComponent, selector: "ec-form-control", inputs: ["id", "icon", "actionIcon", "showClear", "pending", "required", "readonly"], outputs: ["actionClicked"] }, { kind: "component", type: FormGroupComponent, selector: "ec-form-group", inputs: ["id", "label", "formGroup", "labelPosition", "overrideValidationError", "hideValidationMessage", "helpPopover", "helpPopoverPosition"] }, { kind: "pipe", type: i2.TranslatePipe, name: "translate" }] });
4277
4290
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: FileUploadComponent, decorators: [{
4278
4291
  type: Component,
4279
- args: [{ selector: "ec-file-upload", template: "<ec-form-group [label]=\"label\"\r\n [formGroup]=\"formModel\"\r\n [helpPopover]=\"helpPopover\"\r\n [helpPopoverPosition]=\"helpPopoverPosition\"\r\n class=\"mb-0\">\r\n <div class=\"d-flex control-group\">\r\n <div class=\"d-flex flex-grow position-relative\">\r\n <input #fileInput\r\n id=\"{{inputId}}_input\"\r\n type=\"file\"\r\n tabindex=\"-1\"\r\n [attr.accept]=\"fileTypeAccept\"\r\n (change)=\"fileChange($event.target.files)\"\r\n [class.has-value]=\"displayType === 'file' ? formModel?.get('name').value : undefined\"\r\n [attr.multiple]=\"multiSelect ? 'multiple' : undefined\">\r\n <ec-form-control *ngIf=\"displayType === 'file'\"\r\n id=\"{{inputId}}_formControl\"\r\n class=\"text-truncate\"\r\n [required]=\"required\"\r\n [pending]=\"pending\">\r\n <input id=\"{{inputId}}_name\"\r\n [formControl]=\"formModel?.get('name')\"\r\n type=\"text\"\r\n [placeholder]=\"placeholder\"\r\n [tabindex]=\"-1\">\r\n </ec-form-control>\r\n </div>\r\n <ec-button *ngIf=\"displayType === 'file'\"\r\n #browseBtn\r\n id=\"{{inputId}}_browseBtn\"\r\n (clicked)=\"fileInput.click()\"\r\n type=\"secondary\"\r\n [tabindex]=\"tabindex\"\r\n [disabled]=\"formModel?.get('name').disabled\"\r\n label=\"Browse\"\r\n [autofocus]=\"autofocus\">\r\n </ec-button>\r\n </div>\r\n <ec-button *ngIf=\"displayType === 'button'\"\r\n id=\"{{inputId}}_btn\"\r\n [pending]=\"pending\"\r\n [type]=\"buttonType\"\r\n [label]=\"buttonLabel ?? 'Browse_TC' | translate\"\r\n (clicked)=\"fileInput.click()\"\r\n style=\"width: 100%;\">\r\n </ec-button>\r\n</ec-form-group>", styles: [":host{display:block;margin-bottom:1rem}ec-form-control{margin-bottom:0}ec-form-control ::ng-deep>.ec-focus-ring{display:none!important}input[type=file]{opacity:0;display:block;position:absolute;top:0;left:0;width:100%;height:100%;z-index:1;cursor:pointer}input[type=file].has-value{width:calc(100% - 1.5rem)}ec-button{--ec-button-border-color-secondary: var(--ec-form-control-border-color);--ec-button-color-icon-secondary: var(--ec-color-icon)}\n"] }]
4280
- }], ctorParameters: function () { return [{ type: ValidationMessageService }, { type: FormGroupHelper }]; }, propDecorators: { placeholder: [{
4292
+ args: [{ selector: "ec-file-upload", template: "<ec-form-group [label]=\"label\"\r\n [formGroup]=\"formModel\"\r\n [helpPopover]=\"helpPopover\"\r\n [helpPopoverPosition]=\"helpPopoverPosition\"\r\n class=\"mb-0\">\r\n <div class=\"d-flex control-group\">\r\n <div class=\"d-flex flex-grow position-relative\">\r\n <input #fileInput\r\n id=\"{{inputId}}_input\"\r\n type=\"file\"\r\n tabindex=\"-1\"\r\n [attr.accept]=\"fileTypeAccept\"\r\n (change)=\"fileChange($event.target.files)\"\r\n [class.has-value]=\"displayType === 'file' ? formModel?.get('name').value : undefined\"\r\n [attr.multiple]=\"multiSelect ? 'multiple' : undefined\">\r\n <ec-form-control *ngIf=\"displayType === 'file'\"\r\n id=\"{{inputId}}_formControl\"\r\n class=\"text-truncate\"\r\n [required]=\"required\"\r\n [pending]=\"pending || formModel?.pending\">\r\n <input id=\"{{inputId}}_name\"\r\n [formControl]=\"formModel?.get('name')\"\r\n type=\"text\"\r\n [placeholder]=\"placeholder\"\r\n [tabindex]=\"-1\">\r\n </ec-form-control>\r\n </div>\r\n <ec-button *ngIf=\"displayType === 'file'\"\r\n #browseBtn\r\n id=\"{{inputId}}_browseBtn\"\r\n (clicked)=\"fileInput.click()\"\r\n type=\"secondary\"\r\n [tabindex]=\"tabindex\"\r\n [disabled]=\"formModel?.get('name').disabled\"\r\n label=\"Browse\"\r\n [autofocus]=\"autofocus\">\r\n </ec-button>\r\n </div>\r\n <ec-button *ngIf=\"displayType === 'button'\"\r\n id=\"{{inputId}}_btn\"\r\n [pending]=\"pending\"\r\n [type]=\"buttonType\"\r\n [label]=\"buttonLabel ?? 'Browse_TC' | translate\"\r\n (clicked)=\"fileInput.click()\"\r\n style=\"width: 100%;\">\r\n </ec-button>\r\n</ec-form-group>", styles: [":host{display:block;margin-bottom:1rem}ec-form-control{margin-bottom:0}ec-form-control ::ng-deep>.ec-focus-ring{display:none!important}input[type=file]{opacity:0;display:block;position:absolute;top:0;left:0;width:100%;height:100%;z-index:1;cursor:pointer}input[type=file].has-value{width:calc(100% - 1.5rem)}ec-button{--ec-button-border-color-secondary: var(--ec-form-control-border-color);--ec-button-color-icon-secondary: var(--ec-color-icon)}\n"] }]
4293
+ }], ctorParameters: function () { return [{ type: ValidationMessageService }, { type: FormGroupHelper }]; }, propDecorators: { formModel: [{
4294
+ type: Input
4295
+ }], placeholder: [{
4281
4296
  type: Input
4282
4297
  }], fileType: [{
4283
4298
  type: Input
@@ -10579,6 +10594,10 @@ class PageBaseComponent {
10579
10594
  */
10580
10595
  async onSave(event) {
10581
10596
  this.formGroup.markAllAsTouched();
10597
+ // If the form has pending async validators, wait for it them to complete before proceeding
10598
+ if (this.formGroup.pending) {
10599
+ await this.formGroup.statusChanges.pipe(filter(status => status !== 'PENDING'), take(1), takeUntil(this.destroyed)).toPromise();
10600
+ }
10582
10601
  if (this.formGroup.valid) {
10583
10602
  await this.save();
10584
10603
  }