spiderly 19.5.3 → 19.5.4-preview.0
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.
- package/fesm2022/spiderly.mjs +178 -66
- package/fesm2022/spiderly.mjs.map +1 -1
- package/lib/components/base-form/base-form copy.d.ts +130 -9
- package/lib/components/spiderly-form-control/spiderly-form-control.d.ts +8 -2
- package/lib/controls/spiderly-file/spiderly-file.component.d.ts +6 -2
- package/lib/entities/security-entities.d.ts +4 -4
- package/lib/services/base-form.service.d.ts +2 -4
- package/lib/services/helper-functions.d.ts +5 -1
- package/lib/services/validator-abstract.service.d.ts +5 -2
- package/package.json +1 -1
- package/public-api.d.ts +0 -2
- package/lib/entities/mime-type.d.ts +0 -11
- package/lib/services/translate-labels-abstract.service.d.ts +0 -7
package/fesm2022/spiderly.mjs
CHANGED
|
@@ -209,7 +209,7 @@ class SpiderlyFormGroup extends FormGroup {
|
|
|
209
209
|
constructor(controls) {
|
|
210
210
|
super(controls);
|
|
211
211
|
this.trackingId = crypto.randomUUID();
|
|
212
|
-
|
|
212
|
+
// TODO: Delete controlNamesFromHtml and add UIDoNotGenerate flag into ng entity generator, we shouldn't even add those into parentFormGroup
|
|
213
213
|
this.controlNamesFromHtml = [];
|
|
214
214
|
this.getControl = (formControlName) => {
|
|
215
215
|
if (this.controlNamesFromHtml.findIndex((x) => x === formControlName) === -1)
|
|
@@ -285,10 +285,10 @@ class SpiderlyFormArray extends FormArray {
|
|
|
285
285
|
class ValidatorAbstractService {
|
|
286
286
|
constructor(translocoService) {
|
|
287
287
|
this.translocoService = translocoService;
|
|
288
|
-
this.
|
|
288
|
+
this.notEmpty = (control) => {
|
|
289
289
|
const validator = () => {
|
|
290
290
|
const value = control.value;
|
|
291
|
-
const notEmptyRule = typeof value !== 'undefined' && value !== null && value
|
|
291
|
+
const notEmptyRule = typeof value !== 'undefined' && value !== null && value !== '';
|
|
292
292
|
const arrayValid = notEmptyRule;
|
|
293
293
|
return arrayValid
|
|
294
294
|
? null
|
|
@@ -296,25 +296,32 @@ class ValidatorAbstractService {
|
|
|
296
296
|
};
|
|
297
297
|
validator.hasNotEmptyRule = true;
|
|
298
298
|
control.required = true;
|
|
299
|
-
|
|
299
|
+
control.validator = validator;
|
|
300
|
+
control.updateValueAndValidity();
|
|
300
301
|
};
|
|
301
|
-
|
|
302
|
+
/** Validates that a SpiderlyFormArray (collection of form controls/groups) is not empty. */
|
|
303
|
+
this.isFormArrayEmpty = (control) => {
|
|
302
304
|
const validator = () => {
|
|
303
|
-
const value = control
|
|
304
|
-
const notEmptyRule = typeof value !== 'undefined' && value !== null && value !==
|
|
305
|
+
const value = control;
|
|
306
|
+
const notEmptyRule = typeof value !== 'undefined' && value !== null && value.length !== 0;
|
|
305
307
|
const arrayValid = notEmptyRule;
|
|
306
308
|
return arrayValid
|
|
307
309
|
? null
|
|
308
|
-
: {
|
|
310
|
+
: {
|
|
311
|
+
_: this.translocoService.translate('ListCanNotBeEmpty', {
|
|
312
|
+
value: control.labelForDisplay,
|
|
313
|
+
}),
|
|
314
|
+
};
|
|
309
315
|
};
|
|
310
316
|
validator.hasNotEmptyRule = true;
|
|
311
317
|
control.required = true;
|
|
312
|
-
control.validator
|
|
318
|
+
control.setValidators(validator);
|
|
313
319
|
control.updateValueAndValidity();
|
|
314
320
|
};
|
|
315
|
-
|
|
321
|
+
/** Validates that a SpiderlyFormControl holding an array value (e.g., multi-select dropdown) is not empty. */
|
|
322
|
+
this.isArrayEmpty = (control) => {
|
|
316
323
|
const validator = () => {
|
|
317
|
-
const value = control;
|
|
324
|
+
const value = control.value;
|
|
318
325
|
const notEmptyRule = typeof value !== 'undefined' && value !== null && value.length !== 0;
|
|
319
326
|
const arrayValid = notEmptyRule;
|
|
320
327
|
return arrayValid
|
|
@@ -765,7 +772,7 @@ class SpiderlyEditorComponent extends BaseControl {
|
|
|
765
772
|
};
|
|
766
773
|
}
|
|
767
774
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: SpiderlyEditorComponent, deps: [{ token: i1.TranslocoService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
768
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.13", type: SpiderlyEditorComponent, isStandalone: true, selector: "spiderly-editor", viewQueries: [{ propertyName: "editor", first: true, predicate: Editor, descendants: true }, { propertyName: "tooltip", first: true, predicate: Tooltip, descendants: true }], usesInheritance: true, ngImport: i0, template: "<!-- Can't put (onBlur) in this control -->\n\n<div style=\"display: flex; flex-direction: column; gap: 0.5rem\">\n <div *ngIf=\"getTranslatedLabel() != '' && getTranslatedLabel() != null\">\n <label>{{ getTranslatedLabel() }}</label>\n <required *ngIf=\"control?.required && showRequired\"></required>\n </div>\n\n <!-- Disable doesn't work on this control -->\n <p-editor\n *ngIf=\"control\"\n [formControl]=\"control\"\n [pTooltip]=\"getValidationErrrorMessages()\"\n [tooltipEvent]=\"errorMessageTooltipEvent\"\n tooltipPosition=\"bottom\"\n [tooltipDisabled]=\"control.valid\"\n tooltipStyleClass=\"spiderly-tooltip-invalid\"\n [readonly]=\"control.disabled\"\n [class]=\"control.invalid && control.dirty ? 'control-error-border' : ''\"\n [id]=\"control.label\"\n [placeholder]=\"placeholder\"\n (click)=\"onClick()\"\n [style]=\"{ height: '320px' }\"\n ></p-editor>\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: EditorModule }, { kind: "component", type: i4$8.Editor, selector: "p-editor", inputs: ["style", "styleClass", "placeholder", "formats", "modules", "bounds", "scrollingContainer", "debug", "readonly"], outputs: ["onInit", "onTextChange", "onSelectionChange"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i5.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "appendTo", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions"] }, { kind: "component", type: RequiredComponent, selector: "required" }] }); }
|
|
775
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.13", type: SpiderlyEditorComponent, isStandalone: true, selector: "spiderly-editor", viewQueries: [{ propertyName: "editor", first: true, predicate: Editor, descendants: true }, { propertyName: "tooltip", first: true, predicate: Tooltip, descendants: true }], usesInheritance: true, ngImport: i0, template: "<!-- Can't put (onBlur) in this control -->\n\n<div style=\"display: flex; flex-direction: column; gap: 0.5rem; padding: 0 1px\">\n <div *ngIf=\"getTranslatedLabel() != '' && getTranslatedLabel() != null\">\n <label>{{ getTranslatedLabel() }}</label>\n <required *ngIf=\"control?.required && showRequired\"></required>\n </div>\n\n <!-- Disable doesn't work on this control -->\n <p-editor\n *ngIf=\"control\"\n [formControl]=\"control\"\n [pTooltip]=\"getValidationErrrorMessages()\"\n [tooltipEvent]=\"errorMessageTooltipEvent\"\n tooltipPosition=\"bottom\"\n [tooltipDisabled]=\"control.valid\"\n tooltipStyleClass=\"spiderly-tooltip-invalid\"\n [readonly]=\"control.disabled\"\n [class]=\"control.invalid && control.dirty ? 'control-error-border' : ''\"\n [id]=\"control.label\"\n [placeholder]=\"placeholder\"\n (click)=\"onClick()\"\n [style]=\"{ height: '320px' }\"\n ></p-editor>\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: EditorModule }, { kind: "component", type: i4$8.Editor, selector: "p-editor", inputs: ["style", "styleClass", "placeholder", "formats", "modules", "bounds", "scrollingContainer", "debug", "readonly"], outputs: ["onInit", "onTextChange", "onSelectionChange"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i5.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "appendTo", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions"] }, { kind: "component", type: RequiredComponent, selector: "required" }] }); }
|
|
769
776
|
}
|
|
770
777
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: SpiderlyEditorComponent, decorators: [{
|
|
771
778
|
type: Component,
|
|
@@ -776,7 +783,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImpo
|
|
|
776
783
|
EditorModule,
|
|
777
784
|
TooltipModule,
|
|
778
785
|
RequiredComponent,
|
|
779
|
-
], template: "<!-- Can't put (onBlur) in this control -->\n\n<div style=\"display: flex; flex-direction: column; gap: 0.5rem\">\n <div *ngIf=\"getTranslatedLabel() != '' && getTranslatedLabel() != null\">\n <label>{{ getTranslatedLabel() }}</label>\n <required *ngIf=\"control?.required && showRequired\"></required>\n </div>\n\n <!-- Disable doesn't work on this control -->\n <p-editor\n *ngIf=\"control\"\n [formControl]=\"control\"\n [pTooltip]=\"getValidationErrrorMessages()\"\n [tooltipEvent]=\"errorMessageTooltipEvent\"\n tooltipPosition=\"bottom\"\n [tooltipDisabled]=\"control.valid\"\n tooltipStyleClass=\"spiderly-tooltip-invalid\"\n [readonly]=\"control.disabled\"\n [class]=\"control.invalid && control.dirty ? 'control-error-border' : ''\"\n [id]=\"control.label\"\n [placeholder]=\"placeholder\"\n (click)=\"onClick()\"\n [style]=\"{ height: '320px' }\"\n ></p-editor>\n</div>\n" }]
|
|
786
|
+
], template: "<!-- Can't put (onBlur) in this control -->\n\n<div style=\"display: flex; flex-direction: column; gap: 0.5rem; padding: 0 1px\">\n <div *ngIf=\"getTranslatedLabel() != '' && getTranslatedLabel() != null\">\n <label>{{ getTranslatedLabel() }}</label>\n <required *ngIf=\"control?.required && showRequired\"></required>\n </div>\n\n <!-- Disable doesn't work on this control -->\n <p-editor\n *ngIf=\"control\"\n [formControl]=\"control\"\n [pTooltip]=\"getValidationErrrorMessages()\"\n [tooltipEvent]=\"errorMessageTooltipEvent\"\n tooltipPosition=\"bottom\"\n [tooltipDisabled]=\"control.valid\"\n tooltipStyleClass=\"spiderly-tooltip-invalid\"\n [readonly]=\"control.disabled\"\n [class]=\"control.invalid && control.dirty ? 'control-error-border' : ''\"\n [id]=\"control.label\"\n [placeholder]=\"placeholder\"\n (click)=\"onClick()\"\n [style]=\"{ height: '320px' }\"\n ></p-editor>\n</div>\n" }]
|
|
780
787
|
}], ctorParameters: () => [{ type: i1.TranslocoService }], propDecorators: { editor: [{
|
|
781
788
|
type: ViewChild,
|
|
782
789
|
args: [Editor]
|
|
@@ -887,6 +894,7 @@ function getMimeTypeForFileName(fileName) {
|
|
|
887
894
|
'.png': 'image/png',
|
|
888
895
|
'.webp': 'image/webp',
|
|
889
896
|
'.gif': 'image/gif',
|
|
897
|
+
'.svg': 'image/svg+xml',
|
|
890
898
|
'.pdf': 'application/pdf',
|
|
891
899
|
'.txt': 'text/plain',
|
|
892
900
|
'.html': 'text/html',
|
|
@@ -1033,7 +1041,7 @@ function toCommaSeparatedString(input) {
|
|
|
1033
1041
|
return stringList[0] ?? '';
|
|
1034
1042
|
}
|
|
1035
1043
|
}
|
|
1036
|
-
function
|
|
1044
|
+
function isFileImageType(mimeType) {
|
|
1037
1045
|
if (mimeType.startsWith('image/')) {
|
|
1038
1046
|
return true;
|
|
1039
1047
|
}
|
|
@@ -1136,6 +1144,16 @@ const primitiveArrayTypes = [
|
|
|
1136
1144
|
'Date[]',
|
|
1137
1145
|
'string[]',
|
|
1138
1146
|
];
|
|
1147
|
+
function getImageDimensions(file) {
|
|
1148
|
+
return new Promise((resolve) => {
|
|
1149
|
+
const img = new Image();
|
|
1150
|
+
img.onload = () => {
|
|
1151
|
+
resolve({ width: img.width, height: img.height });
|
|
1152
|
+
URL.revokeObjectURL(img.src);
|
|
1153
|
+
};
|
|
1154
|
+
img.src = URL.createObjectURL(file);
|
|
1155
|
+
});
|
|
1156
|
+
}
|
|
1139
1157
|
|
|
1140
1158
|
class SpiderlyMessageService {
|
|
1141
1159
|
// TODO FT: nece da prikaze poruku ako je neki angular error koji se dogodi tek nakon api poziva
|
|
@@ -1221,7 +1239,7 @@ class SpiderlyFileComponent extends BaseControl {
|
|
|
1221
1239
|
}
|
|
1222
1240
|
filesSelected(event) {
|
|
1223
1241
|
const file = event.files[0];
|
|
1224
|
-
if (this.
|
|
1242
|
+
if (this.isFileImageType(file.type) &&
|
|
1225
1243
|
this.hasImageDimensionConstraints()) {
|
|
1226
1244
|
this.files = [];
|
|
1227
1245
|
this.validatorService
|
|
@@ -1240,10 +1258,17 @@ class SpiderlyFileComponent extends BaseControl {
|
|
|
1240
1258
|
this.emitFileSelected(file);
|
|
1241
1259
|
}
|
|
1242
1260
|
}
|
|
1243
|
-
emitFileSelected(file) {
|
|
1261
|
+
async emitFileSelected(file) {
|
|
1244
1262
|
const formData = new FormData();
|
|
1245
1263
|
formData.append('file', file, `${this.objectId}-${file.name}`);
|
|
1246
|
-
|
|
1264
|
+
let width;
|
|
1265
|
+
let height;
|
|
1266
|
+
if (this.isFileImageType(file.type)) {
|
|
1267
|
+
const dimensions = await getImageDimensions(file);
|
|
1268
|
+
width = dimensions.width;
|
|
1269
|
+
height = dimensions.height;
|
|
1270
|
+
}
|
|
1271
|
+
this.onFileSelected.next(new SpiderlyFileSelectEvent({ file, formData, width, height }));
|
|
1247
1272
|
}
|
|
1248
1273
|
hasImageDimensionConstraints() {
|
|
1249
1274
|
return this.imageWidth > 0 || this.imageHeight > 0;
|
|
@@ -1284,14 +1309,14 @@ class SpiderlyFileComponent extends BaseControl {
|
|
|
1284
1309
|
const file = new File([blob], fileName, { type: mimeType });
|
|
1285
1310
|
return file;
|
|
1286
1311
|
}
|
|
1287
|
-
|
|
1288
|
-
return
|
|
1312
|
+
isFileImageType(mimeType) {
|
|
1313
|
+
return isFileImageType(mimeType);
|
|
1289
1314
|
}
|
|
1290
1315
|
isExcelFileType(mimeType) {
|
|
1291
1316
|
return isExcelFileType(mimeType);
|
|
1292
1317
|
}
|
|
1293
1318
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: SpiderlyFileComponent, deps: [{ token: i1.TranslocoService }, { token: SpiderlyMessageService }, { token: ValidatorAbstractService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1294
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.13", type: SpiderlyFileComponent, isStandalone: true, selector: "spiderly-file", inputs: { objectId: "objectId", fileData: "fileData", acceptedFileTypes: "acceptedFileTypes", required: "required", multiple: "multiple", isCloudinaryFileData: "isCloudinaryFileData", imageWidth: "imageWidth", imageHeight: "imageHeight", files: "files" }, outputs: { onFileSelected: "onFileSelected", onFileRemoved: "onFileRemoved" }, usesInheritance: true, ngImport: i0, template: "<ng-container *transloco=\"let t\">\n <div style=\"display: flex; flex-direction: column; gap: 0.5rem; padding: 1px\">\n <div *ngIf=\"getTranslatedLabel() != '' && getTranslatedLabel() != null\">\n <label>{{ getTranslatedLabel() }}</label>\n <!-- It's okay for this control, because for the custom uploads where we are not initializing the control from the backend, there is no need for formControl. -->\n <required *ngIf=\"control?.required || required\"></required>\n </div>\n\n <p-fileUpload\n [files]=\"files\"\n [disabled]=\"disabled\"\n [name]=\"control?.label ?? label\"\n [multiple]=\"multiple\"\n [accept]=\"acceptedFileTypesCommaSeparated\"\n [maxFileSize]=\"1000000\"\n (onSelect)=\"filesSelected($event)\"\n [class]=\"control?.invalid && control?.dirty ? 'control-error-border' : ''\"\n >\n <ng-template\n pTemplate=\"header\"\n let-files\n let-chooseCallback=\"chooseCallback\"\n let-clearCallback=\"clearCallback\"\n let-uploadCallback=\"uploadCallback\"\n >\n <div\n class=\"flex flex-wrap justify-content-between align-items-center flex-1 gap-2\"\n >\n <div class=\"flex gap-2\">\n <spiderly-button\n [disabled]=\"disabled\"\n (onClick)=\"choose($event, chooseCallback)\"\n icon=\"pi pi-upload\"\n [rounded]=\"true\"\n [outlined]=\"true\"\n />\n </div>\n </div>\n </ng-template>\n <ng-template\n pTemplate=\"content\"\n let-files\n let-removeFileCallback=\"removeFileCallback\"\n >\n <div *ngIf=\"files.length > 0\">\n <div class=\"flex justify-content-center p-0 gap-5\">\n <div\n *ngFor=\"let file of files; let index = index\"\n class=\"card m-0 px-3 py-3 flex flex-column align-items-center gap-3\"\n style=\"justify-content: center; overflow: hidden\"\n >\n <div *ngIf=\"
|
|
1319
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.13", type: SpiderlyFileComponent, isStandalone: true, selector: "spiderly-file", inputs: { objectId: "objectId", fileData: "fileData", acceptedFileTypes: "acceptedFileTypes", required: "required", multiple: "multiple", isCloudinaryFileData: "isCloudinaryFileData", imageWidth: "imageWidth", imageHeight: "imageHeight", files: "files" }, outputs: { onFileSelected: "onFileSelected", onFileRemoved: "onFileRemoved" }, usesInheritance: true, ngImport: i0, template: "<ng-container *transloco=\"let t\">\n <div style=\"display: flex; flex-direction: column; gap: 0.5rem; padding: 1px\">\n <div *ngIf=\"getTranslatedLabel() != '' && getTranslatedLabel() != null\">\n <label>{{ getTranslatedLabel() }}</label>\n <!-- It's okay for this control, because for the custom uploads where we are not initializing the control from the backend, there is no need for formControl. -->\n <required *ngIf=\"control?.required || required\"></required>\n </div>\n\n <p-fileUpload\n [files]=\"files\"\n [disabled]=\"disabled\"\n [name]=\"control?.label ?? label\"\n [multiple]=\"multiple\"\n [accept]=\"acceptedFileTypesCommaSeparated\"\n [maxFileSize]=\"1000000\"\n (onSelect)=\"filesSelected($event)\"\n [class]=\"control?.invalid && control?.dirty ? 'control-error-border' : ''\"\n >\n <ng-template\n pTemplate=\"header\"\n let-files\n let-chooseCallback=\"chooseCallback\"\n let-clearCallback=\"clearCallback\"\n let-uploadCallback=\"uploadCallback\"\n >\n <div\n class=\"flex flex-wrap justify-content-between align-items-center flex-1 gap-2\"\n >\n <div class=\"flex gap-2\">\n <spiderly-button\n [disabled]=\"disabled\"\n (onClick)=\"choose($event, chooseCallback)\"\n icon=\"pi pi-upload\"\n [rounded]=\"true\"\n [outlined]=\"true\"\n />\n </div>\n </div>\n </ng-template>\n <ng-template\n pTemplate=\"content\"\n let-files\n let-removeFileCallback=\"removeFileCallback\"\n >\n <div *ngIf=\"files.length > 0\">\n <div class=\"flex justify-content-center p-0 gap-5\">\n <div\n *ngFor=\"let file of files; let index = index\"\n class=\"card m-0 px-3 py-3 flex flex-column align-items-center gap-3\"\n style=\"justify-content: center; overflow: hidden\"\n >\n <div *ngIf=\"isFileImageType(file.type)\" class=\"image-container\">\n <img role=\"presentation\" [src]=\"file.objectURL\" />\n </div>\n <div *ngIf=\"isExcelFileType(file.type)\" class=\"excel-container\">\n <div class=\"excel-details\">\n <i\n class=\"pi pi-file-excel\"\n style=\"color: green; margin-right: 4px\"\n ></i>\n <span class=\"file-name\">{{ file.name }}</span>\n </div>\n </div>\n <spiderly-button\n [disabled]=\"disabled\"\n icon=\"pi pi-times\"\n (onClick)=\"fileRemoved(removeFileCallback, index)\"\n [outlined]=\"true\"\n [rounded]=\"true\"\n severity=\"danger\"\n />\n </div>\n </div>\n </div>\n </ng-template>\n <ng-template pTemplate=\"file\"> </ng-template>\n <ng-template pTemplate=\"empty\">\n <div class=\"flex align-items-center justify-content-center flex-column\">\n <i\n class=\"pi pi-cloud-upload border-2 border-circle p-5 text-8xl text-400 border-400 mt-3\"\n ></i>\n <p class=\"mt-4 mb-0\">{{ t(\"DragAndDropFilesHereToUpload\") }}</p>\n </div>\n </ng-template>\n </p-fileUpload>\n </div>\n</ng-container>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: FileUploadModule }, { kind: "component", type: i5$2.FileUpload, selector: "p-fileupload, p-fileUpload", inputs: ["name", "url", "method", "multiple", "accept", "disabled", "auto", "withCredentials", "maxFileSize", "invalidFileSizeMessageSummary", "invalidFileSizeMessageDetail", "invalidFileTypeMessageSummary", "invalidFileTypeMessageDetail", "invalidFileLimitMessageDetail", "invalidFileLimitMessageSummary", "style", "styleClass", "previewWidth", "chooseLabel", "uploadLabel", "cancelLabel", "chooseIcon", "uploadIcon", "cancelIcon", "showUploadButton", "showCancelButton", "mode", "headers", "customUpload", "fileLimit", "uploadStyleClass", "cancelStyleClass", "removeStyleClass", "chooseStyleClass", "chooseButtonProps", "uploadButtonProps", "cancelButtonProps", "files"], outputs: ["onBeforeUpload", "onSend", "onUpload", "onError", "onClear", "onRemove", "onSelect", "onProgress", "uploadHandler", "onImageError", "onRemoveUploadedFile"] }, { kind: "directive", type: i1$2.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "component", type: RequiredComponent, selector: "required" }, { kind: "component", type: SpiderlyButtonComponent, selector: "spiderly-button", inputs: ["type"] }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }] }); }
|
|
1295
1320
|
}
|
|
1296
1321
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: SpiderlyFileComponent, decorators: [{
|
|
1297
1322
|
type: Component,
|
|
@@ -1303,7 +1328,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImpo
|
|
|
1303
1328
|
RequiredComponent,
|
|
1304
1329
|
SpiderlyButtonComponent,
|
|
1305
1330
|
TranslocoDirective,
|
|
1306
|
-
], template: "<ng-container *transloco=\"let t\">\n <div style=\"display: flex; flex-direction: column; gap: 0.5rem; padding: 1px\">\n <div *ngIf=\"getTranslatedLabel() != '' && getTranslatedLabel() != null\">\n <label>{{ getTranslatedLabel() }}</label>\n <!-- It's okay for this control, because for the custom uploads where we are not initializing the control from the backend, there is no need for formControl. -->\n <required *ngIf=\"control?.required || required\"></required>\n </div>\n\n <p-fileUpload\n [files]=\"files\"\n [disabled]=\"disabled\"\n [name]=\"control?.label ?? label\"\n [multiple]=\"multiple\"\n [accept]=\"acceptedFileTypesCommaSeparated\"\n [maxFileSize]=\"1000000\"\n (onSelect)=\"filesSelected($event)\"\n [class]=\"control?.invalid && control?.dirty ? 'control-error-border' : ''\"\n >\n <ng-template\n pTemplate=\"header\"\n let-files\n let-chooseCallback=\"chooseCallback\"\n let-clearCallback=\"clearCallback\"\n let-uploadCallback=\"uploadCallback\"\n >\n <div\n class=\"flex flex-wrap justify-content-between align-items-center flex-1 gap-2\"\n >\n <div class=\"flex gap-2\">\n <spiderly-button\n [disabled]=\"disabled\"\n (onClick)=\"choose($event, chooseCallback)\"\n icon=\"pi pi-upload\"\n [rounded]=\"true\"\n [outlined]=\"true\"\n />\n </div>\n </div>\n </ng-template>\n <ng-template\n pTemplate=\"content\"\n let-files\n let-removeFileCallback=\"removeFileCallback\"\n >\n <div *ngIf=\"files.length > 0\">\n <div class=\"flex justify-content-center p-0 gap-5\">\n <div\n *ngFor=\"let file of files; let index = index\"\n class=\"card m-0 px-3 py-3 flex flex-column align-items-center gap-3\"\n style=\"justify-content: center; overflow: hidden\"\n >\n <div *ngIf=\"
|
|
1331
|
+
], template: "<ng-container *transloco=\"let t\">\n <div style=\"display: flex; flex-direction: column; gap: 0.5rem; padding: 1px\">\n <div *ngIf=\"getTranslatedLabel() != '' && getTranslatedLabel() != null\">\n <label>{{ getTranslatedLabel() }}</label>\n <!-- It's okay for this control, because for the custom uploads where we are not initializing the control from the backend, there is no need for formControl. -->\n <required *ngIf=\"control?.required || required\"></required>\n </div>\n\n <p-fileUpload\n [files]=\"files\"\n [disabled]=\"disabled\"\n [name]=\"control?.label ?? label\"\n [multiple]=\"multiple\"\n [accept]=\"acceptedFileTypesCommaSeparated\"\n [maxFileSize]=\"1000000\"\n (onSelect)=\"filesSelected($event)\"\n [class]=\"control?.invalid && control?.dirty ? 'control-error-border' : ''\"\n >\n <ng-template\n pTemplate=\"header\"\n let-files\n let-chooseCallback=\"chooseCallback\"\n let-clearCallback=\"clearCallback\"\n let-uploadCallback=\"uploadCallback\"\n >\n <div\n class=\"flex flex-wrap justify-content-between align-items-center flex-1 gap-2\"\n >\n <div class=\"flex gap-2\">\n <spiderly-button\n [disabled]=\"disabled\"\n (onClick)=\"choose($event, chooseCallback)\"\n icon=\"pi pi-upload\"\n [rounded]=\"true\"\n [outlined]=\"true\"\n />\n </div>\n </div>\n </ng-template>\n <ng-template\n pTemplate=\"content\"\n let-files\n let-removeFileCallback=\"removeFileCallback\"\n >\n <div *ngIf=\"files.length > 0\">\n <div class=\"flex justify-content-center p-0 gap-5\">\n <div\n *ngFor=\"let file of files; let index = index\"\n class=\"card m-0 px-3 py-3 flex flex-column align-items-center gap-3\"\n style=\"justify-content: center; overflow: hidden\"\n >\n <div *ngIf=\"isFileImageType(file.type)\" class=\"image-container\">\n <img role=\"presentation\" [src]=\"file.objectURL\" />\n </div>\n <div *ngIf=\"isExcelFileType(file.type)\" class=\"excel-container\">\n <div class=\"excel-details\">\n <i\n class=\"pi pi-file-excel\"\n style=\"color: green; margin-right: 4px\"\n ></i>\n <span class=\"file-name\">{{ file.name }}</span>\n </div>\n </div>\n <spiderly-button\n [disabled]=\"disabled\"\n icon=\"pi pi-times\"\n (onClick)=\"fileRemoved(removeFileCallback, index)\"\n [outlined]=\"true\"\n [rounded]=\"true\"\n severity=\"danger\"\n />\n </div>\n </div>\n </div>\n </ng-template>\n <ng-template pTemplate=\"file\"> </ng-template>\n <ng-template pTemplate=\"empty\">\n <div class=\"flex align-items-center justify-content-center flex-column\">\n <i\n class=\"pi pi-cloud-upload border-2 border-circle p-5 text-8xl text-400 border-400 mt-3\"\n ></i>\n <p class=\"mt-4 mb-0\">{{ t(\"DragAndDropFilesHereToUpload\") }}</p>\n </div>\n </ng-template>\n </p-fileUpload>\n </div>\n</ng-container>\n" }]
|
|
1307
1332
|
}], ctorParameters: () => [{ type: i1.TranslocoService }, { type: SpiderlyMessageService }, { type: ValidatorAbstractService }], propDecorators: { onFileSelected: [{
|
|
1308
1333
|
type: Output
|
|
1309
1334
|
}], onFileRemoved: [{
|
|
@@ -1328,10 +1353,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImpo
|
|
|
1328
1353
|
type: Input
|
|
1329
1354
|
}] } });
|
|
1330
1355
|
class SpiderlyFileSelectEvent extends BaseEntity {
|
|
1331
|
-
constructor({ file, formData, } = {}) {
|
|
1356
|
+
constructor({ file, formData, width, height, } = {}) {
|
|
1332
1357
|
super();
|
|
1333
1358
|
this.file = file;
|
|
1334
1359
|
this.formData = formData;
|
|
1360
|
+
this.width = width;
|
|
1361
|
+
this.height = height;
|
|
1335
1362
|
}
|
|
1336
1363
|
static { this.typeName = 'SpiderlyFileSelectEvent'; }
|
|
1337
1364
|
}
|
|
@@ -1548,12 +1575,12 @@ class UserRole extends BaseEntity {
|
|
|
1548
1575
|
}
|
|
1549
1576
|
class LoginVerificationToken extends BaseEntity {
|
|
1550
1577
|
static { this.typeName = 'LoginVerificationToken'; }
|
|
1551
|
-
constructor({ email, userId, browserId,
|
|
1578
|
+
constructor({ email, userId, browserId, expiresAt, } = {}) {
|
|
1552
1579
|
super();
|
|
1553
1580
|
this.email = email;
|
|
1554
1581
|
this.userId = userId;
|
|
1555
1582
|
this.browserId = browserId;
|
|
1556
|
-
this.
|
|
1583
|
+
this.expiresAt = expiresAt;
|
|
1557
1584
|
}
|
|
1558
1585
|
static { this.schema = {
|
|
1559
1586
|
email: {
|
|
@@ -1565,7 +1592,7 @@ class LoginVerificationToken extends BaseEntity {
|
|
|
1565
1592
|
browserId: {
|
|
1566
1593
|
type: 'string',
|
|
1567
1594
|
},
|
|
1568
|
-
|
|
1595
|
+
expiresAt: {
|
|
1569
1596
|
type: 'Date',
|
|
1570
1597
|
},
|
|
1571
1598
|
}; }
|
|
@@ -1622,21 +1649,8 @@ class SpiderlyError extends Error {
|
|
|
1622
1649
|
}
|
|
1623
1650
|
}
|
|
1624
1651
|
|
|
1625
|
-
class TranslateLabelsAbstractService {
|
|
1626
|
-
constructor() { }
|
|
1627
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: TranslateLabelsAbstractService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1628
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: TranslateLabelsAbstractService, providedIn: 'root' }); }
|
|
1629
|
-
}
|
|
1630
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: TranslateLabelsAbstractService, decorators: [{
|
|
1631
|
-
type: Injectable,
|
|
1632
|
-
args: [{
|
|
1633
|
-
providedIn: 'root',
|
|
1634
|
-
}]
|
|
1635
|
-
}], ctorParameters: () => [] });
|
|
1636
|
-
|
|
1637
1652
|
class BaseFormService {
|
|
1638
|
-
constructor(
|
|
1639
|
-
this.translateLabelsService = translateLabelsService;
|
|
1653
|
+
constructor(validatorService, messageService, translocoService) {
|
|
1640
1654
|
this.validatorService = validatorService;
|
|
1641
1655
|
this.messageService = messageService;
|
|
1642
1656
|
this.translocoService = translocoService;
|
|
@@ -1656,12 +1670,14 @@ class BaseFormService {
|
|
|
1656
1670
|
propSchema.type !== 'Namebook[]') {
|
|
1657
1671
|
if (existingControl instanceof SpiderlyFormArray) {
|
|
1658
1672
|
this.initFormArray(existingControl, propSchema.nestedConstructor, propInitialValue);
|
|
1673
|
+
this.validatorService.setFormArrayValidator(existingControl, targetClass.typeName);
|
|
1659
1674
|
}
|
|
1660
1675
|
else {
|
|
1661
1676
|
const control = new SpiderlyFormArray([], this.translocoService, this);
|
|
1662
1677
|
this.initFormArray(control, propSchema.nestedConstructor, propInitialValue);
|
|
1663
1678
|
control.label = formControlName;
|
|
1664
1679
|
control.labelForDisplay = this.getTranslatedLabel(formControlName);
|
|
1680
|
+
this.validatorService.setFormArrayValidator(control, targetClass.typeName);
|
|
1665
1681
|
formGroup.setControl(formControlName, control);
|
|
1666
1682
|
}
|
|
1667
1683
|
}
|
|
@@ -1681,6 +1697,9 @@ class BaseFormService {
|
|
|
1681
1697
|
if (formControlName === 'id' && !propInitialValue) {
|
|
1682
1698
|
propInitialValue = 0;
|
|
1683
1699
|
}
|
|
1700
|
+
if (propSchema.type.endsWith('[]') && propInitialValue == null) {
|
|
1701
|
+
propInitialValue = [];
|
|
1702
|
+
}
|
|
1684
1703
|
if (existingControl instanceof SpiderlyFormControl) {
|
|
1685
1704
|
existingControl.setValue(propInitialValue);
|
|
1686
1705
|
}
|
|
@@ -1755,7 +1774,7 @@ class BaseFormService {
|
|
|
1755
1774
|
else if (formControlName.endsWith('DisplayName')) {
|
|
1756
1775
|
formControlName = formControlName.replace('DisplayName', '');
|
|
1757
1776
|
}
|
|
1758
|
-
return this.
|
|
1777
|
+
return this.translocoService.translate(firstCharToUpper(formControlName));
|
|
1759
1778
|
}
|
|
1760
1779
|
addNewFormGroupToFormArray(formArray, targetClass, initialValues, index) {
|
|
1761
1780
|
let helperFormGroup = new SpiderlyFormGroup({});
|
|
@@ -1815,6 +1834,11 @@ class BaseFormService {
|
|
|
1815
1834
|
});
|
|
1816
1835
|
}
|
|
1817
1836
|
else if (control instanceof SpiderlyFormArray) {
|
|
1837
|
+
if (control.errors) {
|
|
1838
|
+
control.markAsDirty();
|
|
1839
|
+
this.messageService.warningMessage(control.errors['_']);
|
|
1840
|
+
invalid = true;
|
|
1841
|
+
}
|
|
1818
1842
|
control.controls.forEach((nestedControl) => {
|
|
1819
1843
|
if (!this.isControlValid(nestedControl)) {
|
|
1820
1844
|
invalid = true;
|
|
@@ -1826,7 +1850,7 @@ class BaseFormService {
|
|
|
1826
1850
|
}
|
|
1827
1851
|
return true;
|
|
1828
1852
|
}
|
|
1829
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: BaseFormService, deps: [{ token:
|
|
1853
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: BaseFormService, deps: [{ token: ValidatorAbstractService }, { token: SpiderlyMessageService }, { token: i1.TranslocoService }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1830
1854
|
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: BaseFormService, providedIn: 'root' }); }
|
|
1831
1855
|
}
|
|
1832
1856
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: BaseFormService, decorators: [{
|
|
@@ -1834,7 +1858,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImpo
|
|
|
1834
1858
|
args: [{
|
|
1835
1859
|
providedIn: 'root',
|
|
1836
1860
|
}]
|
|
1837
|
-
}], ctorParameters: () => [{ type:
|
|
1861
|
+
}], ctorParameters: () => [{ type: ValidatorAbstractService }, { type: SpiderlyMessageService }, { type: i1.TranslocoService }] });
|
|
1838
1862
|
|
|
1839
1863
|
class BaseFormCopy {
|
|
1840
1864
|
constructor(differs, http, messageService, changeDetectorRef, router, route, translocoService, baseFormService) {
|
|
@@ -1846,28 +1870,81 @@ class BaseFormCopy {
|
|
|
1846
1870
|
this.route = route;
|
|
1847
1871
|
this.translocoService = translocoService;
|
|
1848
1872
|
this.baseFormService = baseFormService;
|
|
1873
|
+
/**
|
|
1874
|
+
* The root form group that holds all form controls, typed to `TSaveBody`.
|
|
1875
|
+
* Assign `saveObservableMethod` on it to define the HTTP call used for saving.
|
|
1876
|
+
* The form controls are built automatically from the `TSaveBody` schema when you call
|
|
1877
|
+
* `baseFormService.initFormGroup(this.parentFormGroup, saveBodyClass, saveBody)`.
|
|
1878
|
+
*
|
|
1879
|
+
* @example
|
|
1880
|
+
* ```ts
|
|
1881
|
+
* this.parentFormGroup.saveObservableMethod = this.apiService.saveProduct;
|
|
1882
|
+
*
|
|
1883
|
+
* this.baseFormService.initFormGroup(
|
|
1884
|
+
* this.parentFormGroup,
|
|
1885
|
+
* ProductSaveBody,
|
|
1886
|
+
* saveBody,
|
|
1887
|
+
* );
|
|
1888
|
+
* ```
|
|
1889
|
+
*/
|
|
1849
1890
|
this.parentFormGroup = new SpiderlyFormGroup({});
|
|
1891
|
+
/**
|
|
1892
|
+
* The toast message displayed after a successful save.
|
|
1893
|
+
* Override this to customize the success notification text for a specific entity.
|
|
1894
|
+
* If you want to change the message for all entities, update the `SuccessfulSaveToastDescription` key
|
|
1895
|
+
* in your translation JSON file instead.
|
|
1896
|
+
*
|
|
1897
|
+
* @example
|
|
1898
|
+
* ```ts
|
|
1899
|
+
* this.successfulSaveToastDescription = 'Product saved successfully!';
|
|
1900
|
+
* ```
|
|
1901
|
+
*/
|
|
1850
1902
|
this.successfulSaveToastDescription = this.translocoService.translate('SuccessfulSaveToastDescription');
|
|
1851
1903
|
//#region Model
|
|
1904
|
+
/**
|
|
1905
|
+
* Executes the save flow for the form. The execution order is:
|
|
1906
|
+
* 1. Builds the save body from the form's raw value.
|
|
1907
|
+
* 2. Calls {@link onBeforeSave} — use this to modify the save body before validation.
|
|
1908
|
+
* 3. Validates the form. If invalid, shows an error message and stops.
|
|
1909
|
+
* 4. Sends the save HTTP request via `saveObservableMethod`.
|
|
1910
|
+
* 5. Calls {@link onAfterSaveRequest} — fires immediately after the request is sent, before the response arrives.
|
|
1911
|
+
* 6. On successful response: shows a success toast, reroutes, and calls {@link onAfterSave}. The form is re-initialized only when `rerouteToParentSlugAfterSave` is `false`.
|
|
1912
|
+
*
|
|
1913
|
+
* @param rerouteToParentSlugAfterSave - When `true` (default), navigates to the parent URL after save. When `false`, re-initializes the form and navigates to the saved object's URL.
|
|
1914
|
+
*
|
|
1915
|
+
* @example
|
|
1916
|
+
* ```html
|
|
1917
|
+
* <button (click)="onSave()">Save</button>
|
|
1918
|
+
* ```
|
|
1919
|
+
*
|
|
1920
|
+
* @example
|
|
1921
|
+
* ```html
|
|
1922
|
+
* <!-- Save and stay on the saved object's page -->
|
|
1923
|
+
* <button (click)="onSave(false)">Save and stay</button>
|
|
1924
|
+
* ```
|
|
1925
|
+
*/
|
|
1852
1926
|
// onSave method is here only because of the hooks, we should move everything except them to the BaseFromService
|
|
1853
|
-
this.onSave = (
|
|
1927
|
+
this.onSave = (rerouteToParentSlugAfterSave = true) => {
|
|
1854
1928
|
if (!this.saveBodyClass)
|
|
1855
1929
|
throw new SpiderlyError('You did not initialize saveBodyClass');
|
|
1856
1930
|
if (!this.mainUIFormClass)
|
|
1857
1931
|
throw new SpiderlyError('You did not initialize mainUIFormClass');
|
|
1858
|
-
|
|
1859
|
-
this.onBeforeSave(
|
|
1860
|
-
this.saveBody = this.saveBody ?? this.parentFormGroup.getRawValue();
|
|
1932
|
+
let saveBody = this.parentFormGroup.getRawValue();
|
|
1933
|
+
this.onBeforeSave(saveBody);
|
|
1861
1934
|
const isValid = this.baseFormService.isControlValid(this.parentFormGroup);
|
|
1862
1935
|
if (isValid) {
|
|
1863
1936
|
this.parentFormGroup
|
|
1864
|
-
.saveObservableMethod(
|
|
1937
|
+
.saveObservableMethod(saveBody)
|
|
1865
1938
|
.subscribe((res) => {
|
|
1866
1939
|
this.messageService.successMessage(this.successfulSaveToastDescription);
|
|
1867
|
-
|
|
1868
|
-
|
|
1940
|
+
if (rerouteToParentSlugAfterSave) {
|
|
1941
|
+
this.rerouteToSavedObject(undefined);
|
|
1942
|
+
}
|
|
1943
|
+
else {
|
|
1944
|
+
saveBody = this.baseFormService.mapMainUIFormToSaveBody(this.mainUIFormClass, res);
|
|
1945
|
+
this.baseFormService.initFormGroup(this.parentFormGroup, this.saveBodyClass, saveBody);
|
|
1869
1946
|
const saveBodyMainDTOKey = this.baseFormService.getSaveBodyMainDTOKey(this.saveBodyClass);
|
|
1870
|
-
const savedObjectId =
|
|
1947
|
+
const savedObjectId = saveBody[saveBodyMainDTOKey]?.id;
|
|
1871
1948
|
this.rerouteToSavedObject(savedObjectId); // You always need to have id, because of id == 0 and version change
|
|
1872
1949
|
}
|
|
1873
1950
|
this.onAfterSave();
|
|
@@ -1878,6 +1955,21 @@ class BaseFormCopy {
|
|
|
1878
1955
|
this.baseFormService.showInvalidFieldsMessage();
|
|
1879
1956
|
}
|
|
1880
1957
|
};
|
|
1958
|
+
/**
|
|
1959
|
+
* Handles navigation after a successful save.
|
|
1960
|
+
* Override this to customize the post-save navigation behavior.
|
|
1961
|
+
* By default, navigates to the parent URL when `rerouteId` is not provided, or to the saved object's URL otherwise.
|
|
1962
|
+
*
|
|
1963
|
+
* @param rerouteId - The ID of the saved object, used to build the target URL. When not provided, navigates to the parent URL.
|
|
1964
|
+
*
|
|
1965
|
+
* @example
|
|
1966
|
+
* ```ts
|
|
1967
|
+
* // Override to navigate to a custom route after save
|
|
1968
|
+
* rerouteToSavedObject = (rerouteId: number | string): void => {
|
|
1969
|
+
* this.router.navigateByUrl(`/products/${rerouteId}/details`);
|
|
1970
|
+
* };
|
|
1971
|
+
* ```
|
|
1972
|
+
*/
|
|
1881
1973
|
this.rerouteToSavedObject = (rerouteId) => {
|
|
1882
1974
|
if (rerouteId == null) {
|
|
1883
1975
|
const currentUrl = this.router.url;
|
|
@@ -1890,8 +1982,43 @@ class BaseFormCopy {
|
|
|
1890
1982
|
const newUrl = segments.join('/');
|
|
1891
1983
|
this.router.navigateByUrl(newUrl);
|
|
1892
1984
|
};
|
|
1985
|
+
/**
|
|
1986
|
+
* Hook that runs **before** form validation and the save request.
|
|
1987
|
+
* Use this to modify the save body or perform any pre-save logic (e.g., transforming data, setting computed fields).
|
|
1988
|
+
*
|
|
1989
|
+
* @param saveBody - The current save body built from the form's raw value. Mutate it directly to change what gets sent to the server.
|
|
1990
|
+
*
|
|
1991
|
+
* @example
|
|
1992
|
+
* ```ts
|
|
1993
|
+
* onBeforeSave = (saveBody?: ProductSaveBody) => {
|
|
1994
|
+
* saveBody.productDTO.fullName = saveBody.productDTO.firstName + ' ' + saveBody.productDTO.lastName;
|
|
1995
|
+
* };
|
|
1996
|
+
* ```
|
|
1997
|
+
*/
|
|
1893
1998
|
this.onBeforeSave = (saveBody) => { };
|
|
1999
|
+
/**
|
|
2000
|
+
* Hook that runs **after** a successful save response is received.
|
|
2001
|
+
* Use this for post-save side effects (e.g., refreshing related data, showing additional notifications).
|
|
2002
|
+
*
|
|
2003
|
+
* @example
|
|
2004
|
+
* ```ts
|
|
2005
|
+
* onAfterSave = () => {
|
|
2006
|
+
* this.loadRelatedProducts();
|
|
2007
|
+
* };
|
|
2008
|
+
* ```
|
|
2009
|
+
*/
|
|
1894
2010
|
this.onAfterSave = () => { };
|
|
2011
|
+
/**
|
|
2012
|
+
* Hook that runs immediately **after** the save HTTP request is sent, but **before** the response arrives.
|
|
2013
|
+
* Use this for side effects that should happen as soon as the request is dispatched (e.g., disabling UI elements, starting a loading indicator).
|
|
2014
|
+
*
|
|
2015
|
+
* @example
|
|
2016
|
+
* ```ts
|
|
2017
|
+
* onAfterSaveRequest = () => {
|
|
2018
|
+
* this.isSaving = true;
|
|
2019
|
+
* };
|
|
2020
|
+
* ```
|
|
2021
|
+
*/
|
|
1895
2022
|
this.onAfterSaveRequest = () => { };
|
|
1896
2023
|
}
|
|
1897
2024
|
ngOnInit() { }
|
|
@@ -4379,21 +4506,6 @@ class LazyLoadSelectedIdsResult extends BaseEntity {
|
|
|
4379
4506
|
class MenuChangeEvent {
|
|
4380
4507
|
}
|
|
4381
4508
|
|
|
4382
|
-
class MimeTypes {
|
|
4383
|
-
constructor(value) {
|
|
4384
|
-
this.value = value;
|
|
4385
|
-
}
|
|
4386
|
-
static { this.Pdf = new MimeTypes('application/pdf'); }
|
|
4387
|
-
static { this.Zip = new MimeTypes('application/zip'); }
|
|
4388
|
-
static { this.Jpeg = new MimeTypes('image/jpeg'); }
|
|
4389
|
-
static { this.Png = new MimeTypes('image/png'); }
|
|
4390
|
-
static { this.Svg = new MimeTypes('image/svg'); }
|
|
4391
|
-
static { this.Webp = new MimeTypes('image/webp'); }
|
|
4392
|
-
toString() {
|
|
4393
|
-
return this.value;
|
|
4394
|
-
}
|
|
4395
|
-
}
|
|
4396
|
-
|
|
4397
4509
|
class Namebook extends BaseEntity {
|
|
4398
4510
|
constructor({ id, displayName, } = {}) {
|
|
4399
4511
|
super();
|
|
@@ -4731,5 +4843,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImpo
|
|
|
4731
4843
|
* Generated bundle index. Do not edit.
|
|
4732
4844
|
*/
|
|
4733
4845
|
|
|
4734
|
-
export { Action, AllClickEvent, ApiSecurityService, AppSidebarComponent, AuthGuard, AuthResult, AuthServiceBase, BaseAutocompleteControl, BaseControl, BaseDropdownControl, BaseEntity, BaseFormCopy, BaseFormService, CardSkeletonComponent, Codebook, Column, ConfigServiceBase, ExternalProvider, Filter, FilterRule, FilterSortMeta, FooterComponent, GoogleButtonComponent, IndexCardComponent, InfoCardComponent, InitCompanyAuthDialogDetails, InitTopBarData, IsAuthorizedForSaveEvent, LastMenuIconIndexClicked, LayoutServiceBase, LazyLoadSelectedIdsResult, Login, LoginComponent, LoginVerificationComponent, LoginVerificationToken, MatchModeCodes, MenuChangeEvent, MenuitemComponent,
|
|
4846
|
+
export { Action, AllClickEvent, ApiSecurityService, AppSidebarComponent, AuthGuard, AuthResult, AuthServiceBase, BaseAutocompleteControl, BaseControl, BaseDropdownControl, BaseEntity, BaseFormCopy, BaseFormService, CardSkeletonComponent, Codebook, Column, ConfigServiceBase, ExternalProvider, Filter, FilterRule, FilterSortMeta, FooterComponent, GoogleButtonComponent, IndexCardComponent, InfoCardComponent, InitCompanyAuthDialogDetails, InitTopBarData, IsAuthorizedForSaveEvent, LastMenuIconIndexClicked, LayoutServiceBase, LazyLoadSelectedIdsResult, Login, LoginComponent, LoginVerificationComponent, LoginVerificationToken, MatchModeCodes, MenuChangeEvent, MenuitemComponent, Namebook, NotAuthGuard, NotFoundComponent, PROPS_KEY, PaginatedResult, PanelBodyComponent, PanelFooterComponent, PanelHeaderComponent, PrimengOption, ProfileAvatarComponent, ReflectProp, RefreshTokenRequest, RequiredComponent, RowClickEvent, SecurityPermissionCodes, SendLoginVerificationEmailResult, SideMenuTopBarComponent, SidebarMenuComponent, SimpleSaveResult, SpiderlyAutocompleteComponent, SpiderlyButtonBaseComponent, SpiderlyButtonComponent, SpiderlyCalendarComponent, SpiderlyCardComponent, SpiderlyCheckboxComponent, SpiderlyColorPickerComponent, SpiderlyControlsModule, SpiderlyDataTableComponent, SpiderlyDataViewComponent, SpiderlyDeleteConfirmationComponent, SpiderlyDropdownComponent, SpiderlyEditorComponent, SpiderlyErrorHandler, SpiderlyFileComponent, SpiderlyFileSelectEvent, SpiderlyFormArray, SpiderlyFormControl, SpiderlyFormGroup, SpiderlyLayoutComponent, SpiderlyMessageService, SpiderlyMultiAutocompleteComponent, SpiderlyMultiSelectComponent, SpiderlyNumberComponent, SpiderlyPanelComponent, SpiderlyPanelsModule, SpiderlyPasswordComponent, SpiderlyReturnButtonComponent, SpiderlySplitButtonComponent, SpiderlyTab, SpiderlyTemplateTypeDirective, SpiderlyTextareaComponent, SpiderlyTextboxComponent, SpiderlyTranslocoLoader, TopBarComponent, UserBase, UserRole, ValidatorAbstractService, VerificationTokenRequest, VerificationTypeCodes, VerificationWrapperComponent, adjustColor, authInitializer, capitalizeFirstChar, createFakeGoogleWrapper, deleteAction, exportListToExcel, firstCharToUpper, getFileNameFromContentDisposition, getHtmlImgDisplayString64, getImageDimensions, getMimeTypeForFileName, getMonth, getParentUrl, getPrimengAutocompleteCodebookOptions, getPrimengAutocompleteNamebookOptions, getPrimengDropdownCodebookOptions, getPrimengDropdownNamebookOptions, httpLoadingInterceptor, isExcelFileType, isFileImageType, isNullOrEmpty, jsonHttpInterceptor, jwtInterceptor, kebabToTitleCase, nameOf, nameof, primitiveArrayTypes, pushAction, selectedTab, singleOrDefault, splitPascalCase, toCommaSeparatedString, unauthorizedInterceptor, validatePrecisionScale };
|
|
4735
4847
|
//# sourceMappingURL=spiderly.mjs.map
|