ngx-material-entity 16.0.1 → 16.0.3

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.
Files changed (98) hide show
  1. package/components/edit-page/edit-page.component.d.ts +13 -2
  2. package/components/input/boolean/boolean-checkbox-input/boolean-checkbox-input.component.d.ts +1 -0
  3. package/components/input/boolean/boolean-toggle-input/boolean-toggle-input.component.d.ts +2 -1
  4. package/components/input/input.component.d.ts +28 -3
  5. package/components/input/input.module.d.ts +5 -2
  6. package/components/input/number/number-slider-input/number-slider-input.component.d.ts +2 -1
  7. package/components/table/create-dialog/create-entity-dialog.component.d.ts +3 -0
  8. package/components/table/edit-dialog/edit-entity-dialog.component.d.ts +4 -0
  9. package/components/tooltip/tooltip.component.d.ts +9 -0
  10. package/decorators/array/array-decorator-internal.data.d.ts +6 -6
  11. package/decorators/array/array-decorator.data.d.ts +7 -7
  12. package/decorators/base/property-decorator-internal.data.d.ts +11 -2
  13. package/decorators/base/property-decorator.data.d.ts +9 -1
  14. package/decorators/boolean/boolean-decorator-internal.data.d.ts +3 -3
  15. package/decorators/boolean/boolean-decorator.data.d.ts +1 -1
  16. package/decorators/custom/custom-decorator-internal.data.d.ts +2 -2
  17. package/decorators/custom/custom-decorator.data.d.ts +4 -4
  18. package/decorators/date/date-decorator-internal.data.d.ts +4 -4
  19. package/decorators/date/date-decorator.data.d.ts +5 -5
  20. package/decorators/file/file-decorator-internal.data.d.ts +3 -3
  21. package/decorators/file/file-decorator.data.d.ts +1 -1
  22. package/decorators/has-many/has-many-decorator-internal.data.d.ts +1 -1
  23. package/decorators/has-many/has-many-decorator.data.d.ts +1 -1
  24. package/decorators/number/number-decorator-internal.data.d.ts +3 -3
  25. package/decorators/number/number-decorator.data.d.ts +1 -1
  26. package/decorators/object/object-decorator-internal.data.d.ts +1 -1
  27. package/decorators/object/object-decorator.data.d.ts +1 -1
  28. package/decorators/references-many/references-many-decorator-internal.data.d.ts +1 -1
  29. package/decorators/references-many/references-many-decorator.data.d.ts +1 -1
  30. package/decorators/references-one/references-one-decorator-internal.data.d.ts +1 -1
  31. package/decorators/references-one/references-one-decorator.data.d.ts +1 -1
  32. package/decorators/string/string-decorator-internal.data.d.ts +5 -6
  33. package/decorators/string/string-decorator.data.d.ts +1 -7
  34. package/directives/number.directive.d.ts +17 -0
  35. package/directives/password-match.directive.d.ts +14 -0
  36. package/directives/tooltip.directive.d.ts +33 -0
  37. package/esm2022/components/edit-page/edit-page.component.mjs +57 -13
  38. package/esm2022/components/input/array/array-date-range-input/array-date-range-input.component.mjs +1 -1
  39. package/esm2022/components/input/array/array-string-chips-input/array-string-chips-input.component.mjs +2 -2
  40. package/esm2022/components/input/base-input.component.mjs +2 -1
  41. package/esm2022/components/input/boolean/boolean-checkbox-input/boolean-checkbox-input.component.mjs +6 -3
  42. package/esm2022/components/input/boolean/boolean-toggle-input/boolean-toggle-input.component.mjs +6 -3
  43. package/esm2022/components/input/date/date-range-input/date-range-input.component.mjs +3 -1
  44. package/esm2022/components/input/file/file-image-input/file-image-input.component.mjs +3 -3
  45. package/esm2022/components/input/file/file-input/file-input.component.mjs +1 -1
  46. package/esm2022/components/input/input.component.mjs +94 -35
  47. package/esm2022/components/input/input.module.mjs +15 -5
  48. package/esm2022/components/input/number/number-input/number-input.component.mjs +4 -3
  49. package/esm2022/components/input/number/number-slider-input/number-slider-input.component.mjs +6 -3
  50. package/esm2022/components/input/string/string-password-input/string-password-input.component.mjs +4 -3
  51. package/esm2022/components/table/create-dialog/create-entity-dialog.component.mjs +18 -6
  52. package/esm2022/components/table/edit-dialog/edit-entity-dialog.component.mjs +26 -13
  53. package/esm2022/components/table/table.component.mjs +14 -9
  54. package/esm2022/components/tooltip/tooltip.component.mjs +20 -0
  55. package/esm2022/decorators/array/array-decorator-internal.data.mjs +1 -1
  56. package/esm2022/decorators/array/array-decorator.data.mjs +1 -1
  57. package/esm2022/decorators/base/property-decorator-internal.data.mjs +22 -1
  58. package/esm2022/decorators/base/property-decorator.data.mjs +10 -1
  59. package/esm2022/decorators/boolean/boolean-decorator-internal.data.mjs +1 -1
  60. package/esm2022/decorators/boolean/boolean-decorator.data.mjs +1 -1
  61. package/esm2022/decorators/custom/custom-decorator-internal.data.mjs +2 -2
  62. package/esm2022/decorators/custom/custom-decorator.data.mjs +1 -1
  63. package/esm2022/decorators/date/date-decorator-internal.data.mjs +2 -1
  64. package/esm2022/decorators/date/date-decorator.data.mjs +1 -1
  65. package/esm2022/decorators/file/file-decorator-internal.data.mjs +1 -1
  66. package/esm2022/decorators/file/file-decorator.data.mjs +1 -1
  67. package/esm2022/decorators/has-many/has-many-decorator-internal.data.mjs +1 -1
  68. package/esm2022/decorators/has-many/has-many-decorator.data.mjs +1 -1
  69. package/esm2022/decorators/number/number-decorator-internal.data.mjs +1 -1
  70. package/esm2022/decorators/number/number-decorator.data.mjs +1 -1
  71. package/esm2022/decorators/object/object-decorator-internal.data.mjs +1 -1
  72. package/esm2022/decorators/object/object-decorator.data.mjs +1 -1
  73. package/esm2022/decorators/references-many/references-many-decorator-internal.data.mjs +1 -1
  74. package/esm2022/decorators/references-many/references-many-decorator.data.mjs +1 -1
  75. package/esm2022/decorators/references-one/references-one-decorator-internal.data.mjs +1 -1
  76. package/esm2022/decorators/references-one/references-one-decorator.data.mjs +1 -1
  77. package/esm2022/decorators/string/string-decorator-internal.data.mjs +1 -4
  78. package/esm2022/decorators/string/string-decorator.data.mjs +1 -1
  79. package/esm2022/directives/drag-drop.directive.mjs +62 -0
  80. package/esm2022/directives/number.directive.mjs +38 -0
  81. package/esm2022/directives/password-match.directive.mjs +30 -0
  82. package/esm2022/directives/tooltip.directive.mjs +112 -0
  83. package/esm2022/functions/get-validation-error-message.function.mjs +49 -0
  84. package/esm2022/functions/get-validation-errors-tooltip-content.function.ts.mjs +25 -0
  85. package/esm2022/public-api.mjs +6 -2
  86. package/esm2022/utilities/entity.utilities.mjs +83 -386
  87. package/esm2022/utilities/validation.utilities.mjs +455 -0
  88. package/fesm2022/ngx-material-entity.mjs +1713 -1183
  89. package/fesm2022/ngx-material-entity.mjs.map +1 -1
  90. package/functions/get-validation-errors-tooltip-content.function.ts.d.ts +10 -0
  91. package/package.json +1 -1
  92. package/public-api.d.ts +4 -1
  93. package/utilities/entity.utilities.d.ts +33 -30
  94. package/utilities/validation.utilities.d.ts +56 -0
  95. package/esm2022/components/get-validation-error-message.function.mjs +0 -42
  96. package/esm2022/components/input/file/file-input/dragDrop.directive.mjs +0 -62
  97. /package/{components/input/file/file-input/dragDrop.directive.d.ts → directives/drag-drop.directive.d.ts} +0 -0
  98. /package/{components → functions}/get-validation-error-message.function.d.ts +0 -0
@@ -4,11 +4,11 @@ import 'reflect-metadata';
4
4
  import { firstValueFrom, Observable, BehaviorSubject, first, map, Subject, takeUntil } from 'rxjs';
5
5
  import JSZip from 'jszip';
6
6
  import * as i0 from '@angular/core';
7
- import { Component, Inject, Input, EventEmitter, Output, Directive, HostListener, InjectionToken, inject, ViewChild, NgModule } from '@angular/core';
7
+ import { Component, Inject, InjectionToken, inject, EventEmitter, Directive, Output, HostListener, Input, ViewChild, NgModule, runInInjectionContext } from '@angular/core';
8
8
  import * as i1 from '@angular/material/dialog';
9
9
  import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
10
10
  import * as i3 from '@angular/forms';
11
- import { FormsModule } from '@angular/forms';
11
+ import { FormsModule, NG_VALIDATORS } from '@angular/forms';
12
12
  import * as i5 from '@angular/material/button';
13
13
  import { MatButtonModule } from '@angular/material/button';
14
14
  import * as i4 from '@angular/material/checkbox';
@@ -16,7 +16,9 @@ import { MatCheckboxModule } from '@angular/material/checkbox';
16
16
  import * as i1$1 from '@angular/common';
17
17
  import { NgIf, NgFor, CommonModule } from '@angular/common';
18
18
  import * as i2 from '@angular/common/http';
19
- import * as i9 from '@angular/material/menu';
19
+ import * as i10 from '@angular/material/badge';
20
+ import { MatBadgeModule } from '@angular/material/badge';
21
+ import * as i14 from '@angular/material/menu';
20
22
  import { MatMenuModule } from '@angular/material/menu';
21
23
  import * as i15 from '@angular/material/progress-spinner';
22
24
  import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
@@ -42,13 +44,13 @@ import * as i4$3 from '@angular/material/slide-toggle';
42
44
  import { MatSlideToggleModule } from '@angular/material/slide-toggle';
43
45
  import * as i4$4 from '@angular/material/slider';
44
46
  import { MatSliderModule } from '@angular/material/slider';
47
+ import * as i18 from '@angular/material/sort';
48
+ import { MatSort, MatSortModule } from '@angular/material/sort';
45
49
  import * as i6 from '@angular/material/table';
46
50
  import { MatTableDataSource, MatTableModule } from '@angular/material/table';
47
51
  import { SelectionModel } from '@angular/cdk/collections';
48
52
  import * as uuid from 'uuid';
49
53
  import * as i5$1 from '@angular/material/core';
50
- import { UUIDUtilities as UUIDUtilities$1 } from 'projects/ngx-material-entity/src/encapsulation/uuid.utilities';
51
- import { MatSort } from '@angular/material/sort';
52
54
  import * as i3$1 from '@angular/cdk/text-field';
53
55
  import * as js2xml from 'js2xmlparser';
54
56
 
@@ -661,7 +663,7 @@ class EntityUtilities {
661
663
  static getOmitForUpdate(entity) {
662
664
  const res = [];
663
665
  for (const key of ReflectUtilities.ownKeys(entity)) {
664
- const metadata = EntityUtilities.getPropertyMetadata(entity, key);
666
+ const metadata = this.getPropertyMetadata(entity, key);
665
667
  if (metadata.omitForUpdate) {
666
668
  res.push(key);
667
669
  }
@@ -677,7 +679,7 @@ class EntityUtilities {
677
679
  static getOmitForCreate(entity) {
678
680
  const res = [];
679
681
  for (const key of ReflectUtilities.ownKeys(entity)) {
680
- const metadata = EntityUtilities.getPropertyMetadata(entity, key);
682
+ const metadata = this.getPropertyMetadata(entity, key);
681
683
  if (metadata.omitForCreate) {
682
684
  res.push(key);
683
685
  }
@@ -691,7 +693,7 @@ class EntityUtilities {
691
693
  * @returns The reduced entity object.
692
694
  */
693
695
  static getWithoutOmitCreateValues(entity) {
694
- return LodashUtilities.omit(entity, EntityUtilities.getOmitForCreate(entity));
696
+ return LodashUtilities.omit(entity, this.getOmitForCreate(entity));
695
697
  }
696
698
  /**
697
699
  * Returns the given entity without the values that should be omitted for updating.
@@ -704,18 +706,18 @@ class EntityUtilities {
704
706
  */
705
707
  static async getWithoutOmitUpdateValues(entity, entityPriorChanges, http) {
706
708
  const res = {};
707
- for (const key of EntityUtilities.keysOf(entity, false, true)) {
708
- const metadata = EntityUtilities.getPropertyMetadata(entity, key);
709
- const type = EntityUtilities.getPropertyType(entity, key);
710
- if (!(await EntityUtilities.isEqual(entity[key], entityPriorChanges[key], metadata, type, http))) {
709
+ for (const key of this.keysOf(entity, false, true)) {
710
+ const metadata = this.getPropertyMetadata(entity, key);
711
+ const type = this.getPropertyType(entity, key);
712
+ if (!(await this.isEqual(entity[key], entityPriorChanges[key], metadata, type, http))) {
711
713
  switch (type) {
712
714
  case DecoratorTypes.OBJECT:
713
715
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
714
- res[key] = LodashUtilities.omit(entity[key], EntityUtilities.getOmitForCreate(entity[key]));
716
+ res[key] = LodashUtilities.omit(entity[key], this.getOmitForCreate(entity[key]));
715
717
  break;
716
718
  case DecoratorTypes.ARRAY:
717
719
  res[key] = entity[key]
718
- .map(value => LodashUtilities.omit(value, EntityUtilities.getOmitForCreate(value)));
720
+ .map(value => LodashUtilities.omit(value, this.getOmitForCreate(value)));
719
721
  break;
720
722
  default:
721
723
  res[key] = entity[key];
@@ -725,6 +727,20 @@ class EntityUtilities {
725
727
  }
726
728
  return res;
727
729
  }
730
+ /**
731
+ * Sets all default values on the given entity.
732
+ *
733
+ * @param entity - The entity to set the default values on.
734
+ */
735
+ static setDefaultValues(entity) {
736
+ for (const key in entity) {
737
+ const metadata = this.getPropertyMetadata(entity, key);
738
+ if (metadata.default) {
739
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any
740
+ entity[key] = metadata.default();
741
+ }
742
+ }
743
+ }
728
744
  /**
729
745
  * Gets all properties on the given entity which are files.
730
746
  *
@@ -735,9 +751,9 @@ class EntityUtilities {
735
751
  static getFileProperties(entity, omit) {
736
752
  const res = [];
737
753
  for (const key of ReflectUtilities.ownKeys(entity)) {
738
- const type = EntityUtilities.getPropertyType(entity, key);
754
+ const type = this.getPropertyType(entity, key);
739
755
  if (type === DecoratorTypes.FILE_DEFAULT || type === DecoratorTypes.FILE_IMAGE) {
740
- const metadata = EntityUtilities.getPropertyMetadata(entity, key);
756
+ const metadata = this.getPropertyMetadata(entity, key);
741
757
  if (!(metadata.omitForCreate && omit === 'create') && !(metadata.omitForUpdate && omit === 'update')) {
742
758
  res.push(key);
743
759
  }
@@ -794,12 +810,12 @@ class EntityUtilities {
794
810
  */
795
811
  static new(target, entity) {
796
812
  for (const key in target) {
797
- const type = EntityUtilities.getPropertyType(target, key);
813
+ const type = this.getPropertyType(target, key);
798
814
  let value = entity ? ReflectUtilities.get(entity, key) : undefined;
799
815
  switch (type) {
800
816
  case DecoratorTypes.OBJECT:
801
817
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
802
- const objectMetadata = EntityUtilities.getPropertyMetadata(target, key, DecoratorTypes.OBJECT);
818
+ const objectMetadata = this.getPropertyMetadata(target, key, DecoratorTypes.OBJECT);
803
819
  value = new objectMetadata.EntityClass(value);
804
820
  break;
805
821
  case DecoratorTypes.ARRAY:
@@ -807,7 +823,7 @@ class EntityUtilities {
807
823
  const resArray = [];
808
824
  if (inputArray) {
809
825
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
810
- const arrayMetadata = EntityUtilities.getPropertyMetadata(target, key, DecoratorTypes.ARRAY);
826
+ const arrayMetadata = this.getPropertyMetadata(target, key, DecoratorTypes.ARRAY);
811
827
  for (const item of inputArray) {
812
828
  const itemWithMetadata = new arrayMetadata.EntityClass(item);
813
829
  resArray.push(itemWithMetadata);
@@ -822,690 +838,373 @@ class EntityUtilities {
822
838
  }
823
839
  }
824
840
  // eslint-disable-next-line @typescript-eslint/member-ordering, jsdoc/require-jsdoc, @typescript-eslint/typedef
825
- static construct = EntityUtilities.new;
841
+ static construct = this.new;
826
842
  // eslint-disable-next-line @typescript-eslint/member-ordering, jsdoc/require-jsdoc, @typescript-eslint/typedef
827
- static build = EntityUtilities.new;
843
+ static build = this.new;
828
844
  /**
829
- * Checks if the values on an entity are valid.
830
- * Also checks all the validators given by the metadata ("required", "maxLength" etc.).
845
+ * Checks if an entity is "dirty" (if its values have changed).
831
846
  *
832
- * @param entity - The entity to validate.
833
- * @param omit - Whether to check for creating or editing validity.
834
- * @returns Whether or not the entity is valid.
847
+ * @param entity - The entity after all changes.
848
+ * @param entityPriorChanges - The entity before the changes.
849
+ * @param http - The angular HttpClient. Used to fetch files.
850
+ * @returns Whether or not the entity is dirty.
835
851
  */
836
- static isEntityValid(entity, omit) {
837
- for (const key in entity) {
838
- if (!EntityUtilities.isPropertyValid(entity, key, omit)) {
839
- return false;
840
- }
852
+ static async isDirty(entity, entityPriorChanges, http) {
853
+ if (!entityPriorChanges) {
854
+ return false;
841
855
  }
842
- return true;
856
+ const differences = await this.getDifferencesBetweenEntities(entity, entityPriorChanges, http);
857
+ return differences.length ? true : false;
843
858
  }
844
859
  /**
845
- * Checks if a single property value is valid.
860
+ * Gets the differences between the two given entities.
846
861
  *
847
- * @param entity - The entity where the property is from.
848
- * @param key - The name of the property.
849
- * @param omit - Whether to check if the given entity is valid for creation or updating.
850
- * @returns Whether or not the property value is valid.
851
- * @throws Throws when it extracts an unknown metadata type.
862
+ * @param entity - The entity as is.
863
+ * @param entityPriorChanges - The entity before any changes have been made.
864
+ * @param http - The angular http client, is needed to check if files are equal.
865
+ * @returns The differences as an array consisting of key, before and after.
852
866
  */
853
- static isPropertyValid(entity, key, omit) {
854
- const type = EntityUtilities.getPropertyType(entity, key);
855
- const metadata = EntityUtilities.getPropertyMetadata(entity, key, type);
856
- if (metadata.omitForCreate && omit === 'create') {
857
- return true;
858
- }
859
- if (metadata.omitForUpdate && omit === 'update') {
860
- return true;
861
- }
862
- if (metadata.required(entity) && type !== DecoratorTypes.HAS_MANY) {
863
- if (entity[key] == null || entity[key] === '') {
864
- return false;
867
+ static async getDifferencesBetweenEntities(entity, entityPriorChanges, http) {
868
+ const res = [];
869
+ for (const key of ReflectUtilities.ownKeys(entity)) {
870
+ const metadata = this.getPropertyMetadata(entity, key);
871
+ const type = this.getPropertyType(entity, key);
872
+ if (!(await this.isEqual(entity[key], entityPriorChanges[key], metadata, type, http))) {
873
+ res.push({
874
+ key: key,
875
+ before: entityPriorChanges[key],
876
+ after: entity[key]
877
+ });
865
878
  }
866
- }
867
- if (!metadata.required(entity)) {
868
- if (entity[key] == null || entity[key] === '') {
869
- return true;
879
+ else {
880
+ // This is needed to set blob file data so that it is only requested once.
881
+ entityPriorChanges[key] = LodashUtilities.cloneDeep(entity[key]);
870
882
  }
871
883
  }
884
+ return res;
885
+ }
886
+ // TODO Remove
887
+ /**
888
+ * Compares two Entities and returns their difference in an object.
889
+ *
890
+ * @param entity - The first entity to compare.
891
+ * @param entityPriorChanges - The second entity to compare.
892
+ * @returns The difference between the two Entities in form of a Partial.
893
+ */
894
+ // static async difference<EntityType extends BaseEntityType<EntityType>>(
895
+ // entity: EntityType,
896
+ // entityPriorChanges: EntityType
897
+ // ): Promise<Partial<EntityType>> {
898
+ // const res: Partial<EntityType> = {};
899
+ // for (const key in entity) {
900
+ // const metadata: PropertyDecoratorConfigInternal = this.getPropertyMetadata(entity, key);
901
+ // const type: DecoratorTypes = this.getPropertyType(entity, key);
902
+ // if (!(await this.isEqual(entity[key], entityPriorChanges[key], metadata, type))) {
903
+ // res[key] = entity[key];
904
+ // }
905
+ // }
906
+ // return res;
907
+ // }
908
+ /**
909
+ * Checks if two given values are equal.
910
+ * It uses the isEqual method from LodashUtilities and extends it with functionality regarding Dates.
911
+ *
912
+ * @param value - The updated value.
913
+ * @param valuePriorChanges - The value before any changes.
914
+ * @param metadata - The metadata of the property.
915
+ * @param type - The type of the property.
916
+ * @param http - The angular HttpClient. Used to fetch files.
917
+ * @returns Whether or not the given values are equal.
918
+ */
919
+ static async isEqual(value, valuePriorChanges, metadata, type, http) {
872
920
  switch (type) {
873
- case DecoratorTypes.BOOLEAN_DROPDOWN:
874
- break;
875
- case DecoratorTypes.BOOLEAN_CHECKBOX:
876
- case DecoratorTypes.BOOLEAN_TOGGLE:
877
- const entityBoolean = entity[key];
878
- const booleanMetadata = metadata;
879
- if (!EntityUtilities.isBooleanValid(entity, entityBoolean, booleanMetadata)) {
880
- return false;
881
- }
882
- break;
883
- case DecoratorTypes.STRING_DROPDOWN:
884
- break;
885
- case DecoratorTypes.STRING:
886
- case DecoratorTypes.STRING_AUTOCOMPLETE:
887
- const entityString = entity[key];
888
- const stringMetadata = metadata;
889
- if (!EntityUtilities.isStringValid(entityString, stringMetadata)) {
890
- return false;
891
- }
892
- break;
893
- case DecoratorTypes.STRING_TEXTBOX:
894
- const entityTextbox = entity[key];
895
- const textboxMetadata = metadata;
896
- if (!EntityUtilities.isTextboxValid(entityTextbox, textboxMetadata)) {
897
- return false;
898
- }
899
- break;
900
- case DecoratorTypes.STRING_PASSWORD:
901
- const entityPassword = entity[key];
902
- const passwordMetadata = metadata;
903
- const confirmPassword = ReflectUtilities.getMetadata(this.CONFIRM_PASSWORD_KEY, entity, key);
904
- if (!EntityUtilities.isPasswordValid(entityPassword, passwordMetadata, confirmPassword)) {
905
- return false;
906
- }
907
- break;
908
- case DecoratorTypes.NUMBER_DROPDOWN:
909
- return true;
910
- case DecoratorTypes.NUMBER:
911
- case DecoratorTypes.NUMBER_SLIDER:
912
- const entityNumber = entity[key];
913
- const numberMetadata = metadata;
914
- if (!EntityUtilities.isNumberValid(entityNumber, numberMetadata)) {
915
- return false;
916
- }
917
- break;
918
- case DecoratorTypes.OBJECT:
919
- const entityObject = entity[key];
920
- for (const parameterKey in entityObject) {
921
- const value = entityObject[parameterKey];
922
- if (!metadata.omit.includes(parameterKey)
923
- && !(!metadata.required(entity) && (value == null || value == ''))) {
924
- if (!EntityUtilities.isPropertyValid(entityObject, parameterKey, omit)) {
925
- return false;
926
- }
927
- }
928
- }
929
- break;
930
- case DecoratorTypes.ARRAY_STRING_CHIPS:
931
- case DecoratorTypes.ARRAY_STRING_AUTOCOMPLETE_CHIPS:
921
+ case DecoratorTypes.DATE_RANGE:
922
+ return this.isEqualDateRange(value, valuePriorChanges, metadata.filter);
923
+ case DecoratorTypes.DATE:
924
+ return this.isEqualDate(value, valuePriorChanges);
925
+ case DecoratorTypes.DATE_TIME:
926
+ return this.isEqualDateTime(value, valuePriorChanges);
932
927
  case DecoratorTypes.ARRAY_DATE:
933
928
  case DecoratorTypes.ARRAY_DATE_TIME:
929
+ return this.isEqualArrayDate(value, valuePriorChanges);
934
930
  case DecoratorTypes.ARRAY_DATE_RANGE:
935
- case DecoratorTypes.ARRAY:
936
- const entityArray = entity[key];
937
- // eslint-disable-next-line max-len
938
- const arrayMetadata = metadata;
939
- if (arrayMetadata.required(entity) && !entityArray.length) {
940
- return false;
941
- }
942
- break;
943
- case DecoratorTypes.DATE:
944
- const entityDate = new Date(entity[key]);
945
- const dateMetadata = metadata;
946
- if (!EntityUtilities.isDateValid(entityDate, dateMetadata)) {
947
- return false;
948
- }
949
- break;
950
- case DecoratorTypes.DATE_RANGE:
951
- const entityDateRange = LodashUtilities.cloneDeep(entity[key]);
952
- const dateRangeMetadata = metadata;
953
- if (!EntityUtilities.isDateRangeValid(entity, entityDateRange, dateRangeMetadata)) {
954
- return false;
955
- }
956
- break;
957
- case DecoratorTypes.DATE_TIME:
958
- const entityDateTime = new Date(entity[key]);
959
- const dateTimeMetadata = metadata;
960
- const hasTime = ReflectUtilities.hasMetadata(this.TIME_KEY, entity, key);
961
- if (!EntityUtilities.isDateTimeValid(entityDateTime, dateTimeMetadata, hasTime)) {
962
- return false;
963
- }
964
- break;
965
- case DecoratorTypes.FILE_DEFAULT:
931
+ return this.isEqualArrayDateRange(value, valuePriorChanges, metadata.filter);
932
+ case DecoratorTypes.ARRAY_STRING_CHIPS:
933
+ case DecoratorTypes.ARRAY_STRING_AUTOCOMPLETE_CHIPS:
934
+ return this.isEqualArrayString(value, valuePriorChanges);
966
935
  case DecoratorTypes.FILE_IMAGE:
967
- const entityFile = entity[key];
968
- const entityFileMetadata = metadata;
969
- if (!EntityUtilities.isFileDataValid(entityFile, entityFileMetadata)) {
970
- return false;
971
- }
972
- break;
973
- case DecoratorTypes.REFERENCES_MANY:
974
- case DecoratorTypes.REFERENCES_ONE:
975
- case DecoratorTypes.HAS_MANY:
976
- break;
936
+ case DecoratorTypes.FILE_DEFAULT:
937
+ return this.isEqualFile(value, valuePriorChanges, metadata.multiple, http);
977
938
  case DecoratorTypes.CUSTOM:
978
- // eslint-disable-next-line @typescript-eslint/no-explicit-any, max-len
979
- const customMetadata = metadata;
980
- if (!customMetadata.isValid(entity[key], omit)) {
981
- return false;
982
- }
983
- break;
939
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
940
+ return this.isEqualCustom(value, valuePriorChanges, metadata);
984
941
  default:
985
- throw new Error(`Could not validate the input because the DecoratorType ${type} is not known`);
942
+ return LodashUtilities.isEqual(value, valuePriorChanges);
986
943
  }
987
- return true;
988
944
  }
989
- static isBooleanValid(entity, value, metadata) {
990
- if (metadata.required(entity) && !value) {
945
+ static isEqualArrayString(value, valuePriorChanges) {
946
+ const stringArray = LodashUtilities.cloneDeep(value).sort();
947
+ const stringArrayPriorChanges = LodashUtilities.cloneDeep(valuePriorChanges).sort();
948
+ return LodashUtilities.isEqual(stringArray, stringArrayPriorChanges);
949
+ }
950
+ static isEqualArrayDate(value, valuePriorChanges) {
951
+ const newValue = value.map(v => new Date(v)).sort();
952
+ const newValuePriorChanges = valuePriorChanges.map(v => new Date(v)).sort();
953
+ return LodashUtilities.isEqual(newValue, newValuePriorChanges);
954
+ }
955
+ static isEqualArrayDateRange(value, valuePriorChanges, filter) {
956
+ const dateRanges = value.sort();
957
+ const dateRangesPriorChanges = valuePriorChanges.sort();
958
+ if (dateRanges.length !== dateRangesPriorChanges.length) {
991
959
  return false;
992
960
  }
961
+ for (let i = 0; i < dateRanges.length; i++) {
962
+ if (!this.isEqualDateRange(dateRanges[i], dateRangesPriorChanges[i], filter)) {
963
+ return false;
964
+ }
965
+ }
993
966
  return true;
994
967
  }
995
- static isStringValid(value, metadata) {
996
- if (metadata.maxLength && value.length > metadata.maxLength) {
997
- return false;
968
+ static isEqualDateTime(value, valuePriorChanges) {
969
+ const date = new Date(value);
970
+ const datePriorChanges = new Date(valuePriorChanges);
971
+ return LodashUtilities.isEqual(date, datePriorChanges);
972
+ }
973
+ static isEqualDate(value, valuePriorChanges) {
974
+ const date = new Date(value);
975
+ const datePriorChanges = new Date(valuePriorChanges);
976
+ date.setHours(0, 0, 0, 0);
977
+ datePriorChanges.setHours(0, 0, 0, 0);
978
+ return LodashUtilities.isEqual(date, datePriorChanges);
979
+ }
980
+ static isEqualDateRange(value, valuePriorChanges, filter) {
981
+ const dateRange = LodashUtilities.cloneDeep(value);
982
+ dateRange.start = new Date(value.start);
983
+ dateRange.end = new Date(value.end);
984
+ dateRange.values = DateUtilities.getDatesBetween(dateRange.start, dateRange.end, filter);
985
+ const dateRangePriorChanges = LodashUtilities.cloneDeep(valuePriorChanges);
986
+ dateRangePriorChanges.start = new Date(valuePriorChanges.start);
987
+ dateRangePriorChanges.end = new Date(valuePriorChanges.end);
988
+ dateRangePriorChanges.values = DateUtilities.getDatesBetween(dateRangePriorChanges.start, dateRangePriorChanges.end, filter);
989
+ return LodashUtilities.isEqual(dateRange, dateRangePriorChanges);
990
+ }
991
+ // TODO: Find a way to use blobs with jest
992
+ /* istanbul ignore next */
993
+ static async isEqualFile(value, valuePriorChanges, multiple, http) {
994
+ if (value == null) {
995
+ if (valuePriorChanges == null) {
996
+ return true;
997
+ }
998
+ else {
999
+ return false;
1000
+ }
998
1001
  }
999
- if (metadata.minLength && value.length < metadata.minLength) {
1002
+ const files = multiple ? value.sort() : [value].sort();
1003
+ const filesPriorChanges = multiple ? valuePriorChanges.sort() : [valuePriorChanges].sort();
1004
+ if (files.length !== filesPriorChanges.length) {
1000
1005
  return false;
1001
1006
  }
1002
- if (metadata.regex && !value.match(metadata.regex)) {
1003
- return false;
1007
+ for (let i = 0; i < files.length; i++) {
1008
+ // checks this before actually getting any files due to performance reasons.
1009
+ if (!LodashUtilities.isEqual(LodashUtilities.omit(files[i], 'file'), LodashUtilities.omit(filesPriorChanges[i], 'file'))) {
1010
+ return false;
1011
+ }
1012
+ if (filesPriorChanges[i].file && !files[i].file) {
1013
+ files[i] = await FileUtilities.getFileData(files[i], http);
1014
+ value = files[i];
1015
+ }
1016
+ if (files[i].file && !filesPriorChanges[i].file) {
1017
+ filesPriorChanges[i] = await FileUtilities.getFileData(filesPriorChanges[i], http);
1018
+ valuePriorChanges = filesPriorChanges[i];
1019
+ }
1020
+ if (!LodashUtilities.isEqual(await files[i].file?.text(), await filesPriorChanges[i].file?.text())) {
1021
+ return false;
1022
+ }
1004
1023
  }
1005
1024
  return true;
1006
1025
  }
1007
- static isTextboxValid(value, metadata) {
1008
- if (metadata.maxLength && value.length > metadata.maxLength) {
1009
- return false;
1010
- }
1011
- if (metadata.minLength && value.length < metadata.minLength) {
1012
- return false;
1013
- }
1014
- return true;
1015
- }
1016
- static isPasswordValid(value, metadata, confirmPassword) {
1017
- if (value !== confirmPassword) {
1018
- return false;
1019
- }
1020
- if (metadata.maxLength && value.length > metadata.maxLength) {
1021
- return false;
1022
- }
1023
- if (metadata.minLength && value.length < metadata.minLength) {
1024
- return false;
1025
- }
1026
- if (metadata.regex && !value.match(metadata.regex)) {
1026
+ static isEqualCustom(value, valuePriorChanges,
1027
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1028
+ metadata) {
1029
+ if (!metadata.isEqual(value, valuePriorChanges, metadata)) {
1027
1030
  return false;
1028
1031
  }
1029
1032
  return true;
1030
1033
  }
1031
- static isNumberValid(value, metadata) {
1032
- if (metadata.max && value > metadata.max) {
1033
- return false;
1034
+ /**
1035
+ * Compare function for sorting entity keys by their order value.
1036
+ *
1037
+ * @param a - First key of entity.
1038
+ * @param b - Second key of entity.
1039
+ * @param entity - Current entity (used to get metadata of entity keys).
1040
+ * @returns 0 if both values have the same order, a negative value if 'a' comes before 'b', a positive value if 'a' comes behind 'b'.
1041
+ */
1042
+ static compareOrder(a, b, entity) {
1043
+ const metadataA = this.getPropertyMetadata(entity, a);
1044
+ const metadataB = this.getPropertyMetadata(entity, b);
1045
+ if (metadataA.position.order === -1) {
1046
+ if (metadataB.position.order === -1) {
1047
+ return 0;
1048
+ }
1049
+ return 1;
1034
1050
  }
1035
- if (metadata.min && value < metadata.min) {
1036
- return false;
1051
+ else if (metadataB.position.order === -1) {
1052
+ return -1;
1037
1053
  }
1038
- return true;
1054
+ return metadataA.position.order - metadataB.position.order;
1039
1055
  }
1040
- static isDateValid(value, metadata) {
1041
- if (metadata.min && value.getTime() < metadata.min(value).getTime()) {
1042
- return false;
1043
- }
1044
- if (metadata.max && value.getTime() > metadata.max(value).getTime()) {
1045
- return false;
1046
- }
1047
- if (metadata.filter && !metadata.filter(value)) {
1048
- return false;
1056
+ /**
1057
+ * Gets the bootstrap column values for "lg", "md", "sm".
1058
+ *
1059
+ * @param entity - Entity to get the bootstrap column values of the key.
1060
+ * @param key - Key of the property to get bootstrap column values from.
1061
+ * @param type - Defines for which screen size the column values should be returned.
1062
+ * @returns Bootstrap column value.
1063
+ */
1064
+ static getWidth(entity, key, type) {
1065
+ const metadata = this.getPropertyMetadata(entity, key);
1066
+ switch (type) {
1067
+ case 'lg':
1068
+ return metadata.defaultWidths[0];
1069
+ case 'md':
1070
+ return metadata.defaultWidths[1];
1071
+ case 'sm':
1072
+ return metadata.defaultWidths[2];
1049
1073
  }
1050
- return true;
1051
1074
  }
1052
- static isDateRangeValid(entity, value, metadata) {
1053
- if (metadata.required(entity)) {
1054
- if (!value.start) {
1055
- return false;
1056
- }
1057
- if (!value.end) {
1058
- return false;
1059
- }
1060
- }
1061
- value.start = new Date(value.start);
1062
- value.end = new Date(value.end);
1063
- if (metadata.minStart && value.start.getTime() < metadata.minStart(value.start).getTime()) {
1064
- return false;
1065
- }
1066
- if (metadata.maxStart && value.start.getTime() > metadata.maxStart(value.start).getTime()) {
1067
- return false;
1068
- }
1069
- if (metadata.minEnd && value.end.getTime() < metadata.minEnd(value.end).getTime()) {
1070
- return false;
1071
- }
1072
- if (metadata.maxEnd && value.end.getTime() > metadata.maxEnd(value.end).getTime()) {
1073
- return false;
1074
- }
1075
- if (metadata.filter) {
1076
- if (!metadata.filter(value.start)) {
1077
- return false;
1078
- }
1079
- if (!metadata.filter(value.end)) {
1080
- return false;
1081
- }
1082
- if (value.values) {
1083
- for (const date of value.values) {
1084
- if (!metadata.filter(date)) {
1085
- return false;
1075
+ /**
1076
+ * Resets all changes on an entity.
1077
+ *
1078
+ * @param entity - The entity to reset.
1079
+ * @param entityPriorChanges - The entity before any changes.
1080
+ */
1081
+ static resetChangesOnEntity(entity, entityPriorChanges) {
1082
+ for (const key in entityPriorChanges) {
1083
+ ReflectUtilities.set(entity, key, ReflectUtilities.get(entityPriorChanges, key));
1084
+ if (ReflectUtilities.hasMetadata(this.METADATA_KEYS_TO_RESET_KEY, entity, key)) {
1085
+ for (const k of ReflectUtilities.getMetadata(this.METADATA_KEYS_TO_RESET_KEY, entity, key)) {
1086
+ if (ReflectUtilities.hasMetadata(k, entity, key)) {
1087
+ ReflectUtilities.defineMetadata(k, undefined, entity, key);
1086
1088
  }
1087
1089
  }
1088
1090
  }
1089
1091
  }
1090
- return true;
1091
1092
  }
1092
- static isDateTimeValid(value, metadata, hasTime) {
1093
- if (!hasTime) {
1094
- return false;
1095
- }
1096
- if (metadata.minDate && value.getTime() < metadata.minDate(value).getTime()) {
1097
- return false;
1098
- }
1099
- if (metadata.maxDate && value.getTime() > metadata.maxDate(value).getTime()) {
1100
- return false;
1101
- }
1102
- if (metadata.filterDate && !metadata.filterDate(value)) {
1103
- return false;
1093
+ static getEntityRows(entity, tab, hideOmitForCreate, hideOmitForEdit, additionalOmitValues) {
1094
+ const res = [];
1095
+ const keys = this.keysOf(entity, hideOmitForCreate, hideOmitForEdit)
1096
+ .filter(k => !additionalOmitValues.includes(k));
1097
+ const numberOfRows = this.getNumberOfRows(keys, entity, tab);
1098
+ for (let i = 1; i <= numberOfRows; i++) {
1099
+ const row = {
1100
+ row: i,
1101
+ keys: this.getKeysForRow(keys, entity, i, tab)
1102
+ };
1103
+ res.push(row);
1104
1104
  }
1105
- const time = {
1106
- hours: value.getHours(),
1107
- minutes: value.getMinutes()
1108
- };
1109
- if (metadata.minTime) {
1110
- const minTime = metadata.minTime(value);
1111
- if (!(time.hours > minTime.hours
1112
- || (time.hours === minTime.hours
1113
- && time.minutes >= minTime.minutes))) {
1114
- return false;
1115
- }
1105
+ if (this.getKeysForRow(keys, entity, -1, tab).length) {
1106
+ const lastRow = {
1107
+ row: numberOfRows + 1,
1108
+ keys: this.getKeysForRow(keys, entity, -1, tab)
1109
+ };
1110
+ res.push(lastRow);
1116
1111
  }
1117
- if (metadata.maxTime) {
1118
- const maxTime = metadata.maxTime(value);
1119
- if (!(time.hours < maxTime.hours
1120
- || (time.hours === maxTime.hours
1121
- && time.minutes <= maxTime.minutes))) {
1122
- return false;
1123
- }
1112
+ return res;
1113
+ }
1114
+ /**
1115
+ * Gets the tabs that are used to display the given entity.
1116
+ *
1117
+ * @param entity - The entity to get the rows from.
1118
+ * @param hideOmitForCreate - Whether or not keys with the metadata omitForCreate should be filtered out.
1119
+ * @param hideOmitForEdit - Whether or not keys with the metadata omitForUpdate should be filtered out.
1120
+ * @param additionalOmitValues - Additional omit values.
1121
+ * @returns The sorted Tabs containing the rows and the keys to display in that row.
1122
+ */
1123
+ static getEntityTabs(entity, hideOmitForCreate = false, hideOmitForEdit = false, additionalOmitValues = []) {
1124
+ const res = [];
1125
+ const keys = this.keysOf(entity, hideOmitForCreate, hideOmitForEdit)
1126
+ .filter(k => !additionalOmitValues.includes(k));
1127
+ const numberOfTabs = this.getNumberOfTabs(keys, entity);
1128
+ // eslint-disable-next-line max-len
1129
+ const firstTabRows = this.getEntityRows(entity, -1, hideOmitForCreate, hideOmitForEdit, additionalOmitValues);
1130
+ if (firstTabRows.length) {
1131
+ const firstTab = {
1132
+ tabName: this.getFirstTabName(entity),
1133
+ tab: -1,
1134
+ rows: firstTabRows
1135
+ };
1136
+ res.push(firstTab);
1124
1137
  }
1125
- if (metadata.filterTime) {
1126
- if (!metadata.filterTime(time)) {
1127
- return false;
1138
+ for (let i = 2; i <= numberOfTabs; i++) {
1139
+ const rows = this.getEntityRows(entity, i, hideOmitForCreate, hideOmitForEdit, additionalOmitValues);
1140
+ if (rows.length) {
1141
+ const tab = {
1142
+ tabName: this.getTabName(entity, i),
1143
+ tab: i,
1144
+ rows: rows
1145
+ };
1146
+ res.push(tab);
1128
1147
  }
1129
1148
  }
1130
- return true;
1149
+ return res;
1131
1150
  }
1132
- static isFileDataValid(value, metadata) {
1133
- const files = metadata.multiple ? value : [value];
1134
- let fileSizeTotal = 0;
1135
- for (const file of files) {
1136
- if (!file.name || !file.file && !file.url) {
1137
- return false;
1138
- }
1139
- if (!FileUtilities.isMimeTypeValid(file.type, metadata.allowedMimeTypes)) {
1140
- return false;
1141
- }
1142
- if (FileUtilities.transformToMegaBytes(file.size, 'B') > metadata.maxSize) {
1143
- return false;
1144
- }
1145
- fileSizeTotal += file.size;
1146
- if (FileUtilities.transformToMegaBytes(fileSizeTotal, 'B') > metadata.maxSizeTotal) {
1147
- return false;
1148
- }
1149
- }
1150
- return true;
1151
+ static getKeysForRow(keys, entity, row, tab) {
1152
+ return keys
1153
+ .filter(k => this.getPropertyMetadata(entity, k).position.row === row)
1154
+ .filter(k => this.getPropertyMetadata(entity, k).position.tab === tab)
1155
+ .sort((a, b) => this.compareOrder(a, b, entity));
1156
+ }
1157
+ static getNumberOfRows(keys, entity, tab) {
1158
+ return keys
1159
+ .filter(k => this.getPropertyMetadata(entity, k).position.tab === tab)
1160
+ .map(k => this.getPropertyMetadata(entity, k).position.row)
1161
+ .sort((a, b) => (a > b ? -1 : 1))[0];
1162
+ }
1163
+ static getNumberOfTabs(keys, entity) {
1164
+ return keys
1165
+ .map(k => this.getPropertyMetadata(entity, k).position.tab)
1166
+ .sort((a, b) => (a > b ? -1 : 1))[0];
1167
+ }
1168
+ static getTabName(entity, tab) {
1169
+ const providedTabName = ReflectUtilities.ownKeys(entity)
1170
+ .map(k => this.getPropertyMetadata(entity, k))
1171
+ .find(m => m.position.tab === tab && m.position.tabName)?.position.tabName;
1172
+ return providedTabName ?? `Tab ${tab}`;
1173
+ }
1174
+ static getFirstTabName(entity) {
1175
+ const providedTabName = ReflectUtilities.ownKeys(entity)
1176
+ .map(k => this.getPropertyMetadata(entity, k))
1177
+ .find(m => m.position.tabName && m.position.tab === -1)?.position.tabName;
1178
+ return providedTabName ?? 'Tab 1';
1151
1179
  }
1152
1180
  /**
1153
- * Checks if an entity is "dirty" (if its values have changed).
1181
+ * Gets the keys of the provided entity correctly typed.
1154
1182
  *
1155
- * @param entity - The entity after all changes.
1156
- * @param entityPriorChanges - The entity before the changes.
1157
- * @param http - The angular HttpClient. Used to fetch files.
1158
- * @returns Whether or not the entity is dirty.
1183
+ * @param entity - The entity to get the keys of.
1184
+ * @param hideOmitForCreate - Whether or not keys with the metadata omitForCreate should be filtered out.
1185
+ * @param hideOmitForEdit - Whether or not keys with the metadata omitForUpdate should be filtered out.
1186
+ * @returns An array of keys of the entity.
1159
1187
  */
1160
- static async isDirty(entity, entityPriorChanges, http) {
1161
- if (!entityPriorChanges) {
1162
- return false;
1188
+ static keysOf(entity, hideOmitForCreate = false, hideOmitForEdit = false) {
1189
+ let keys = ReflectUtilities.ownKeys(entity);
1190
+ const dontDisplayKeys = this.getDontDisplayKeys(entity);
1191
+ keys = keys.filter(k => !dontDisplayKeys.includes(k));
1192
+ if (hideOmitForCreate) {
1193
+ const omitForCreateKeys = this.getOmitForCreate(entity);
1194
+ keys = keys.filter(k => !omitForCreateKeys.includes(k));
1163
1195
  }
1164
- const differences = await EntityUtilities.differencesForDirty(entity, entityPriorChanges, http);
1165
- return differences.length ? true : false;
1196
+ if (hideOmitForEdit) {
1197
+ const omitForUpdateKeys = this.getOmitForUpdate(entity);
1198
+ keys = keys.filter(k => !omitForUpdateKeys.includes(k));
1199
+ }
1200
+ return keys;
1166
1201
  }
1167
- static async differencesForDirty(entity, entityPriorChanges, http) {
1202
+ static getDontDisplayKeys(entity) {
1168
1203
  const res = [];
1169
1204
  for (const key of ReflectUtilities.ownKeys(entity)) {
1170
- const metadata = EntityUtilities.getPropertyMetadata(entity, key);
1171
- const type = EntityUtilities.getPropertyType(entity, key);
1172
- if (!(await EntityUtilities.isEqual(entity[key], entityPriorChanges[key], metadata, type, http))) {
1173
- res.push({
1174
- key: key,
1175
- before: entityPriorChanges[key],
1176
- after: entity[key]
1177
- });
1178
- }
1179
- else {
1180
- // This is needed to set blob file data so that it is only requested once.
1181
- entityPriorChanges[key] = LodashUtilities.cloneDeep(entity[key]);
1182
- }
1183
- }
1184
- return res;
1185
- }
1186
- // TODO Remove
1187
- /**
1188
- * Compares two Entities and returns their difference in an object.
1189
- *
1190
- * @param entity - The first entity to compare.
1191
- * @param entityPriorChanges - The second entity to compare.
1192
- * @returns The difference between the two Entities in form of a Partial.
1193
- */
1194
- // static async difference<EntityType extends BaseEntityType<EntityType>>(
1195
- // entity: EntityType,
1196
- // entityPriorChanges: EntityType
1197
- // ): Promise<Partial<EntityType>> {
1198
- // const res: Partial<EntityType> = {};
1199
- // for (const key in entity) {
1200
- // const metadata: PropertyDecoratorConfigInternal = EntityUtilities.getPropertyMetadata(entity, key);
1201
- // const type: DecoratorTypes = EntityUtilities.getPropertyType(entity, key);
1202
- // if (!(await EntityUtilities.isEqual(entity[key], entityPriorChanges[key], metadata, type))) {
1203
- // res[key] = entity[key];
1204
- // }
1205
- // }
1206
- // return res;
1207
- // }
1208
- /**
1209
- * Checks if two given values are equal.
1210
- * It uses the isEqual method from LodashUtilities and extends it with functionality regarding Dates.
1211
- *
1212
- * @param value - The updated value.
1213
- * @param valuePriorChanges - The value before any changes.
1214
- * @param metadata - The metadata of the property.
1215
- * @param type - The type of the property.
1216
- * @param http - The angular HttpClient. Used to fetch files.
1217
- * @returns Whether or not the given values are equal.
1218
- */
1219
- static async isEqual(value, valuePriorChanges, metadata, type, http) {
1220
- switch (type) {
1221
- case DecoratorTypes.DATE_RANGE:
1222
- return EntityUtilities.isEqualDateRange(value, valuePriorChanges, metadata.filter);
1223
- case DecoratorTypes.DATE:
1224
- return EntityUtilities.isEqualDate(value, valuePriorChanges);
1225
- case DecoratorTypes.DATE_TIME:
1226
- return EntityUtilities.isEqualDateTime(value, valuePriorChanges);
1227
- case DecoratorTypes.ARRAY_DATE:
1228
- case DecoratorTypes.ARRAY_DATE_TIME:
1229
- return EntityUtilities.isEqualArrayDate(value, valuePriorChanges);
1230
- case DecoratorTypes.ARRAY_DATE_RANGE:
1231
- return EntityUtilities.isEqualArrayDateRange(value, valuePriorChanges, metadata.filter);
1232
- case DecoratorTypes.ARRAY_STRING_CHIPS:
1233
- case DecoratorTypes.ARRAY_STRING_AUTOCOMPLETE_CHIPS:
1234
- return EntityUtilities.isEqualArrayString(value, valuePriorChanges);
1235
- case DecoratorTypes.FILE_IMAGE:
1236
- case DecoratorTypes.FILE_DEFAULT:
1237
- // eslint-disable-next-line max-len
1238
- return EntityUtilities.isEqualFile(value, valuePriorChanges, metadata.multiple, http);
1239
- case DecoratorTypes.CUSTOM:
1240
- // eslint-disable-next-line max-len, @typescript-eslint/no-explicit-any
1241
- return EntityUtilities.isEqualCustom(value, valuePriorChanges, metadata);
1242
- default:
1243
- return LodashUtilities.isEqual(value, valuePriorChanges);
1244
- }
1245
- }
1246
- static isEqualArrayString(value, valuePriorChanges) {
1247
- const stringArray = LodashUtilities.cloneDeep(value).sort();
1248
- const stringArrayPriorChanges = LodashUtilities.cloneDeep(valuePriorChanges).sort();
1249
- return LodashUtilities.isEqual(stringArray, stringArrayPriorChanges);
1250
- }
1251
- static isEqualArrayDate(value, valuePriorChanges) {
1252
- const newValue = value.map(v => new Date(v)).sort();
1253
- const newValuePriorChanges = valuePriorChanges.map(v => new Date(v)).sort();
1254
- return LodashUtilities.isEqual(newValue, newValuePriorChanges);
1255
- }
1256
- static isEqualArrayDateRange(value, valuePriorChanges, filter) {
1257
- const dateRanges = value.sort();
1258
- const dateRangesPriorChanges = valuePriorChanges.sort();
1259
- if (dateRanges.length !== dateRangesPriorChanges.length) {
1260
- return false;
1261
- }
1262
- for (let i = 0; i < dateRanges.length; i++) {
1263
- if (!EntityUtilities.isEqualDateRange(dateRanges[i], dateRangesPriorChanges[i], filter)) {
1264
- return false;
1265
- }
1266
- }
1267
- return true;
1268
- }
1269
- static isEqualDateTime(value, valuePriorChanges) {
1270
- const date = new Date(value);
1271
- const datePriorChanges = new Date(valuePriorChanges);
1272
- return LodashUtilities.isEqual(date, datePriorChanges);
1273
- }
1274
- static isEqualDate(value, valuePriorChanges) {
1275
- const date = new Date(value);
1276
- const datePriorChanges = new Date(valuePriorChanges);
1277
- date.setHours(0, 0, 0, 0);
1278
- datePriorChanges.setHours(0, 0, 0, 0);
1279
- return LodashUtilities.isEqual(date, datePriorChanges);
1280
- }
1281
- static isEqualDateRange(value, valuePriorChanges, filter) {
1282
- const dateRange = LodashUtilities.cloneDeep(value);
1283
- dateRange.start = new Date(value.start);
1284
- dateRange.end = new Date(value.end);
1285
- dateRange.values = DateUtilities.getDatesBetween(dateRange.start, dateRange.end, filter);
1286
- const dateRangePriorChanges = LodashUtilities.cloneDeep(valuePriorChanges);
1287
- dateRangePriorChanges.start = new Date(valuePriorChanges.start);
1288
- dateRangePriorChanges.end = new Date(valuePriorChanges.end);
1289
- dateRangePriorChanges.values = DateUtilities.getDatesBetween(dateRangePriorChanges.start, dateRangePriorChanges.end, filter);
1290
- return LodashUtilities.isEqual(dateRange, dateRangePriorChanges);
1291
- }
1292
- // TODO: Find a way to use blobs with jest
1293
- /* istanbul ignore next */
1294
- static async isEqualFile(value, valuePriorChanges, multiple, http) {
1295
- if (value == null) {
1296
- if (valuePriorChanges == null) {
1297
- return true;
1298
- }
1299
- else {
1300
- return false;
1301
- }
1302
- }
1303
- const files = multiple ? value.sort() : [value].sort();
1304
- const filesPriorChanges = multiple ? valuePriorChanges.sort() : [valuePriorChanges].sort();
1305
- if (files.length !== filesPriorChanges.length) {
1306
- return false;
1307
- }
1308
- for (let i = 0; i < files.length; i++) {
1309
- // checks this before actually getting any files due to performance reasons.
1310
- if (!LodashUtilities.isEqual(LodashUtilities.omit(files[i], 'file'), LodashUtilities.omit(filesPriorChanges[i], 'file'))) {
1311
- return false;
1312
- }
1313
- if (filesPriorChanges[i].file && !files[i].file) {
1314
- files[i] = await FileUtilities.getFileData(files[i], http);
1315
- value = files[i];
1316
- }
1317
- if (files[i].file && !filesPriorChanges[i].file) {
1318
- filesPriorChanges[i] = await FileUtilities.getFileData(filesPriorChanges[i], http);
1319
- valuePriorChanges = filesPriorChanges[i];
1320
- }
1321
- if (!LodashUtilities.isEqual(await files[i].file?.text(), await filesPriorChanges[i].file?.text())) {
1322
- return false;
1323
- }
1324
- }
1325
- return true;
1326
- }
1327
- static isEqualCustom(value, valuePriorChanges,
1328
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1329
- metadata) {
1330
- if (!metadata.isEqual(value, valuePriorChanges, metadata)) {
1331
- return false;
1332
- }
1333
- return true;
1334
- }
1335
- /**
1336
- * Compare function for sorting entity keys by their order value.
1337
- *
1338
- * @param a - First key of entity.
1339
- * @param b - Second key of entity.
1340
- * @param entity - Current entity (used to get metadata of entity keys).
1341
- * @returns 0 if both values have the same order, a negative value if 'a' comes before 'b', a positive value if 'a' comes behind 'b'.
1342
- */
1343
- static compareOrder(a, b, entity) {
1344
- const metadataA = EntityUtilities.getPropertyMetadata(entity, a);
1345
- const metadataB = EntityUtilities.getPropertyMetadata(entity, b);
1346
- if (metadataA.position.order === -1) {
1347
- if (metadataB.position.order === -1) {
1348
- return 0;
1349
- }
1350
- return 1;
1351
- }
1352
- else if (metadataB.position.order === -1) {
1353
- return -1;
1354
- }
1355
- return metadataA.position.order - metadataB.position.order;
1356
- }
1357
- /**
1358
- * Gets the bootstrap column values for "lg", "md", "sm".
1359
- *
1360
- * @param entity - Entity to get the bootstrap column values of the key.
1361
- * @param key - Key of the property to get bootstrap column values from.
1362
- * @param type - Defines for which screen size the column values should be returned.
1363
- * @returns Bootstrap column value.
1364
- */
1365
- static getWidth(entity, key, type) {
1366
- const metadata = EntityUtilities.getPropertyMetadata(entity, key);
1367
- switch (type) {
1368
- case 'lg':
1369
- return metadata.defaultWidths[0];
1370
- case 'md':
1371
- return metadata.defaultWidths[1];
1372
- case 'sm':
1373
- return metadata.defaultWidths[2];
1374
- }
1375
- }
1376
- /**
1377
- * Resets all changes on an entity.
1378
- *
1379
- * @param entity - The entity to reset.
1380
- * @param entityPriorChanges - The entity before any changes.
1381
- */
1382
- static resetChangesOnEntity(entity, entityPriorChanges) {
1383
- for (const key in entityPriorChanges) {
1384
- ReflectUtilities.set(entity, key, ReflectUtilities.get(entityPriorChanges, key));
1385
- if (ReflectUtilities.hasMetadata(this.METADATA_KEYS_TO_RESET_KEY, entity, key)) {
1386
- for (const k of ReflectUtilities.getMetadata(this.METADATA_KEYS_TO_RESET_KEY, entity, key)) {
1387
- if (ReflectUtilities.hasMetadata(k, entity, key)) {
1388
- ReflectUtilities.defineMetadata(k, undefined, entity, key);
1389
- }
1390
- }
1391
- }
1392
- }
1393
- }
1394
- static getEntityRows(entity, tab, hideOmitForCreate, hideOmitForEdit, additionalOmitValues) {
1395
- const res = [];
1396
- const keys = EntityUtilities.keysOf(entity, hideOmitForCreate, hideOmitForEdit)
1397
- .filter(k => !additionalOmitValues.includes(k));
1398
- const numberOfRows = EntityUtilities.getNumberOfRows(keys, entity, tab);
1399
- for (let i = 1; i <= numberOfRows; i++) {
1400
- const row = {
1401
- row: i,
1402
- keys: EntityUtilities.getKeysForRow(keys, entity, i, tab)
1403
- };
1404
- res.push(row);
1405
- }
1406
- if (EntityUtilities.getKeysForRow(keys, entity, -1, tab).length) {
1407
- const lastRow = {
1408
- row: numberOfRows + 1,
1409
- keys: EntityUtilities.getKeysForRow(keys, entity, -1, tab)
1410
- };
1411
- res.push(lastRow);
1412
- }
1413
- return res;
1414
- }
1415
- /**
1416
- * Gets the tabs that are used to display the given entity.
1417
- *
1418
- * @param entity - The entity to get the rows from.
1419
- * @param hideOmitForCreate - Whether or not keys with the metadata omitForCreate should be filtered out.
1420
- * @param hideOmitForEdit - Whether or not keys with the metadata omitForUpdate should be filtered out.
1421
- * @param additionalOmitValues - Additional omit values.
1422
- * @returns The sorted Tabs containing the rows and the keys to display in that row.
1423
- */
1424
- static getEntityTabs(entity, hideOmitForCreate = false, hideOmitForEdit = false, additionalOmitValues = []) {
1425
- const res = [];
1426
- const keys = EntityUtilities.keysOf(entity, hideOmitForCreate, hideOmitForEdit)
1427
- .filter(k => !additionalOmitValues.includes(k));
1428
- const numberOfTabs = EntityUtilities.getNumberOfTabs(keys, entity);
1429
- // eslint-disable-next-line max-len
1430
- const firstTabRows = EntityUtilities.getEntityRows(entity, -1, hideOmitForCreate, hideOmitForEdit, additionalOmitValues);
1431
- if (firstTabRows.length) {
1432
- const firstTab = {
1433
- tabName: EntityUtilities.getFirstTabName(entity),
1434
- tab: -1,
1435
- rows: firstTabRows
1436
- };
1437
- res.push(firstTab);
1438
- }
1439
- for (let i = 2; i <= numberOfTabs; i++) {
1440
- const rows = EntityUtilities.getEntityRows(entity, i, hideOmitForCreate, hideOmitForEdit, additionalOmitValues);
1441
- if (rows.length) {
1442
- const tab = {
1443
- tabName: EntityUtilities.getTabName(entity, i),
1444
- tab: i,
1445
- rows: rows
1446
- };
1447
- res.push(tab);
1448
- }
1449
- }
1450
- return res;
1451
- }
1452
- static getKeysForRow(keys, entity, row, tab) {
1453
- return keys
1454
- .filter(k => EntityUtilities.getPropertyMetadata(entity, k).position.row === row)
1455
- .filter(k => EntityUtilities.getPropertyMetadata(entity, k).position.tab === tab)
1456
- .sort((a, b) => EntityUtilities.compareOrder(a, b, entity));
1457
- }
1458
- static getNumberOfRows(keys, entity, tab) {
1459
- return keys
1460
- .filter(k => EntityUtilities.getPropertyMetadata(entity, k).position.tab === tab)
1461
- .map(k => EntityUtilities.getPropertyMetadata(entity, k).position.row)
1462
- .sort((a, b) => (a > b ? -1 : 1))[0];
1463
- }
1464
- static getNumberOfTabs(keys, entity) {
1465
- return keys
1466
- .map(k => EntityUtilities.getPropertyMetadata(entity, k).position.tab)
1467
- .sort((a, b) => (a > b ? -1 : 1))[0];
1468
- }
1469
- static getTabName(entity, tab) {
1470
- const providedTabName = ReflectUtilities.ownKeys(entity)
1471
- .map(k => EntityUtilities.getPropertyMetadata(entity, k))
1472
- .find(m => m.position.tab === tab && m.position.tabName)?.position.tabName;
1473
- return providedTabName ?? `Tab ${tab}`;
1474
- }
1475
- static getFirstTabName(entity) {
1476
- const providedTabName = ReflectUtilities.ownKeys(entity)
1477
- .map(k => EntityUtilities.getPropertyMetadata(entity, k))
1478
- .find(m => m.position.tabName && m.position.tab === -1)?.position.tabName;
1479
- return providedTabName ?? 'Tab 1';
1480
- }
1481
- /**
1482
- * Gets the keys of the provided entity correctly typed.
1483
- *
1484
- * @param entity - The entity to get the keys of.
1485
- * @param hideOmitForCreate - Whether or not keys with the metadata omitForCreate should be filtered out.
1486
- * @param hideOmitForEdit - Whether or not keys with the metadata omitForUpdate should be filtered out.
1487
- * @returns An array of keys of the entity.
1488
- */
1489
- static keysOf(entity, hideOmitForCreate = false, hideOmitForEdit = false) {
1490
- let keys = ReflectUtilities.ownKeys(entity);
1491
- const dontDisplayKeys = EntityUtilities.getDontDisplayKeys(entity);
1492
- keys = keys.filter(k => !dontDisplayKeys.includes(k));
1493
- if (hideOmitForCreate) {
1494
- const omitForCreateKeys = EntityUtilities.getOmitForCreate(entity);
1495
- keys = keys.filter(k => !omitForCreateKeys.includes(k));
1496
- }
1497
- if (hideOmitForEdit) {
1498
- const omitForUpdateKeys = EntityUtilities.getOmitForUpdate(entity);
1499
- keys = keys.filter(k => !omitForUpdateKeys.includes(k));
1500
- }
1501
- return keys;
1502
- }
1503
- static getDontDisplayKeys(entity) {
1504
- const res = [];
1505
- for (const key of ReflectUtilities.ownKeys(entity)) {
1506
- const metadata = EntityUtilities.getPropertyMetadata(entity, key);
1507
- if (!metadata.display(entity)) {
1508
- res.push(key);
1205
+ const metadata = this.getPropertyMetadata(entity, key);
1206
+ if (!metadata.display(entity)) {
1207
+ res.push(key);
1509
1208
  }
1510
1209
  }
1511
1210
  return res;
@@ -1595,6 +1294,10 @@ class PropertyDecoratorConfigInternal {
1595
1294
  position;
1596
1295
  // eslint-disable-next-line jsdoc/require-jsdoc
1597
1296
  isReadOnly;
1297
+ // eslint-disable-next-line jsdoc/require-jsdoc
1298
+ default;
1299
+ // eslint-disable-next-line jsdoc/require-jsdoc
1300
+ change;
1598
1301
  constructor(data) {
1599
1302
  this.display = this.booleanToFunction(data.display ?? true);
1600
1303
  this.displayName = data.displayName;
@@ -1604,6 +1307,23 @@ class PropertyDecoratorConfigInternal {
1604
1307
  this.defaultWidths = data.defaultWidths ?? [6, 6, 12];
1605
1308
  this.position = new PositionInternal(data.position);
1606
1309
  this.isReadOnly = this.booleanToFunction(data.isReadOnly ?? false);
1310
+ this.default = this.defaultToFunction(data.default);
1311
+ this.change = data.change;
1312
+ }
1313
+ /**
1314
+ * Converts the default value to a function or undefined.
1315
+ *
1316
+ * @param value - The default value provided by the metadata.
1317
+ * @returns A function that returns a default value or undefined.
1318
+ */
1319
+ defaultToFunction(value) {
1320
+ if (value == null) {
1321
+ return undefined;
1322
+ }
1323
+ if (typeof value == 'function') {
1324
+ return value;
1325
+ }
1326
+ return (() => value);
1607
1327
  }
1608
1328
  /**
1609
1329
  * Converts the given boolean or boolean function to a boolean function.
@@ -1702,520 +1422,1113 @@ class AutocompleteStringDecoratorConfigInternal extends PropertyDecoratorConfigI
1702
1422
  }
1703
1423
  }
1704
1424
  /**
1705
- * The internal PasswordStringDecoratorConfig. Sets default values.
1425
+ * The internal PasswordStringDecoratorConfig. Sets default values.
1426
+ */
1427
+ class PasswordStringDecoratorConfigInternal extends PropertyDecoratorConfigInternal {
1428
+ // eslint-disable-next-line jsdoc/require-jsdoc
1429
+ displayStyle;
1430
+ // eslint-disable-next-line jsdoc/require-jsdoc
1431
+ minLength;
1432
+ // eslint-disable-next-line jsdoc/require-jsdoc
1433
+ maxLength;
1434
+ // eslint-disable-next-line jsdoc/require-jsdoc
1435
+ regex;
1436
+ // eslint-disable-next-line jsdoc/require-jsdoc
1437
+ needsConfirmation;
1438
+ // eslint-disable-next-line jsdoc/require-jsdoc
1439
+ confirmationDisplayName;
1440
+ constructor(data) {
1441
+ super(data);
1442
+ this.displayStyle = data.displayStyle;
1443
+ this.minLength = data.minLength;
1444
+ this.maxLength = data.maxLength;
1445
+ this.regex = data.regex;
1446
+ this.needsConfirmation = data.needsConfirmation ?? true;
1447
+ this.confirmationDisplayName = data.confirmationDisplayName ?? 'Confirm Password';
1448
+ this.defaultWidths = data.defaultWidths ?? [12, 12, 12];
1449
+ }
1450
+ }
1451
+
1452
+ /**
1453
+ * Decorator for setting and getting string Property metadata.
1454
+ *
1455
+ * @param metadata - The metadata of the string property.
1456
+ * @returns The method that defines the metadata.
1457
+ */
1458
+ function string(metadata) {
1459
+ switch (metadata.displayStyle) {
1460
+ case 'dropdown':
1461
+ return baseProperty(new DropdownStringDecoratorConfigInternal(metadata), DecoratorTypes.STRING_DROPDOWN);
1462
+ case 'autocomplete':
1463
+ return baseProperty(new AutocompleteStringDecoratorConfigInternal(metadata), DecoratorTypes.STRING_AUTOCOMPLETE);
1464
+ case 'textbox':
1465
+ return baseProperty(new TextboxStringDecoratorConfigInternal(metadata), DecoratorTypes.STRING_TEXTBOX);
1466
+ case 'password':
1467
+ return baseProperty(new PasswordStringDecoratorConfigInternal(metadata), DecoratorTypes.STRING_PASSWORD, [EntityUtilities.CONFIRM_PASSWORD_KEY]);
1468
+ default:
1469
+ return baseProperty(new DefaultStringDecoratorConfigInternal(metadata), DecoratorTypes.STRING);
1470
+ }
1471
+ }
1472
+
1473
+ /**
1474
+ * A base Entity class with a builtin id.
1475
+ */
1476
+ class Entity {
1477
+ /**
1478
+ * A unique identifier for the Entity.
1479
+ */
1480
+ id;
1481
+ constructor(entity) {
1482
+ this.id = entity?.id;
1483
+ }
1484
+ }
1485
+ __decorate([
1486
+ string({
1487
+ omitForCreate: true,
1488
+ omitForUpdate: true,
1489
+ display: false,
1490
+ displayStyle: 'line',
1491
+ displayName: 'ID',
1492
+ required: true
1493
+ }),
1494
+ __metadata("design:type", String)
1495
+ ], Entity.prototype, "id", void 0);
1496
+
1497
+ /**
1498
+ * The abstract BaseBuilder class.
1499
+ */
1500
+ class BaseBuilder {
1501
+ data;
1502
+ inputData;
1503
+ constructor(data) {
1504
+ this.validateInput(data);
1505
+ this.inputData = data;
1506
+ this.data = this.generateBaseData(data);
1507
+ return this;
1508
+ }
1509
+ /**
1510
+ * Used to validate the user input in the constructor.
1511
+ *
1512
+ * @param data - The user input.
1513
+ */
1514
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1515
+ validateInput(data) {
1516
+ // By default, no validation is done
1517
+ }
1518
+ /**
1519
+ * Sets the value for the given key if no user value was provided.
1520
+ *
1521
+ * @param key - The key to set the default value for.
1522
+ * @param value - The value to set when nothing was provided.
1523
+ * @returns The Builder.
1524
+ */
1525
+ withDefault(key, value) {
1526
+ if (this.inputData == null || this.inputData[key] == null) {
1527
+ this.data[key] = value;
1528
+ }
1529
+ return this;
1530
+ }
1531
+ /**
1532
+ * Method used to get the final build value after applying all chaining.
1533
+ *
1534
+ * @returns The build value.
1535
+ */
1536
+ getResult() {
1537
+ return this.data;
1538
+ }
1539
+ }
1540
+
1541
+ /**
1542
+ * The internal ConfirmDialogData. Requires all default values the user can leave out.
1706
1543
  */
1707
- class PasswordStringDecoratorConfigInternal extends PropertyDecoratorConfigInternal {
1544
+ class ConfirmDialogDataInternal {
1708
1545
  // eslint-disable-next-line jsdoc/require-jsdoc
1709
- displayStyle;
1546
+ text;
1710
1547
  // eslint-disable-next-line jsdoc/require-jsdoc
1711
- minLength;
1548
+ type;
1712
1549
  // eslint-disable-next-line jsdoc/require-jsdoc
1713
- maxLength;
1550
+ confirmButtonLabel;
1714
1551
  // eslint-disable-next-line jsdoc/require-jsdoc
1715
- regex;
1552
+ cancelButtonLabel;
1716
1553
  // eslint-disable-next-line jsdoc/require-jsdoc
1717
- needsConfirmation;
1554
+ title;
1718
1555
  // eslint-disable-next-line jsdoc/require-jsdoc
1719
- confirmationDisplayName;
1556
+ requireConfirmation;
1720
1557
  // eslint-disable-next-line jsdoc/require-jsdoc
1721
- passwordsDontMatchErrorMessage;
1558
+ confirmationText;
1559
+ constructor(text, type, confirmButtonLabel, cancelButtonLabel, title, requireConfirmation, confirmationText) {
1560
+ this.text = text;
1561
+ this.type = type;
1562
+ this.confirmButtonLabel = confirmButtonLabel;
1563
+ this.cancelButtonLabel = cancelButtonLabel;
1564
+ this.title = title;
1565
+ this.requireConfirmation = requireConfirmation;
1566
+ this.confirmationText = confirmationText;
1567
+ }
1568
+ }
1569
+ /**
1570
+ * The Builder for the ConfirmDialogData. Sets default values.
1571
+ */
1572
+ class ConfirmDialogDataBuilder extends BaseBuilder {
1722
1573
  constructor(data) {
1723
1574
  super(data);
1724
- this.displayStyle = data.displayStyle;
1725
- this.minLength = data.minLength;
1726
- this.maxLength = data.maxLength;
1727
- this.regex = data.regex;
1728
- this.needsConfirmation = data.needsConfirmation ?? true;
1729
- this.confirmationDisplayName = data.confirmationDisplayName ?? 'Confirm Password';
1730
- this.passwordsDontMatchErrorMessage = data.passwordsDontMatchErrorMessage ?? 'Passwords need to match!';
1731
- this.defaultWidths = data.defaultWidths ?? [12, 12, 12];
1575
+ }
1576
+ // eslint-disable-next-line jsdoc/require-jsdoc
1577
+ generateBaseData(data) {
1578
+ return new ConfirmDialogDataInternal(data?.text ?? ['Do you really want to do this?'], data?.type ?? 'default', data?.confirmButtonLabel ?? 'Confirm', data?.cancelButtonLabel ?? 'Cancel', data?.title ?? 'Confirmation', data?.requireConfirmation ?? false, data?.confirmationText);
1579
+ }
1580
+ // eslint-disable-next-line jsdoc/require-jsdoc
1581
+ validateInput(data) {
1582
+ if (!data) {
1583
+ return;
1584
+ }
1585
+ if (data.requireConfirmation === true && !data.confirmationText) {
1586
+ // eslint-disable-next-line max-len
1587
+ throw new Error('Missing required Input data "confirmationText". You can only omit this value when "requireConfirmation" is false.');
1588
+ }
1589
+ if (data.requireConfirmation !== true && data.confirmationText) {
1590
+ throw new Error('The "confirmationText" will never be shown because "requireConfirmation" is not set to true');
1591
+ }
1592
+ if (data.type === 'info-only' && data.cancelButtonLabel) {
1593
+ throw new Error('The "cancelButtonLabel" will never be shown because "type" is set to "info-only"');
1594
+ }
1732
1595
  }
1733
1596
  }
1734
1597
 
1735
1598
  /**
1736
- * Decorator for setting and getting string Property metadata.
1599
+ * The Dialog used whenever confirmation by the user is required (e.g. When the user tries to delete an entity).
1737
1600
  *
1738
- * @param metadata - The metadata of the string property.
1739
- * @returns The method that defines the metadata.
1601
+ * Can be customized with the MAT_DIALOG_DATA "inputData". Customization options are defined in "ConfirmDialogData".
1740
1602
  */
1741
- function string(metadata) {
1742
- switch (metadata.displayStyle) {
1743
- case 'dropdown':
1744
- return baseProperty(new DropdownStringDecoratorConfigInternal(metadata), DecoratorTypes.STRING_DROPDOWN);
1745
- case 'autocomplete':
1746
- return baseProperty(new AutocompleteStringDecoratorConfigInternal(metadata), DecoratorTypes.STRING_AUTOCOMPLETE);
1747
- case 'textbox':
1748
- return baseProperty(new TextboxStringDecoratorConfigInternal(metadata), DecoratorTypes.STRING_TEXTBOX);
1749
- case 'password':
1750
- return baseProperty(new PasswordStringDecoratorConfigInternal(metadata), DecoratorTypes.STRING_PASSWORD, [EntityUtilities.CONFIRM_PASSWORD_KEY]);
1751
- default:
1752
- return baseProperty(new DefaultStringDecoratorConfigInternal(metadata), DecoratorTypes.STRING);
1603
+ class NgxMatEntityConfirmDialogComponent {
1604
+ dialogRef;
1605
+ inputData;
1606
+ confirm = false;
1607
+ data;
1608
+ constructor(dialogRef, inputData) {
1609
+ this.dialogRef = dialogRef;
1610
+ this.inputData = inputData;
1611
+ }
1612
+ ngOnInit() {
1613
+ this.data = new ConfirmDialogDataBuilder(this.inputData).getResult();
1614
+ this.dialogRef.disableClose = true;
1615
+ }
1616
+ /**
1617
+ * Closes the dialog with true to signal that the action should be run.
1618
+ */
1619
+ confirmAction() {
1620
+ this.dialogRef.close(true);
1621
+ }
1622
+ /**
1623
+ * Closes the dialog.
1624
+ */
1625
+ cancel() {
1626
+ this.dialogRef.close(false);
1627
+ }
1628
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.0", ngImport: i0, type: NgxMatEntityConfirmDialogComponent, deps: [{ token: i1.MatDialogRef }, { token: MAT_DIALOG_DATA }], target: i0.ɵɵFactoryTarget.Component });
1629
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.0", type: NgxMatEntityConfirmDialogComponent, isStandalone: true, selector: "ngx-mat-entity-confirm-dialog", ngImport: i0, template: "<h2 mat-dialog-title *ngIf=\"data.title\">{{data.title}}</h2>\n\n<mat-dialog-content>\n <p *ngFor=\"let paragraph of data.text\">{{paragraph}}</p>\n <div *ngIf=\"data.requireConfirmation\" class=\"checkbox-wrapper\">\n <mat-checkbox [(ngModel)]=\"confirm\" name=\"confirm\">\n {{data.confirmationText}}\n </mat-checkbox>\n </div>\n</mat-dialog-content>\n\n<mat-dialog-actions>\n <button type=\"button\" *ngIf=\"data.type === 'delete'\" mat-raised-button color=\"warn\" (click)=\"confirmAction()\" [disabled]=\"data.requireConfirmation && !confirm\" class=\"confirm-button\">\n {{data.confirmButtonLabel}}\n </button>\n <button type=\"button\" *ngIf=\"data.type !== 'delete'\" mat-raised-button (click)=\"confirmAction()\" [disabled]=\"data.requireConfirmation && !confirm\" class=\"confirm-button\">\n {{data.confirmButtonLabel}}\n </button>\n <button type=\"button\" *ngIf=\"data.type !== 'info-only'\" mat-raised-button (click)=\"cancel()\" class=\"cancel-button\">\n {{data.cancelButtonLabel}}\n </button>\n</mat-dialog-actions>", styles: [".checkbox-wrapper{min-height:50px;display:flex}.checkbox-wrapper>mat-checkbox{align-self:center}mat-dialog-actions{justify-content:space-between}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "ngmodule", type: MatDialogModule }, { kind: "directive", type: i1.MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: i1.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "directive", type: i1.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i4.MatCheckbox, selector: "mat-checkbox", inputs: ["disableRipple", "color", "tabIndex"], exportAs: ["matCheckbox"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i5.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", inputs: ["disabled", "disableRipple", "color"], exportAs: ["matButton"] }] });
1630
+ }
1631
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.0", ngImport: i0, type: NgxMatEntityConfirmDialogComponent, decorators: [{
1632
+ type: Component,
1633
+ args: [{ selector: 'ngx-mat-entity-confirm-dialog', standalone: true, imports: [
1634
+ NgIf,
1635
+ NgFor,
1636
+ MatDialogModule,
1637
+ FormsModule,
1638
+ MatCheckboxModule,
1639
+ MatButtonModule
1640
+ ], template: "<h2 mat-dialog-title *ngIf=\"data.title\">{{data.title}}</h2>\n\n<mat-dialog-content>\n <p *ngFor=\"let paragraph of data.text\">{{paragraph}}</p>\n <div *ngIf=\"data.requireConfirmation\" class=\"checkbox-wrapper\">\n <mat-checkbox [(ngModel)]=\"confirm\" name=\"confirm\">\n {{data.confirmationText}}\n </mat-checkbox>\n </div>\n</mat-dialog-content>\n\n<mat-dialog-actions>\n <button type=\"button\" *ngIf=\"data.type === 'delete'\" mat-raised-button color=\"warn\" (click)=\"confirmAction()\" [disabled]=\"data.requireConfirmation && !confirm\" class=\"confirm-button\">\n {{data.confirmButtonLabel}}\n </button>\n <button type=\"button\" *ngIf=\"data.type !== 'delete'\" mat-raised-button (click)=\"confirmAction()\" [disabled]=\"data.requireConfirmation && !confirm\" class=\"confirm-button\">\n {{data.confirmButtonLabel}}\n </button>\n <button type=\"button\" *ngIf=\"data.type !== 'info-only'\" mat-raised-button (click)=\"cancel()\" class=\"cancel-button\">\n {{data.cancelButtonLabel}}\n </button>\n</mat-dialog-actions>", styles: [".checkbox-wrapper{min-height:50px;display:flex}.checkbox-wrapper>mat-checkbox{align-self:center}mat-dialog-actions{justify-content:space-between}\n"] }]
1641
+ }], ctorParameters: function () { return [{ type: i1.MatDialogRef }, { type: undefined, decorators: [{
1642
+ type: Inject,
1643
+ args: [MAT_DIALOG_DATA]
1644
+ }] }]; } });
1645
+
1646
+ // eslint-disable-next-line jsdoc/require-jsdoc, @typescript-eslint/no-explicit-any
1647
+ function UnsavedChangesGuard(component) {
1648
+ return new Observable((obs) => {
1649
+ if (component.canDeactivate()) {
1650
+ obs.next(true);
1651
+ return;
1652
+ }
1653
+ component.openConfirmNavigationDialog().subscribe(v => {
1654
+ obs.next(v);
1655
+ });
1656
+ });
1657
+ }
1658
+
1659
+ const defaultEditDataRoute = {
1660
+ loadComponent: () => Promise.resolve().then(function () { return editPage_component; }).then(m => m.NgxMatEntityEditPageComponent),
1661
+ title: 'Edit',
1662
+ path: 'entities:id',
1663
+ canDeactivate: [UnsavedChangesGuard]
1664
+ };
1665
+
1666
+ const NGX_VALIDATION_ERRORS_TOOLTIP_TITLE = new InjectionToken('Provider for the validation errors tooltip title.', {
1667
+ providedIn: 'root',
1668
+ factory: () => 'Validation Errors:'
1669
+ });
1670
+ /**
1671
+ * The default function that gets the validation errors tooltip content.
1672
+ *
1673
+ * @param validationErrors - All validation errors for which the tooltip content should be generated.
1674
+ * @returns A html string, containing a list of the name of each invalid property.
1675
+ */
1676
+ function getValidationErrorsTooltipContent(validationErrors) {
1677
+ const title = inject(NGX_VALIDATION_ERRORS_TOOLTIP_TITLE);
1678
+ let res = `
1679
+ ${title}
1680
+ <br>
1681
+ <ul style="margin-bottom: 0px; padding-left: 16px;">
1682
+ `;
1683
+ for (const error of validationErrors) {
1684
+ res = res.concat(`<li>${error.property}</li>`);
1685
+ }
1686
+ res = res.concat('</ul>');
1687
+ return res;
1688
+ }
1689
+
1690
+ /**
1691
+ * A generic EntityService class.
1692
+ * Offers basic CRUD-functionality.
1693
+ * You should create a service for every Entity you have.
1694
+ * If you extend from this you need to make sure that the extended Service can be injected.
1695
+ */
1696
+ class EntityService {
1697
+ http;
1698
+ /**
1699
+ * The route segment that comes before the id when editing an entity in a separate page.
1700
+ */
1701
+ editBaseRoute = 'entities';
1702
+ /**
1703
+ * The key which holds the id value.
1704
+ *
1705
+ * @default 'id'
1706
+ */
1707
+ idKey = 'id';
1708
+ /**
1709
+ * A subject of all the entity values.
1710
+ * Can be subscribed to when you want to do a specific thing whenever the entities change.
1711
+ */
1712
+ entitiesSubject = new BehaviorSubject([]);
1713
+ /**
1714
+ * When frequently trying to find a single entity by an id (eg. When nesting relations)
1715
+ * you might send a lot of unnecessary requests.
1716
+ * Therefore the findById method tries to look in the already existing entities first,
1717
+ * IF the entities have been requested in the last READ_EXPIRATION_IN_MS milliseconds.
1718
+ *
1719
+ * @default 900000 (5 minutes)
1720
+ */
1721
+ READ_EXPIRATION_IN_MS = 900000;
1722
+ /**
1723
+ * Gets the entities in an array from the internal entitiesSubject.
1724
+ *
1725
+ * @returns The current entities in form of an array.
1726
+ */
1727
+ get entities() {
1728
+ return this.entitiesSubject.value;
1729
+ }
1730
+ lastRead;
1731
+ constructor(http) {
1732
+ this.http = http;
1733
+ }
1734
+ /**
1735
+ * Creates a new Entity and pushes it to the entities array.
1736
+ *
1737
+ * @param entity - The data of the entity to create.
1738
+ * All values that should be omitted will be removed from it inside this method.
1739
+ * @param baseUrl - The base url to send the post request to.
1740
+ * This can be used if you want to create an entity belonging to another, like "customers/{id}/invoices".
1741
+ * @returns A Promise of the created entity.
1742
+ */
1743
+ async create(entity, baseUrl = this.baseUrl) {
1744
+ const body = EntityUtilities.getWithoutOmitCreateValues(entity);
1745
+ const filePropertyKeys = EntityUtilities.getFileProperties(entity);
1746
+ if (!filePropertyKeys.length) {
1747
+ return await this.createWithJson(body, baseUrl);
1748
+ }
1749
+ else {
1750
+ return await this.createWithFormData(body, filePropertyKeys, entity, baseUrl);
1751
+ }
1752
+ }
1753
+ // TODO: Find a way to use blobs with jest
1754
+ /* istanbul ignore next */
1755
+ /**
1756
+ * Imports everything from the provided json file.
1757
+ *
1758
+ * @param file - The json file to import from.
1759
+ * @returns All entities that have been imported.
1760
+ */
1761
+ async import(file) {
1762
+ const formData = new FormData();
1763
+ formData.append('import', file);
1764
+ const result = await firstValueFrom(this.http.post(`${this.baseUrl}/import`, formData));
1765
+ this.entities.push(...result);
1766
+ this.entitiesSubject.next(this.entities);
1767
+ return result;
1768
+ }
1769
+ // TODO: Find a way to use blobs with jest
1770
+ /* istanbul ignore next */
1771
+ /**
1772
+ * Creates the entity with form data when the entity contains files in contrast to creating it with a normal json body.
1773
+ * All file values are stored inside their respective property key and their name.
1774
+ * Form data is able to handle setting multiple files to the same key.
1775
+ *
1776
+ * @param body - The body Of the request.
1777
+ * @param filePropertyKeys - All property keys that are files and need to be added to the form data.
1778
+ * @param entity - The entity to create. This is needed in addition to the body because the body doesn't contain any metadata.
1779
+ * @param baseUrl - The base url to send the post request to.
1780
+ * This can be used if you want to create an entity belonging to another, like "customers/{id}/invoices".
1781
+ * @returns The created entity from the server.
1782
+ */
1783
+ async createWithFormData(body, filePropertyKeys, entity, baseUrl = this.baseUrl) {
1784
+ const formData = new FormData();
1785
+ formData.append('body', JSON.stringify(LodashUtilities.omit(body, filePropertyKeys)));
1786
+ for (const key of filePropertyKeys) {
1787
+ if (EntityUtilities.getPropertyMetadata(entity, key, DecoratorTypes.FILE_DEFAULT).multiple) {
1788
+ const fileDataValues = body[key];
1789
+ for (const value of fileDataValues) {
1790
+ formData.append(key, (await FileUtilities.getFileData(value, this.http)).file, value.name);
1791
+ }
1792
+ }
1793
+ else {
1794
+ const fileData = body[key];
1795
+ formData.append(key, (await FileUtilities.getFileData(fileData, this.http)).file, fileData.name);
1796
+ }
1797
+ }
1798
+ const e = await firstValueFrom(this.http.post(baseUrl, formData));
1799
+ if (!e) {
1800
+ throw new Error(`
1801
+ The created entity was not returned in the response.
1802
+ If you want to provide a logic that allows that
1803
+ you need to override the create methods of this class.
1804
+ `);
1805
+ }
1806
+ this.entities.push(e);
1807
+ this.entitiesSubject.next(this.entities);
1808
+ return e;
1753
1809
  }
1754
- }
1755
-
1756
- /**
1757
- * A base Entity class with a builtin id.
1758
- */
1759
- class Entity {
1760
1810
  /**
1761
- * A unique identifier for the Entity.
1811
+ * Creates the entity with a normal json body in contrast to creating it with form data when the entity contains files.
1812
+ *
1813
+ * @param body - The body Of the request.
1814
+ * @param baseUrl - The base url to send the post request to.
1815
+ * This can be used if you want to create an entity belonging to another, like "customers/{id}/invoices".
1816
+ * @returns The created entity from the server.
1762
1817
  */
1763
- id;
1764
- constructor(entity) {
1765
- this.id = entity?.id;
1766
- }
1767
- }
1768
- __decorate([
1769
- string({
1770
- omitForCreate: true,
1771
- omitForUpdate: true,
1772
- display: false,
1773
- displayStyle: 'line',
1774
- displayName: 'ID',
1775
- required: true
1776
- }),
1777
- __metadata("design:type", String)
1778
- ], Entity.prototype, "id", void 0);
1779
-
1780
- /**
1781
- * The abstract BaseBuilder class.
1782
- */
1783
- class BaseBuilder {
1784
- data;
1785
- inputData;
1786
- constructor(data) {
1787
- this.validateInput(data);
1788
- this.inputData = data;
1789
- this.data = this.generateBaseData(data);
1790
- return this;
1818
+ async createWithJson(body, baseUrl = this.baseUrl) {
1819
+ const e = await firstValueFrom(this.http.post(baseUrl, body));
1820
+ if (!e) {
1821
+ throw new Error(`
1822
+ The created entity was not returned in the response.
1823
+ If you want to provide a logic that allows that
1824
+ you need to override the create methods of this class.
1825
+ `);
1826
+ }
1827
+ this.entities.push(e);
1828
+ this.entitiesSubject.next(this.entities);
1829
+ return e;
1791
1830
  }
1792
1831
  /**
1793
- * Used to validate the user input in the constructor.
1832
+ * Gets all existing entities and pushes them to the entities array.
1794
1833
  *
1795
- * @param data - The user input.
1834
+ * @param baseUrl - The base url for the request. Defaults to the baseUrl on the service.
1835
+ * @returns A Promise of all received Entities.
1796
1836
  */
1797
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
1798
- validateInput(data) {
1799
- // By default, no validation is done
1837
+ async read(baseUrl = this.baseUrl) {
1838
+ const e = await firstValueFrom(this.http.get(baseUrl));
1839
+ this.entitiesSubject.next(e);
1840
+ this.lastRead = new Date();
1841
+ return e;
1800
1842
  }
1801
1843
  /**
1802
- * Sets the value for the given key if no user value was provided.
1844
+ * Tries to find an entity with the given id.
1803
1845
  *
1804
- * @param key - The key to set the default value for.
1805
- * @param value - The value to set when nothing was provided.
1806
- * @returns The Builder.
1846
+ * @param id - The id of the entity to find.
1847
+ * @returns The found entity.
1807
1848
  */
1808
- withDefault(key, value) {
1809
- if (this.inputData == null || this.inputData[key] == null) {
1810
- this.data[key] = value;
1849
+ async findById(id) {
1850
+ if (this.lastRead == null
1851
+ || (new Date().getTime() - this.lastRead.getTime()) > this.READ_EXPIRATION_IN_MS) {
1852
+ return firstValueFrom(this.http.get(`${this.baseUrl}/${id}`));
1811
1853
  }
1812
- return this;
1854
+ return this.entities.find(e => e[this.idKey] === id) ?? firstValueFrom(this.http.get(`${this.baseUrl}/${id}`));
1813
1855
  }
1814
1856
  /**
1815
- * Method used to get the final build value after applying all chaining.
1857
+ * Updates a specific Entity.
1816
1858
  *
1817
- * @returns The build value.
1859
+ * @param entity - The updated Entity
1860
+ * All values that should be omitted will be removed from it inside this method.
1861
+ * @param entityPriorChanges - The current Entity.
1862
+ * It Is used to get changed values and only update them instead of sending the whole entity data.
1818
1863
  */
1819
- getResult() {
1820
- return this.data;
1821
- }
1822
- }
1823
-
1824
- /**
1825
- * The internal ConfirmDialogData. Requires all default values the user can leave out.
1826
- */
1827
- class ConfirmDialogDataInternal {
1828
- // eslint-disable-next-line jsdoc/require-jsdoc
1829
- text;
1830
- // eslint-disable-next-line jsdoc/require-jsdoc
1831
- type;
1832
- // eslint-disable-next-line jsdoc/require-jsdoc
1833
- confirmButtonLabel;
1834
- // eslint-disable-next-line jsdoc/require-jsdoc
1835
- cancelButtonLabel;
1836
- // eslint-disable-next-line jsdoc/require-jsdoc
1837
- title;
1838
- // eslint-disable-next-line jsdoc/require-jsdoc
1839
- requireConfirmation;
1840
- // eslint-disable-next-line jsdoc/require-jsdoc
1841
- confirmationText;
1842
- constructor(text, type, confirmButtonLabel, cancelButtonLabel, title, requireConfirmation, confirmationText) {
1843
- this.text = text;
1844
- this.type = type;
1845
- this.confirmButtonLabel = confirmButtonLabel;
1846
- this.cancelButtonLabel = cancelButtonLabel;
1847
- this.title = title;
1848
- this.requireConfirmation = requireConfirmation;
1849
- this.confirmationText = confirmationText;
1850
- }
1851
- }
1852
- /**
1853
- * The Builder for the ConfirmDialogData. Sets default values.
1854
- */
1855
- class ConfirmDialogDataBuilder extends BaseBuilder {
1856
- constructor(data) {
1857
- super(data);
1858
- }
1859
- // eslint-disable-next-line jsdoc/require-jsdoc
1860
- generateBaseData(data) {
1861
- return new ConfirmDialogDataInternal(data?.text ?? ['Do you really want to do this?'], data?.type ?? 'default', data?.confirmButtonLabel ?? 'Confirm', data?.cancelButtonLabel ?? 'Cancel', data?.title ?? 'Confirmation', data?.requireConfirmation ?? false, data?.confirmationText);
1862
- }
1863
- // eslint-disable-next-line jsdoc/require-jsdoc
1864
- validateInput(data) {
1865
- if (!data) {
1866
- return;
1867
- }
1868
- if (data.requireConfirmation === true && !data.confirmationText) {
1869
- // eslint-disable-next-line max-len
1870
- throw new Error('Missing required Input data "confirmationText". You can only omit this value when "requireConfirmation" is false.');
1871
- }
1872
- if (data.requireConfirmation !== true && data.confirmationText) {
1873
- throw new Error('The "confirmationText" will never be shown because "requireConfirmation" is not set to true');
1864
+ async update(entity, entityPriorChanges) {
1865
+ const filePropertyKeys = EntityUtilities.getFileProperties(entityPriorChanges);
1866
+ const body = await this.entityToUpdateRequestBody(entity, entityPriorChanges);
1867
+ if (!filePropertyKeys.length) {
1868
+ await this.updateWithJson(body, entityPriorChanges[this.idKey]);
1874
1869
  }
1875
- if (data.type === 'info-only' && data.cancelButtonLabel) {
1876
- throw new Error('The "cancelButtonLabel" will never be shown because "type" is set to "info-only"');
1870
+ else {
1871
+ await this.updateWithFormData(body, filePropertyKeys, entity, entityPriorChanges[this.idKey]);
1877
1872
  }
1878
1873
  }
1879
- }
1880
-
1881
- /**
1882
- * The Dialog used whenever confirmation by the user is required (e.g. When the user tries to delete an entity).
1883
- *
1884
- * Can be customized with the MAT_DIALOG_DATA "inputData". Customization options are defined in "ConfirmDialogData".
1885
- */
1886
- class NgxMatEntityConfirmDialogComponent {
1887
- dialogRef;
1888
- inputData;
1889
- confirm = false;
1890
- data;
1891
- constructor(dialogRef, inputData) {
1892
- this.dialogRef = dialogRef;
1893
- this.inputData = inputData;
1874
+ /**
1875
+ * Builds the update request body from the given entity before and after its changes.
1876
+ *
1877
+ * @param entity - The entity with changed values.
1878
+ * @param entityPriorChanges - The entity before any changes.
1879
+ * @returns A partial of only the changed values.
1880
+ */
1881
+ async entityToUpdateRequestBody(entity, entityPriorChanges) {
1882
+ const body = await EntityUtilities.getWithoutOmitUpdateValues(entity, entityPriorChanges, this.http);
1883
+ return LodashUtilities.omitBy(body, LodashUtilities.isNil);
1894
1884
  }
1895
- ngOnInit() {
1896
- this.data = new ConfirmDialogDataBuilder(this.inputData).getResult();
1897
- this.dialogRef.disableClose = true;
1885
+ // TODO: Find a way to use blobs with jest
1886
+ /* istanbul ignore next */
1887
+ /**
1888
+ * Updates the entity with form data when the entity contains files in contrast to creating it with a normal json body.
1889
+ * All file values are stored inside their respective property key and their name.
1890
+ * Form data is able to handle setting multiple files to the same key.
1891
+ *
1892
+ * @param body - The request body. Already contains only properties that have changed.
1893
+ * @param filePropertyKeys - The keys of all properties which are files and need to separately be appended to the form data.
1894
+ * @param entity - The original entity. Is needed to get the metadata of all the files.
1895
+ * @param id - The id of the entity to update.
1896
+ */
1897
+ async updateWithFormData(body, filePropertyKeys, entity, id) {
1898
+ const formData = new FormData();
1899
+ formData.append('body', JSON.stringify(body));
1900
+ for (const key of filePropertyKeys) {
1901
+ if (EntityUtilities.getPropertyMetadata(entity, key, DecoratorTypes.FILE_DEFAULT).multiple) {
1902
+ const fileDataValues = body[key];
1903
+ for (const value of fileDataValues) {
1904
+ formData.append(key, (await FileUtilities.getFileData(value, this.http)).file, value.name);
1905
+ }
1906
+ }
1907
+ else {
1908
+ const fileData = body[key];
1909
+ formData.append(key, (await FileUtilities.getFileData(fileData, this.http)).file, fileData.name);
1910
+ }
1911
+ }
1912
+ const updatedEntity = await firstValueFrom(this.http.patch(`${this.baseUrl}/${id}`, formData));
1913
+ if (!updatedEntity) {
1914
+ // eslint-disable-next-line no-console
1915
+ console.warn('The updated entity was not returned in the response. Applying the changes from the request body.');
1916
+ for (const key in body) {
1917
+ this.entities[this.entities.findIndex(e => e[this.idKey] === id)][key]
1918
+ = body[key];
1919
+ }
1920
+ this.entitiesSubject.next(this.entities);
1921
+ return;
1922
+ }
1923
+ this.entities[this.entities.findIndex(e => e[this.idKey] === id)] = updatedEntity;
1924
+ this.entitiesSubject.next(this.entities);
1898
1925
  }
1899
1926
  /**
1900
- * Closes the dialog with true to signal that the action should be run.
1927
+ * Updates the entity with a normal json body in contrast to updating it with form data when the entity contains files.
1928
+ *
1929
+ * @param body - The body of the Request. Has already removed all unnecessary values.
1930
+ * @param id - The id of the entity to update.
1901
1931
  */
1902
- confirmAction() {
1903
- this.dialogRef.close(true);
1932
+ async updateWithJson(body, id) {
1933
+ const updatedEntity = await firstValueFrom(this.http.patch(`${this.baseUrl}/${id}`, LodashUtilities.omitBy(body, LodashUtilities.isNil)));
1934
+ if (!updatedEntity) {
1935
+ // eslint-disable-next-line no-console
1936
+ console.warn('The updated entity was not returned in the response. Applying the changes from the request body.');
1937
+ const foundEntity = this.entities[this.entities.findIndex(e => e[this.idKey] === id)];
1938
+ for (const key in body) {
1939
+ foundEntity[key]
1940
+ = body[key];
1941
+ }
1942
+ this.entitiesSubject.next(this.entities);
1943
+ return;
1944
+ }
1945
+ this.entities[this.entities.findIndex(e => e[this.idKey] === id)] = updatedEntity;
1946
+ this.entitiesSubject.next(this.entities);
1904
1947
  }
1905
1948
  /**
1906
- * Closes the dialog.
1949
+ * Deletes a specific Entity.
1950
+ *
1951
+ * @param entity - The entity to delete.
1907
1952
  */
1908
- cancel() {
1909
- this.dialogRef.close(false);
1953
+ async delete(entity) {
1954
+ await firstValueFrom(this.http.delete(`${this.baseUrl}/${entity[this.idKey]}`));
1955
+ // the == comparison instead of === is to catch ids that are numbers.
1956
+ this.entities.splice(this.entities.findIndex(e => e[this.idKey] === entity[this.idKey]), 1);
1957
+ this.entitiesSubject.next(this.entities);
1910
1958
  }
1911
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.0", ngImport: i0, type: NgxMatEntityConfirmDialogComponent, deps: [{ token: i1.MatDialogRef }, { token: MAT_DIALOG_DATA }], target: i0.ɵɵFactoryTarget.Component });
1912
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.0", type: NgxMatEntityConfirmDialogComponent, isStandalone: true, selector: "ngx-mat-entity-confirm-dialog", ngImport: i0, template: "<h2 mat-dialog-title *ngIf=\"data.title\">{{data.title}}</h2>\n\n<mat-dialog-content>\n <p *ngFor=\"let paragraph of data.text\">{{paragraph}}</p>\n <div *ngIf=\"data.requireConfirmation\" class=\"checkbox-wrapper\">\n <mat-checkbox [(ngModel)]=\"confirm\" name=\"confirm\">\n {{data.confirmationText}}\n </mat-checkbox>\n </div>\n</mat-dialog-content>\n\n<mat-dialog-actions>\n <button type=\"button\" *ngIf=\"data.type === 'delete'\" mat-raised-button color=\"warn\" (click)=\"confirmAction()\" [disabled]=\"data.requireConfirmation && !confirm\" class=\"confirm-button\">\n {{data.confirmButtonLabel}}\n </button>\n <button type=\"button\" *ngIf=\"data.type !== 'delete'\" mat-raised-button (click)=\"confirmAction()\" [disabled]=\"data.requireConfirmation && !confirm\" class=\"confirm-button\">\n {{data.confirmButtonLabel}}\n </button>\n <button type=\"button\" *ngIf=\"data.type !== 'info-only'\" mat-raised-button (click)=\"cancel()\" class=\"cancel-button\">\n {{data.cancelButtonLabel}}\n </button>\n</mat-dialog-actions>", styles: [".checkbox-wrapper{min-height:50px;display:flex}.checkbox-wrapper>mat-checkbox{align-self:center}mat-dialog-actions{justify-content:space-between}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "ngmodule", type: MatDialogModule }, { kind: "directive", type: i1.MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: i1.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "directive", type: i1.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i4.MatCheckbox, selector: "mat-checkbox", inputs: ["disableRipple", "color", "tabIndex"], exportAs: ["matCheckbox"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i5.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", inputs: ["disabled", "disableRipple", "color"], exportAs: ["matButton"] }] });
1913
- }
1914
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.0", ngImport: i0, type: NgxMatEntityConfirmDialogComponent, decorators: [{
1915
- type: Component,
1916
- args: [{ selector: 'ngx-mat-entity-confirm-dialog', standalone: true, imports: [
1917
- NgIf,
1918
- NgFor,
1919
- MatDialogModule,
1920
- FormsModule,
1921
- MatCheckboxModule,
1922
- MatButtonModule
1923
- ], template: "<h2 mat-dialog-title *ngIf=\"data.title\">{{data.title}}</h2>\n\n<mat-dialog-content>\n <p *ngFor=\"let paragraph of data.text\">{{paragraph}}</p>\n <div *ngIf=\"data.requireConfirmation\" class=\"checkbox-wrapper\">\n <mat-checkbox [(ngModel)]=\"confirm\" name=\"confirm\">\n {{data.confirmationText}}\n </mat-checkbox>\n </div>\n</mat-dialog-content>\n\n<mat-dialog-actions>\n <button type=\"button\" *ngIf=\"data.type === 'delete'\" mat-raised-button color=\"warn\" (click)=\"confirmAction()\" [disabled]=\"data.requireConfirmation && !confirm\" class=\"confirm-button\">\n {{data.confirmButtonLabel}}\n </button>\n <button type=\"button\" *ngIf=\"data.type !== 'delete'\" mat-raised-button (click)=\"confirmAction()\" [disabled]=\"data.requireConfirmation && !confirm\" class=\"confirm-button\">\n {{data.confirmButtonLabel}}\n </button>\n <button type=\"button\" *ngIf=\"data.type !== 'info-only'\" mat-raised-button (click)=\"cancel()\" class=\"cancel-button\">\n {{data.cancelButtonLabel}}\n </button>\n</mat-dialog-actions>", styles: [".checkbox-wrapper{min-height:50px;display:flex}.checkbox-wrapper>mat-checkbox{align-self:center}mat-dialog-actions{justify-content:space-between}\n"] }]
1924
- }], ctorParameters: function () { return [{ type: i1.MatDialogRef }, { type: undefined, decorators: [{
1925
- type: Inject,
1926
- args: [MAT_DIALOG_DATA]
1927
- }] }]; } });
1928
-
1929
- // eslint-disable-next-line jsdoc/require-jsdoc, @typescript-eslint/no-explicit-any
1930
- function UnsavedChangesGuard(component) {
1931
- return new Observable((obs) => {
1932
- if (component.canDeactivate()) {
1933
- obs.next(true);
1934
- return;
1935
- }
1936
- component.openConfirmNavigationDialog().subscribe(v => {
1937
- obs.next(v);
1938
- });
1939
- });
1940
1959
  }
1941
1960
 
1942
- const defaultEditDataRoute = {
1943
- loadComponent: () => Promise.resolve().then(function () { return editPage_component; }).then(m => m.NgxMatEntityEditPageComponent),
1944
- title: 'Edit',
1945
- path: 'entities:id',
1946
- canDeactivate: [UnsavedChangesGuard]
1947
- };
1948
-
1949
1961
  /**
1950
- * A generic EntityService class.
1951
- * Offers basic CRUD-functionality.
1952
- * You should create a service for every Entity you have.
1953
- * If you extend from this you need to make sure that the extended Service can be injected.
1962
+ * Contains HelperMethods around handling Validation of entities and properties.
1954
1963
  */
1955
- class EntityService {
1956
- http;
1957
- /**
1958
- * The route segment that comes before the id when editing an entity in a separate page.
1959
- */
1960
- editBaseRoute = 'entities';
1964
+ class ValidationUtilities {
1961
1965
  /**
1962
- * The key which holds the id value.
1966
+ * Checks if the values on an entity are valid.
1967
+ * Also checks all the validators given by the metadata ("required", "maxLength" etc.).
1963
1968
  *
1964
- * @default 'id'
1965
- */
1966
- idKey = 'id';
1967
- /**
1968
- * A subject of all the entity values.
1969
- * Can be subscribed to when you want to do a specific thing whenever the entities change.
1969
+ * @param entity - The entity to validate.
1970
+ * @param omit - Whether to check for creating or editing validity.
1971
+ * @returns Whether or not the entity is valid.
1970
1972
  */
1971
- entitiesSubject = new BehaviorSubject([]);
1973
+ static isEntityValid(entity, omit) {
1974
+ return this.getEntityValidationErrors(entity, omit).length === 0;
1975
+ }
1972
1976
  /**
1973
- * When frequently trying to find a single entity by an id (eg. When nesting relations)
1974
- * you might send a lot of unnecessary requests.
1975
- * Therefore the findById method tries to look in the already existing entities first,
1976
- * IF the entities have been requested in the last READ_EXPIRATION_IN_MS milliseconds.
1977
+ * Gets all validation errors on the given entity.
1977
1978
  *
1978
- * @default 900000 (5 minutes)
1979
+ * @param entity - The entity to validate.
1980
+ * @param omit - What keys not to check. An empty value means no keys are omitted.
1981
+ * @returns An array of validation errors on the provided entity.
1979
1982
  */
1980
- READ_EXPIRATION_IN_MS = 900000;
1983
+ static getEntityValidationErrors(entity, omit) {
1984
+ const res = [];
1985
+ for (const key in entity) {
1986
+ const err = this.getPropertyValidationError(entity, key, omit);
1987
+ if (err) {
1988
+ res.push(err);
1989
+ }
1990
+ }
1991
+ return res;
1992
+ }
1993
+ // /**
1994
+ // * Checks if a single property value is valid.
1995
+ // *
1996
+ // * @param entity - The entity where the property is from.
1997
+ // * @param key - The name of the property.
1998
+ // * @param omit - Whether to check if the given entity is valid for creation or updating.
1999
+ // * @returns Whether or not the property value is valid.
2000
+ // * @throws Throws when it extracts an unknown metadata type.
2001
+ // */
2002
+ // static isPropertyValid<EntityType extends BaseEntityType<EntityType>>(
2003
+ // entity: EntityType,
2004
+ // key: keyof EntityType,
2005
+ // omit?: 'create' | 'update'
2006
+ // ): boolean {
2007
+ // return this.getPropertyValidationError(entity, key, omit) == null;
2008
+ // }
1981
2009
  /**
1982
- * Gets the entities in an array from the internal entitiesSubject.
2010
+ * Validates the property on the given entity with the given key.
1983
2011
  *
1984
- * @returns The current entities in form of an array.
2012
+ * @param entity - The entity on which the property to check is.
2013
+ * @param key - The key of the property to validate.
2014
+ * @param omit - What keys not to check. An empty value means no keys are omitted.
2015
+ * @returns A validation error when the property is not valid, undefined otherwise.
2016
+ * @throws When the type of the property is not known.
1985
2017
  */
1986
- get entities() {
1987
- return this.entitiesSubject.value;
2018
+ static getPropertyValidationError(entity, key, omit) {
2019
+ const type = EntityUtilities.getPropertyType(entity, key);
2020
+ const metadata = EntityUtilities.getPropertyMetadata(entity, key, type);
2021
+ if (metadata.omitForCreate && omit === 'create') {
2022
+ return undefined;
2023
+ }
2024
+ if (metadata.omitForUpdate && omit === 'update') {
2025
+ return undefined;
2026
+ }
2027
+ if (metadata.required(entity) && type !== DecoratorTypes.HAS_MANY) {
2028
+ if (entity[key] == null || entity[key] === '') {
2029
+ return {
2030
+ property: metadata.displayName,
2031
+ message: 'required'
2032
+ };
2033
+ }
2034
+ }
2035
+ if (!metadata.required(entity)) {
2036
+ if (entity[key] == null || entity[key] === '') {
2037
+ return undefined;
2038
+ }
2039
+ }
2040
+ switch (type) {
2041
+ case DecoratorTypes.BOOLEAN_DROPDOWN:
2042
+ break;
2043
+ case DecoratorTypes.BOOLEAN_CHECKBOX:
2044
+ case DecoratorTypes.BOOLEAN_TOGGLE:
2045
+ const entityBoolean = entity[key];
2046
+ const booleanMetadata = metadata;
2047
+ return this.getBooleanValidationError(entity, entityBoolean, booleanMetadata);
2048
+ case DecoratorTypes.STRING_DROPDOWN:
2049
+ break;
2050
+ case DecoratorTypes.STRING:
2051
+ case DecoratorTypes.STRING_AUTOCOMPLETE:
2052
+ const entityString = entity[key];
2053
+ const stringMetadata = metadata;
2054
+ return this.getStringValidationError(entityString, stringMetadata);
2055
+ case DecoratorTypes.STRING_TEXTBOX:
2056
+ const entityTextbox = entity[key];
2057
+ const textboxMetadata = metadata;
2058
+ return this.getTextboxValidationError(entityTextbox, textboxMetadata);
2059
+ case DecoratorTypes.STRING_PASSWORD:
2060
+ const entityPassword = entity[key];
2061
+ const passwordMetadata = metadata;
2062
+ const confirmPassword = ReflectUtilities.getMetadata(EntityUtilities.CONFIRM_PASSWORD_KEY, entity, key);
2063
+ return this.getPasswordValidationError(entityPassword, passwordMetadata, confirmPassword);
2064
+ case DecoratorTypes.NUMBER_DROPDOWN:
2065
+ // Because only valid values can be selected, this is always true when it has a value
2066
+ return undefined;
2067
+ case DecoratorTypes.NUMBER:
2068
+ case DecoratorTypes.NUMBER_SLIDER:
2069
+ const entityNumber = entity[key];
2070
+ const numberMetadata = metadata;
2071
+ return this.getNumberValidationError(entityNumber, numberMetadata);
2072
+ case DecoratorTypes.OBJECT:
2073
+ const entityObject = entity[key];
2074
+ for (const parameterKey in entityObject) {
2075
+ const value = entityObject[parameterKey];
2076
+ if (!metadata.omit.includes(parameterKey)
2077
+ && !(!metadata.required(entity) && (value == null || value == ''))) {
2078
+ const err = this.getPropertyValidationError(entityObject, parameterKey, omit);
2079
+ if (err) {
2080
+ return {
2081
+ property: metadata.displayName,
2082
+ message: `${err.property} is invalid: ${err.message}`
2083
+ };
2084
+ }
2085
+ }
2086
+ }
2087
+ break;
2088
+ case DecoratorTypes.ARRAY_STRING_CHIPS:
2089
+ case DecoratorTypes.ARRAY_STRING_AUTOCOMPLETE_CHIPS:
2090
+ case DecoratorTypes.ARRAY_DATE:
2091
+ case DecoratorTypes.ARRAY_DATE_TIME:
2092
+ case DecoratorTypes.ARRAY_DATE_RANGE:
2093
+ case DecoratorTypes.ARRAY:
2094
+ const entityArray = entity[key];
2095
+ // eslint-disable-next-line max-len
2096
+ const arrayMetadata = metadata;
2097
+ if (arrayMetadata.required(entity) && !entityArray.length) {
2098
+ return {
2099
+ property: metadata.displayName,
2100
+ message: 'no items in array'
2101
+ };
2102
+ }
2103
+ break;
2104
+ case DecoratorTypes.DATE:
2105
+ const entityDate = new Date(entity[key]);
2106
+ const dateMetadata = metadata;
2107
+ return this.getDateValidationError(entityDate, dateMetadata);
2108
+ case DecoratorTypes.DATE_RANGE:
2109
+ const entityDateRange = LodashUtilities.cloneDeep(entity[key]);
2110
+ const dateRangeMetadata = metadata;
2111
+ return this.getDateRangeValidationError(entity, entityDateRange, dateRangeMetadata);
2112
+ case DecoratorTypes.DATE_TIME:
2113
+ const entityDateTime = new Date(entity[key]);
2114
+ const dateTimeMetadata = metadata;
2115
+ const hasTime = ReflectUtilities.hasMetadata(EntityUtilities.TIME_KEY, entity, key);
2116
+ return this.getDateTimeValidationError(entityDateTime, dateTimeMetadata, hasTime);
2117
+ case DecoratorTypes.FILE_DEFAULT:
2118
+ case DecoratorTypes.FILE_IMAGE:
2119
+ const entityFile = entity[key];
2120
+ const entityFileMetadata = metadata;
2121
+ return this.getFileDataValidationError(entityFile, entityFileMetadata);
2122
+ case DecoratorTypes.REFERENCES_MANY:
2123
+ case DecoratorTypes.REFERENCES_ONE:
2124
+ case DecoratorTypes.HAS_MANY:
2125
+ break;
2126
+ case DecoratorTypes.CUSTOM:
2127
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, max-len
2128
+ const customMetadata = metadata;
2129
+ if (!customMetadata.isValid(entity[key], omit)) {
2130
+ return {
2131
+ property: metadata.displayName,
2132
+ message: 'invalid'
2133
+ };
2134
+ }
2135
+ break;
2136
+ default:
2137
+ throw new Error(`Could not validate the input because the DecoratorType ${type} is not known`);
2138
+ }
2139
+ return undefined;
1988
2140
  }
1989
- lastRead;
1990
- constructor(http) {
1991
- this.http = http;
2141
+ static getBooleanValidationError(entity, value, metadata) {
2142
+ if (metadata.required(entity) && !value) {
2143
+ return {
2144
+ property: metadata.displayName,
2145
+ message: 'not selected'
2146
+ };
2147
+ }
2148
+ return undefined;
1992
2149
  }
1993
- /**
1994
- * Creates a new Entity and pushes it to the entities array.
1995
- *
1996
- * @param entity - The data of the entity to create.
1997
- * All values that should be omitted will be removed from it inside this method.
1998
- * @param baseUrl - The base url to send the post request to.
1999
- * This can be used if you want to create an entity belonging to another, like "customers/{id}/invoices".
2000
- * @returns A Promise of the created entity.
2001
- */
2002
- async create(entity, baseUrl = this.baseUrl) {
2003
- const body = EntityUtilities.getWithoutOmitCreateValues(entity);
2004
- const filePropertyKeys = EntityUtilities.getFileProperties(entity);
2005
- if (!filePropertyKeys.length) {
2006
- return await this.createWithJson(body, baseUrl);
2150
+ static getStringValidationError(value, metadata) {
2151
+ if (metadata.maxLength && value.length > metadata.maxLength) {
2152
+ return {
2153
+ property: metadata.displayName,
2154
+ message: `needs to be smaller than ${metadata.maxLength} characters`
2155
+ };
2156
+ }
2157
+ if (metadata.minLength && value.length < metadata.minLength) {
2158
+ return {
2159
+ property: metadata.displayName,
2160
+ message: `needs to be bigger than ${metadata.minLength} characters`
2161
+ };
2162
+ }
2163
+ if (metadata.regex && !value.match(metadata.regex)) {
2164
+ return {
2165
+ property: metadata.displayName,
2166
+ message: 'invalid'
2167
+ };
2168
+ }
2169
+ return undefined;
2170
+ }
2171
+ static getTextboxValidationError(value, metadata) {
2172
+ if (metadata.maxLength && value.length > metadata.maxLength) {
2173
+ return {
2174
+ property: metadata.displayName,
2175
+ message: `needs to be smaller than ${metadata.maxLength} characters`
2176
+ };
2177
+ }
2178
+ if (metadata.minLength && value.length < metadata.minLength) {
2179
+ return {
2180
+ property: metadata.displayName,
2181
+ message: `needs to be bigger than ${metadata.minLength} characters`
2182
+ };
2183
+ }
2184
+ return undefined;
2185
+ }
2186
+ static getPasswordValidationError(value, metadata, confirmPassword) {
2187
+ if (value !== confirmPassword) {
2188
+ return {
2189
+ property: metadata.displayName,
2190
+ message: 'passwords need to match'
2191
+ };
2192
+ }
2193
+ if (metadata.maxLength && value.length > metadata.maxLength) {
2194
+ return {
2195
+ property: metadata.displayName,
2196
+ message: `needs to be smaller than ${metadata.maxLength} characters`
2197
+ };
2198
+ }
2199
+ if (metadata.minLength && value.length < metadata.minLength) {
2200
+ return {
2201
+ property: metadata.displayName,
2202
+ message: `needs to be bigger than ${metadata.minLength} characters`
2203
+ };
2204
+ }
2205
+ if (metadata.regex && !value.match(metadata.regex)) {
2206
+ return {
2207
+ property: metadata.displayName,
2208
+ message: 'invalid'
2209
+ };
2210
+ }
2211
+ return undefined;
2212
+ }
2213
+ static getNumberValidationError(value, metadata) {
2214
+ if (metadata.max && value > metadata.max) {
2215
+ return {
2216
+ property: metadata.displayName,
2217
+ message: `needs to be smaller than ${metadata.max}`
2218
+ };
2219
+ }
2220
+ if (metadata.min && value < metadata.min) {
2221
+ return {
2222
+ property: metadata.displayName,
2223
+ message: `needs to be bigger than ${metadata.min}`
2224
+ };
2225
+ }
2226
+ return undefined;
2227
+ }
2228
+ static getDateValidationError(value, metadata) {
2229
+ if (metadata.min && value.getTime() < metadata.min(value).getTime()) {
2230
+ return {
2231
+ property: metadata.displayName,
2232
+ message: `needs to be after ${formatDate(metadata.min(value))}`
2233
+ };
2234
+ }
2235
+ if (metadata.max && value.getTime() > metadata.max(value).getTime()) {
2236
+ return {
2237
+ property: metadata.displayName,
2238
+ message: `needs to be before ${formatDate(metadata.max(value))}`
2239
+ };
2007
2240
  }
2008
- else {
2009
- return await this.createWithFormData(body, filePropertyKeys, entity, baseUrl);
2241
+ if (metadata.filter && !metadata.filter(value)) {
2242
+ return {
2243
+ property: metadata.displayName,
2244
+ message: 'invalid'
2245
+ };
2010
2246
  }
2247
+ return undefined;
2011
2248
  }
2012
- // TODO: Find a way to use blobs with jest
2013
- /* istanbul ignore next */
2014
- /**
2015
- * Imports everything from the provided json file.
2016
- *
2017
- * @param file - The json file to import from.
2018
- * @returns All entities that have been imported.
2019
- */
2020
- async import(file) {
2021
- const formData = new FormData();
2022
- formData.append('import', file);
2023
- const result = await firstValueFrom(this.http.post(`${this.baseUrl}/import`, formData));
2024
- this.entities.push(...result);
2025
- this.entitiesSubject.next(this.entities);
2026
- return result;
2027
- }
2028
- // TODO: Find a way to use blobs with jest
2029
- /* istanbul ignore next */
2030
- /**
2031
- * Creates the entity with form data when the entity contains files in contrast to creating it with a normal json body.
2032
- * All file values are stored inside their respective property key and their name.
2033
- * Form data is able to handle setting multiple files to the same key.
2034
- *
2035
- * @param body - The body Of the request.
2036
- * @param filePropertyKeys - All property keys that are files and need to be added to the form data.
2037
- * @param entity - The entity to create. This is needed in addition to the body because the body doesn't contain any metadata.
2038
- * @param baseUrl - The base url to send the post request to.
2039
- * This can be used if you want to create an entity belonging to another, like "customers/{id}/invoices".
2040
- * @returns The created entity from the server.
2041
- */
2042
- async createWithFormData(body, filePropertyKeys, entity, baseUrl = this.baseUrl) {
2043
- const formData = new FormData();
2044
- formData.append('body', JSON.stringify(LodashUtilities.omit(body, filePropertyKeys)));
2045
- for (const key of filePropertyKeys) {
2046
- if (EntityUtilities.getPropertyMetadata(entity, key, DecoratorTypes.FILE_DEFAULT).multiple) {
2047
- const fileDataValues = body[key];
2048
- for (const value of fileDataValues) {
2049
- formData.append(key, (await FileUtilities.getFileData(value, this.http)).file, value.name);
2249
+ static getDateRangeValidationError(entity, value, metadata) {
2250
+ if (metadata.required(entity)) {
2251
+ if (value.start == null || value.end == null || value.values == null) {
2252
+ return {
2253
+ property: metadata.displayName,
2254
+ message: 'required'
2255
+ };
2256
+ }
2257
+ }
2258
+ value.start = new Date(value.start);
2259
+ value.end = new Date(value.end);
2260
+ if (metadata.minStart && value.start.getTime() < metadata.minStart(value.start).getTime()) {
2261
+ return {
2262
+ property: metadata.displayName,
2263
+ message: `start date needs to be after ${formatDate(metadata.minStart(value.start))}`
2264
+ };
2265
+ }
2266
+ if (metadata.maxStart && value.start.getTime() > metadata.maxStart(value.start).getTime()) {
2267
+ return {
2268
+ property: metadata.displayName,
2269
+ message: `start date needs to be before ${formatDate(metadata.maxStart(value.start))}`
2270
+ };
2271
+ }
2272
+ if (metadata.minEnd && value.end.getTime() < metadata.minEnd(value.end).getTime()) {
2273
+ return {
2274
+ property: metadata.displayName,
2275
+ message: `end date needs to be after ${formatDate(metadata.minEnd(value.end))}`
2276
+ };
2277
+ }
2278
+ if (metadata.maxEnd && value.end.getTime() > metadata.maxEnd(value.end).getTime()) {
2279
+ return {
2280
+ property: metadata.displayName,
2281
+ message: `end date needs to be before ${formatDate(metadata.maxEnd(value.end))}`
2282
+ };
2283
+ }
2284
+ if (metadata.filter) {
2285
+ if (!metadata.filter(value.start)) {
2286
+ return {
2287
+ property: metadata.displayName,
2288
+ message: 'start date invalid'
2289
+ };
2290
+ }
2291
+ if (!metadata.filter(value.end)) {
2292
+ return {
2293
+ property: metadata.displayName,
2294
+ message: 'end date invalid'
2295
+ };
2296
+ }
2297
+ for (const date of value.values) {
2298
+ if (!metadata.filter(date)) {
2299
+ return {
2300
+ property: metadata.displayName,
2301
+ message: `value ${formatDate(date)} invalid`
2302
+ };
2050
2303
  }
2051
2304
  }
2052
- else {
2053
- const fileData = body[key];
2054
- formData.append(key, (await FileUtilities.getFileData(fileData, this.http)).file, fileData.name);
2305
+ }
2306
+ return undefined;
2307
+ }
2308
+ static getDateTimeValidationError(value, metadata, hasTime) {
2309
+ if (!hasTime) {
2310
+ return {
2311
+ property: metadata.displayName,
2312
+ message: 'no time'
2313
+ };
2314
+ }
2315
+ if (metadata.minDate && value.getTime() < metadata.minDate(value).getTime()) {
2316
+ return {
2317
+ property: metadata.displayName,
2318
+ message: `date needs to be after ${formatDate(metadata.minDate(value))}`
2319
+ };
2320
+ }
2321
+ if (metadata.maxDate && value.getTime() > metadata.maxDate(value).getTime()) {
2322
+ return {
2323
+ property: metadata.displayName,
2324
+ message: `date needs to be before ${formatDate(metadata.maxDate(value))}`
2325
+ };
2326
+ }
2327
+ if (metadata.filterDate && !metadata.filterDate(value)) {
2328
+ return {
2329
+ property: metadata.displayName,
2330
+ message: 'invalid date'
2331
+ };
2332
+ }
2333
+ const time = DateUtilities.getTimeFromDate(value);
2334
+ if (metadata.minTime) {
2335
+ const minTime = metadata.minTime(value);
2336
+ if (!(time.hours > minTime.hours
2337
+ || (time.hours === minTime.hours
2338
+ && time.minutes >= minTime.minutes))) {
2339
+ return {
2340
+ property: metadata.displayName,
2341
+ message: `time needs to be after ${minTime.hours}:${minTime.minutes}`
2342
+ };
2055
2343
  }
2056
2344
  }
2057
- const e = await firstValueFrom(this.http.post(baseUrl, formData));
2058
- if (!e) {
2059
- throw new Error(`
2060
- The created entity was not returned in the response.
2061
- If you want to provide a logic that allows that
2062
- you need to override the create methods of this class.
2063
- `);
2345
+ if (metadata.maxTime) {
2346
+ const maxTime = metadata.maxTime(value);
2347
+ if (!(time.hours < maxTime.hours
2348
+ || (time.hours === maxTime.hours
2349
+ && time.minutes <= maxTime.minutes))) {
2350
+ return {
2351
+ property: metadata.displayName,
2352
+ message: `time needs to be before ${maxTime.hours}:${maxTime.minutes}`
2353
+ };
2354
+ }
2064
2355
  }
2065
- this.entities.push(e);
2066
- this.entitiesSubject.next(this.entities);
2067
- return e;
2068
- }
2069
- /**
2070
- * Creates the entity with a normal json body in contrast to creating it with form data when the entity contains files.
2071
- *
2072
- * @param body - The body Of the request.
2073
- * @param baseUrl - The base url to send the post request to.
2074
- * This can be used if you want to create an entity belonging to another, like "customers/{id}/invoices".
2075
- * @returns The created entity from the server.
2076
- */
2077
- async createWithJson(body, baseUrl = this.baseUrl) {
2078
- const e = await firstValueFrom(this.http.post(baseUrl, body));
2079
- if (!e) {
2080
- throw new Error(`
2081
- The created entity was not returned in the response.
2082
- If you want to provide a logic that allows that
2083
- you need to override the create methods of this class.
2084
- `);
2356
+ if (metadata.filterTime && !metadata.filterTime(time)) {
2357
+ return {
2358
+ property: metadata.displayName,
2359
+ message: 'invalid time'
2360
+ };
2085
2361
  }
2086
- this.entities.push(e);
2087
- this.entitiesSubject.next(this.entities);
2088
- return e;
2089
- }
2090
- /**
2091
- * Gets all existing entities and pushes them to the entities array.
2092
- *
2093
- * @param baseUrl - The base url for the request. Defaults to the baseUrl on the service.
2094
- * @returns A Promise of all received Entities.
2095
- */
2096
- async read(baseUrl = this.baseUrl) {
2097
- const e = await firstValueFrom(this.http.get(baseUrl));
2098
- this.entitiesSubject.next(e);
2099
- this.lastRead = new Date();
2100
- return e;
2362
+ return undefined;
2101
2363
  }
2102
- /**
2103
- * Tries to find an entity with the given id.
2104
- *
2105
- * @param id - The id of the entity to find.
2106
- * @returns The found entity.
2107
- */
2108
- async findById(id) {
2109
- if (this.lastRead == null
2110
- || (new Date().getTime() - this.lastRead.getTime()) > this.READ_EXPIRATION_IN_MS) {
2111
- return firstValueFrom(this.http.get(`${this.baseUrl}/${id}`));
2364
+ static getFileDataValidationError(value, metadata) {
2365
+ const files = metadata.multiple ? value : [value];
2366
+ let fileSizeTotal = 0;
2367
+ for (const file of files) {
2368
+ if (!file.name || !file.file && !file.url) {
2369
+ return {
2370
+ property: metadata.displayName,
2371
+ message: 'invalid'
2372
+ };
2373
+ }
2374
+ if (!FileUtilities.isMimeTypeValid(file.type, metadata.allowedMimeTypes)) {
2375
+ return {
2376
+ property: metadata.displayName,
2377
+ message: `mimetype needs to be one of ${metadata.allowedMimeTypes}`
2378
+ };
2379
+ }
2380
+ if (FileUtilities.transformToMegaBytes(file.size, 'B') > metadata.maxSize) {
2381
+ return {
2382
+ property: metadata.displayName,
2383
+ message: `file needs to be smaller than ${metadata.maxSize} MB`
2384
+ };
2385
+ }
2386
+ fileSizeTotal += file.size;
2387
+ if (FileUtilities.transformToMegaBytes(fileSizeTotal, 'B') > metadata.maxSizeTotal) {
2388
+ return {
2389
+ property: metadata.displayName,
2390
+ message: `The size of all files combined needs to be smaller than ${metadata.maxSizeTotal}`
2391
+ };
2392
+ }
2112
2393
  }
2113
- return this.entities.find(e => e[this.idKey] === id) ?? firstValueFrom(this.http.get(`${this.baseUrl}/${id}`));
2394
+ return undefined;
2114
2395
  }
2396
+ }
2397
+ // eslint-disable-next-line jsdoc/require-jsdoc
2398
+ function padTo2Digits(num) {
2399
+ return num.toString().padStart(2, '0');
2400
+ }
2401
+ // eslint-disable-next-line jsdoc/require-jsdoc
2402
+ function formatDate(date) {
2403
+ return [
2404
+ padTo2Digits(date.getDate()),
2405
+ padTo2Digits(date.getMonth() + 1),
2406
+ date.getFullYear()
2407
+ ].join('/');
2408
+ }
2409
+
2410
+ /**
2411
+ * Adds drag and drop functionality to an element.
2412
+ */
2413
+ class DragDropDirective {
2115
2414
  /**
2116
- * Updates a specific Entity.
2117
- *
2118
- * @param entity - The updated Entity
2119
- * All values that should be omitted will be removed from it inside this method.
2120
- * @param entityPriorChanges - The current Entity.
2121
- * It Is used to get changed values and only update them instead of sending the whole entity data.
2415
+ * Emits the dropped files to the parent.
2122
2416
  */
2123
- async update(entity, entityPriorChanges) {
2124
- const filePropertyKeys = EntityUtilities.getFileProperties(entityPriorChanges);
2125
- const body = await this.entityToUpdateRequestBody(entity, entityPriorChanges);
2126
- if (!filePropertyKeys.length) {
2127
- await this.updateWithJson(body, entityPriorChanges[this.idKey]);
2128
- }
2129
- else {
2130
- await this.updateWithFormData(body, filePropertyKeys, entity, entityPriorChanges[this.idKey]);
2131
- }
2132
- }
2417
+ files = new EventEmitter();
2418
+ constructor() { }
2133
2419
  /**
2134
- * Builds the update request body from the given entity before and after its changes.
2420
+ * Prevents the event default.
2135
2421
  *
2136
- * @param entity - The entity with changed values.
2137
- * @param entityPriorChanges - The entity before any changes.
2138
- * @returns A partial of only the changed values.
2422
+ * @param evt - The Event when dragged files hover over the parent.
2139
2423
  */
2140
- async entityToUpdateRequestBody(entity, entityPriorChanges) {
2141
- const body = await EntityUtilities.getWithoutOmitUpdateValues(entity, entityPriorChanges, this.http);
2142
- return LodashUtilities.omitBy(body, LodashUtilities.isNil);
2424
+ onDragOver(evt) {
2425
+ evt.preventDefault();
2426
+ evt.stopPropagation();
2143
2427
  }
2144
- // TODO: Find a way to use blobs with jest
2145
- /* istanbul ignore next */
2146
2428
  /**
2147
- * Updates the entity with form data when the entity contains files in contrast to creating it with a normal json body.
2148
- * All file values are stored inside their respective property key and their name.
2149
- * Form data is able to handle setting multiple files to the same key.
2429
+ * Prevents the event default.
2150
2430
  *
2151
- * @param body - The request body. Already contains only properties that have changed.
2152
- * @param filePropertyKeys - The keys of all properties which are files and need to separately be appended to the form data.
2153
- * @param entity - The original entity. Is needed to get the metadata of all the files.
2154
- * @param id - The id of the entity to update.
2155
- */
2156
- async updateWithFormData(body, filePropertyKeys, entity, id) {
2157
- const formData = new FormData();
2158
- formData.append('body', JSON.stringify(body));
2159
- for (const key of filePropertyKeys) {
2160
- if (EntityUtilities.getPropertyMetadata(entity, key, DecoratorTypes.FILE_DEFAULT).multiple) {
2161
- const fileDataValues = body[key];
2162
- for (const value of fileDataValues) {
2163
- formData.append(key, (await FileUtilities.getFileData(value, this.http)).file, value.name);
2164
- }
2165
- }
2166
- else {
2167
- const fileData = body[key];
2168
- formData.append(key, (await FileUtilities.getFileData(fileData, this.http)).file, fileData.name);
2169
- }
2170
- }
2171
- const updatedEntity = await firstValueFrom(this.http.patch(`${this.baseUrl}/${id}`, formData));
2172
- if (!updatedEntity) {
2173
- // eslint-disable-next-line no-console
2174
- console.warn('The updated entity was not returned in the response. Applying the changes from the request body.');
2175
- for (const key in body) {
2176
- this.entities[this.entities.findIndex(e => e[this.idKey] === id)][key]
2177
- = body[key];
2178
- }
2179
- this.entitiesSubject.next(this.entities);
2180
- return;
2431
+ * @param evt - The Event when dragged files leave the parent.
2432
+ */
2433
+ onDragLeave(evt) {
2434
+ evt.preventDefault();
2435
+ evt.stopPropagation();
2436
+ }
2437
+ /**
2438
+ * Prevents the event default and emits the dropped files with the output.
2439
+ *
2440
+ * @param evt - The Event when files are dropped.
2441
+ */
2442
+ onDrop(evt) {
2443
+ evt.preventDefault();
2444
+ evt.stopPropagation();
2445
+ if (evt.dataTransfer && evt.dataTransfer.files.length > 0) {
2446
+ this.files.emit(Array.from(evt.dataTransfer.files));
2181
2447
  }
2182
- this.entities[this.entities.findIndex(e => e[this.idKey] === id)] = updatedEntity;
2183
- this.entitiesSubject.next(this.entities);
2448
+ }
2449
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.0", ngImport: i0, type: DragDropDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
2450
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.0", type: DragDropDirective, selector: "[dragDrop]", outputs: { files: "files" }, host: { listeners: { "dragover": "onDragOver($event)", "dragleave": "onDragLeave($event)", "drop": "onDrop($event)" } }, ngImport: i0 });
2451
+ }
2452
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.0", ngImport: i0, type: DragDropDirective, decorators: [{
2453
+ type: Directive,
2454
+ args: [{
2455
+ selector: '[dragDrop]'
2456
+ }]
2457
+ }], ctorParameters: function () { return []; }, propDecorators: { files: [{
2458
+ type: Output
2459
+ }], onDragOver: [{
2460
+ type: HostListener,
2461
+ args: ['dragover', ['$event']]
2462
+ }], onDragLeave: [{
2463
+ type: HostListener,
2464
+ args: ['dragleave', ['$event']]
2465
+ }], onDrop: [{
2466
+ type: HostListener,
2467
+ args: ['drop', ['$event']]
2468
+ }] } });
2469
+
2470
+ /**
2471
+ * A directive that only allows number inputs.
2472
+ */
2473
+ class NumberDirective {
2474
+ el;
2475
+ constructor(el) {
2476
+ this.el = el;
2184
2477
  }
2185
2478
  /**
2186
- * Updates the entity with a normal json body in contrast to updating it with form data when the entity contains files.
2479
+ * Prevents the default event when a key is pressed that is not a valid number, eg. 'A', 'B', 'C', 'D' etc.
2187
2480
  *
2188
- * @param body - The body of the Request. Has already removed all unnecessary values.
2189
- * @param id - The id of the entity to update.
2481
+ * @param e - The keydown event from the user.
2190
2482
  */
2191
- async updateWithJson(body, id) {
2192
- const updatedEntity = await firstValueFrom(this.http.patch(`${this.baseUrl}/${id}`, LodashUtilities.omitBy(body, LodashUtilities.isNil)));
2193
- if (!updatedEntity) {
2194
- // eslint-disable-next-line no-console
2195
- console.warn('The updated entity was not returned in the response. Applying the changes from the request body.');
2196
- const foundEntity = this.entities[this.entities.findIndex(e => e[this.idKey] === id)];
2197
- for (const key in body) {
2198
- foundEntity[key]
2199
- = body[key];
2200
- }
2201
- this.entitiesSubject.next(this.entities);
2483
+ onKeyDown(e) {
2484
+ if (!isNaN(parseInt(e.key))
2485
+ || ['.', ',', 'Escape', 'Enter', 'Delete', 'Backspace', 'Home', 'End', 'Left', 'Right', 'Tab'].includes(e.key)
2486
+ || (e.ctrlKey || e.metaKey) // && ['a', 'c', 'v', 'x', 'z'].includes(e.key)
2487
+ ) {
2202
2488
  return;
2203
2489
  }
2204
- this.entities[this.entities.findIndex(e => e[this.idKey] === id)] = updatedEntity;
2205
- this.entitiesSubject.next(this.entities);
2490
+ e.preventDefault();
2206
2491
  }
2492
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.0", ngImport: i0, type: NumberDirective, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive });
2493
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.0", type: NumberDirective, isStandalone: true, selector: "[number]", host: { listeners: { "keydown": "onKeyDown($event)" } }, ngImport: i0 });
2494
+ }
2495
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.0", ngImport: i0, type: NumberDirective, decorators: [{
2496
+ type: Directive,
2497
+ args: [{
2498
+ selector: '[number]',
2499
+ standalone: true
2500
+ }]
2501
+ }], ctorParameters: function () { return [{ type: i0.ElementRef }]; }, propDecorators: { onKeyDown: [{
2502
+ type: HostListener,
2503
+ args: ['keydown', ['$event']]
2504
+ }] } });
2505
+
2506
+ /**
2507
+ * A directive that validates if a given password matches the control value.
2508
+ */
2509
+ class PasswordMatchValidatorDirective {
2207
2510
  /**
2208
- * Deletes a specific Entity.
2209
- *
2210
- * @param entity - The entity to delete.
2511
+ * The password that the control value should be matched against.
2211
2512
  */
2212
- async delete(entity) {
2213
- await firstValueFrom(this.http.delete(`${this.baseUrl}/${entity[this.idKey]}`));
2214
- // the == comparison instead of === is to catch ids that are numbers.
2215
- this.entities.splice(this.entities.findIndex(e => e[this.idKey] === entity[this.idKey]), 1);
2216
- this.entitiesSubject.next(this.entities);
2513
+ passwordMatch;
2514
+ // eslint-disable-next-line jsdoc/require-jsdoc
2515
+ validate(control) {
2516
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
2517
+ return this.passwordMatch == control.value ? null : { passwordMatch: { value: control.value } };
2217
2518
  }
2519
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.0", ngImport: i0, type: PasswordMatchValidatorDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
2520
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.0", type: PasswordMatchValidatorDirective, isStandalone: true, selector: "[passwordMatch]", inputs: { passwordMatch: "passwordMatch" }, providers: [{ provide: NG_VALIDATORS, useExisting: PasswordMatchValidatorDirective, multi: true }], ngImport: i0 });
2218
2521
  }
2522
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.0", ngImport: i0, type: PasswordMatchValidatorDirective, decorators: [{
2523
+ type: Directive,
2524
+ args: [{
2525
+ selector: '[passwordMatch]',
2526
+ providers: [{ provide: NG_VALIDATORS, useExisting: PasswordMatchValidatorDirective, multi: true }],
2527
+ standalone: true
2528
+ }]
2529
+ }], propDecorators: { passwordMatch: [{
2530
+ type: Input
2531
+ }] } });
2219
2532
 
2220
2533
  /* eslint-disable @angular-eslint/component-selector */
2221
2534
  /**
@@ -2361,6 +2674,7 @@ class NgxMatEntityBaseInputComponent {
2361
2674
  // eslint-disable-next-line jsdoc/require-jsdoc
2362
2675
  set propertyValue(value) {
2363
2676
  this.entity[this.key] = value;
2677
+ this.metadata.change?.(this.entity);
2364
2678
  }
2365
2679
  /**
2366
2680
  * The metadata of the property.
@@ -2573,7 +2887,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.0", ngImpor
2573
2887
 
2574
2888
  /* eslint-disable jsdoc/require-jsdoc */
2575
2889
  class ArrayStringChipsInputComponent extends NgxMatEntityBaseInputComponent {
2576
- uuid2 = UUIDUtilities$1.create();
2890
+ uuid2 = UUIDUtilities.create();
2577
2891
  chipsInput = '';
2578
2892
  /**
2579
2893
  * Handles adding strings to the chipsArray.
@@ -2676,12 +2990,15 @@ class BooleanCheckboxInputComponent extends NgxMatEntityBaseInputComponent {
2676
2990
  super.ngOnInit();
2677
2991
  this.propertyValue = this.propertyValue ?? false;
2678
2992
  }
2993
+ updatePropertyValue() {
2994
+ this.propertyValue = this.propertyValue != null ? !this.propertyValue : true;
2995
+ }
2679
2996
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.0", ngImport: i0, type: BooleanCheckboxInputComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
2680
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.0", type: BooleanCheckboxInputComponent, selector: "boolean-checkbox-input", usesInheritance: true, ngImport: i0, template: "<mat-form-field>\n <mat-label>{{metadata.displayName}}</mat-label>\n <mat-checkbox\n color=\"primary\"\n (ngModelChange)=\"emitChange()\"\n (click)=\"model.control.markAsTouched()\"\n [(ngModel)]=\"propertyValue\"\n [name]=\"key.toString() + uuid\"\n [disabled]=\"isReadOnly\"\n [class.disabled]=\"isReadOnly\"\n [class.mat-checkbox-disabled]=\"false\"\n >\n </mat-checkbox>\n <!-- hidden input is needed so that the checkbox can be used inside a mat-form-field -->\n <input matInput hidden\n [(ngModel)]=\"propertyValue\"\n [name]=\"key.toString() + 'Helper' + uuid\"\n #model=\"ngModel\"\n [pattern]=\"metadata.required(entity) ? 'true' : '[\\\\s\\\\S]*'\"\n [required]=\"metadata.required(entity)\"\n [disabled]=\"isReadOnly\"\n >\n <mat-error>{{getValidationErrorMessage(model)}}</mat-error>\n</mat-form-field>", styles: ["mat-checkbox{margin-top:-10px;margin-bottom:-10px}mat-form-field{width:100%}\n"], dependencies: [{ kind: "directive", type: i2$1.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "component", type: i2$2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$2.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$2.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i3.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: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i3.PatternValidator, selector: "[pattern][formControlName],[pattern][formControl],[pattern][ngModel]", inputs: ["pattern"] }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i4.MatCheckbox, selector: "mat-checkbox", inputs: ["disableRipple", "color", "tabIndex"], exportAs: ["matCheckbox"] }] });
2997
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.0", type: BooleanCheckboxInputComponent, selector: "boolean-checkbox-input", usesInheritance: true, ngImport: i0, template: "<mat-form-field>\n <mat-label>{{metadata.displayName}}</mat-label>\n <mat-checkbox\n color=\"primary\"\n (click)=\"model.control.markAsTouched(); updatePropertyValue(); emitChange();\"\n [disabled]=\"isReadOnly\"\n [class.disabled]=\"isReadOnly\"\n [class.mat-checkbox-disabled]=\"false\"\n [checked]=\"propertyValue\"\n >\n </mat-checkbox>\n <!-- hidden input is needed so that the checkbox can be used inside a mat-form-field -->\n <input matInput hidden\n [(ngModel)]=\"propertyValue\"\n [name]=\"key.toString() + uuid\"\n #model=\"ngModel\"\n [pattern]=\"metadata.required(entity) ? 'true' : '[\\\\s\\\\S]*'\"\n [required]=\"metadata.required(entity)\"\n [disabled]=\"isReadOnly\"\n >\n <mat-error>{{getValidationErrorMessage(model)}}</mat-error>\n</mat-form-field>", styles: ["mat-checkbox{margin-top:-10px;margin-bottom:-10px}mat-form-field{width:100%}\n"], dependencies: [{ kind: "directive", type: i2$1.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "component", type: i2$2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$2.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$2.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i3.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: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i3.PatternValidator, selector: "[pattern][formControlName],[pattern][formControl],[pattern][ngModel]", inputs: ["pattern"] }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i4.MatCheckbox, selector: "mat-checkbox", inputs: ["disableRipple", "color", "tabIndex"], exportAs: ["matCheckbox"] }] });
2681
2998
  }
2682
2999
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.0", ngImport: i0, type: BooleanCheckboxInputComponent, decorators: [{
2683
3000
  type: Component,
2684
- args: [{ selector: 'boolean-checkbox-input', template: "<mat-form-field>\n <mat-label>{{metadata.displayName}}</mat-label>\n <mat-checkbox\n color=\"primary\"\n (ngModelChange)=\"emitChange()\"\n (click)=\"model.control.markAsTouched()\"\n [(ngModel)]=\"propertyValue\"\n [name]=\"key.toString() + uuid\"\n [disabled]=\"isReadOnly\"\n [class.disabled]=\"isReadOnly\"\n [class.mat-checkbox-disabled]=\"false\"\n >\n </mat-checkbox>\n <!-- hidden input is needed so that the checkbox can be used inside a mat-form-field -->\n <input matInput hidden\n [(ngModel)]=\"propertyValue\"\n [name]=\"key.toString() + 'Helper' + uuid\"\n #model=\"ngModel\"\n [pattern]=\"metadata.required(entity) ? 'true' : '[\\\\s\\\\S]*'\"\n [required]=\"metadata.required(entity)\"\n [disabled]=\"isReadOnly\"\n >\n <mat-error>{{getValidationErrorMessage(model)}}</mat-error>\n</mat-form-field>", styles: ["mat-checkbox{margin-top:-10px;margin-bottom:-10px}mat-form-field{width:100%}\n"] }]
3001
+ args: [{ selector: 'boolean-checkbox-input', template: "<mat-form-field>\n <mat-label>{{metadata.displayName}}</mat-label>\n <mat-checkbox\n color=\"primary\"\n (click)=\"model.control.markAsTouched(); updatePropertyValue(); emitChange();\"\n [disabled]=\"isReadOnly\"\n [class.disabled]=\"isReadOnly\"\n [class.mat-checkbox-disabled]=\"false\"\n [checked]=\"propertyValue\"\n >\n </mat-checkbox>\n <!-- hidden input is needed so that the checkbox can be used inside a mat-form-field -->\n <input matInput hidden\n [(ngModel)]=\"propertyValue\"\n [name]=\"key.toString() + uuid\"\n #model=\"ngModel\"\n [pattern]=\"metadata.required(entity) ? 'true' : '[\\\\s\\\\S]*'\"\n [required]=\"metadata.required(entity)\"\n [disabled]=\"isReadOnly\"\n >\n <mat-error>{{getValidationErrorMessage(model)}}</mat-error>\n</mat-form-field>", styles: ["mat-checkbox{margin-top:-10px;margin-bottom:-10px}mat-form-field{width:100%}\n"] }]
2685
3002
  }] });
2686
3003
 
2687
3004
  /* eslint-disable jsdoc/require-jsdoc */
@@ -2700,12 +3017,15 @@ class BooleanToggleInputComponent extends NgxMatEntityBaseInputComponent {
2700
3017
  super.ngOnInit();
2701
3018
  this.propertyValue = this.propertyValue ?? false;
2702
3019
  }
3020
+ updatePropertyValue() {
3021
+ this.propertyValue = this.propertyValue != null ? !this.propertyValue : true;
3022
+ }
2703
3023
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.0", ngImport: i0, type: BooleanToggleInputComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
2704
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.0", type: BooleanToggleInputComponent, selector: "boolean-toggle-input", usesInheritance: true, ngImport: i0, template: "<mat-form-field>\n <mat-label>{{metadata.displayName}}</mat-label>\n <mat-slide-toggle\n color=\"primary\"\n (click)=\"model.control.markAsTouched()\"\n [(ngModel)]=\"propertyValue\"\n [name]=\"key.toString() + uuid\"\n [disabled]=\"isReadOnly\"\n >\n </mat-slide-toggle>\n <!-- hidden input is needed so that the toggle can be used inside a mat-form-field -->\n <input matInput hidden\n [(ngModel)]=\"propertyValue\"\n [name]=\"key.toString() + 'Helper' + uuid\"\n #model=\"ngModel\"\n [pattern]=\"metadata.required(entity) ? 'true' : '[\\\\s\\\\S]*'\"\n [required]=\"metadata.required(entity)\"\n (ngModelChange)=\"emitChange()\"\n >\n <mat-error>{{getValidationErrorMessage(model)}}</mat-error>\n</mat-form-field>", styles: ["mat-form-field{width:100%}\n"], dependencies: [{ kind: "directive", type: i2$1.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "component", type: i2$2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$2.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$2.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i3.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: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i3.PatternValidator, selector: "[pattern][formControlName],[pattern][formControl],[pattern][ngModel]", inputs: ["pattern"] }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i4$3.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["disabled", "disableRipple", "color", "tabIndex"], exportAs: ["matSlideToggle"] }] });
3024
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.0", type: BooleanToggleInputComponent, selector: "boolean-toggle-input", usesInheritance: true, ngImport: i0, template: "<mat-form-field>\n <mat-label>{{metadata.displayName}}</mat-label>\n <mat-slide-toggle\n color=\"primary\"\n (click)=\"model.control.markAsTouched(); updatePropertyValue(); emitChange();\"\n [disabled]=\"isReadOnly\"\n [checked]=\"propertyValue\"\n >\n </mat-slide-toggle>\n <!-- hidden input is needed so that the toggle can be used inside a mat-form-field -->\n <input matInput hidden\n [(ngModel)]=\"propertyValue\"\n [name]=\"key.toString() + uuid\"\n #model=\"ngModel\"\n [pattern]=\"metadata.required(entity) ? 'true' : '[\\\\s\\\\S]*'\"\n [required]=\"metadata.required(entity)\"\n [disabled]=\"isReadOnly\"\n (ngModelChange)=\"emitChange()\"\n >\n <mat-error>{{getValidationErrorMessage(model)}}</mat-error>\n</mat-form-field>", styles: ["mat-form-field{width:100%}\n"], dependencies: [{ kind: "directive", type: i2$1.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "component", type: i2$2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$2.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$2.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i3.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: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i3.PatternValidator, selector: "[pattern][formControlName],[pattern][formControl],[pattern][ngModel]", inputs: ["pattern"] }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i4$3.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["disabled", "disableRipple", "color", "tabIndex"], exportAs: ["matSlideToggle"] }] });
2705
3025
  }
2706
3026
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.0", ngImport: i0, type: BooleanToggleInputComponent, decorators: [{
2707
3027
  type: Component,
2708
- args: [{ selector: 'boolean-toggle-input', template: "<mat-form-field>\n <mat-label>{{metadata.displayName}}</mat-label>\n <mat-slide-toggle\n color=\"primary\"\n (click)=\"model.control.markAsTouched()\"\n [(ngModel)]=\"propertyValue\"\n [name]=\"key.toString() + uuid\"\n [disabled]=\"isReadOnly\"\n >\n </mat-slide-toggle>\n <!-- hidden input is needed so that the toggle can be used inside a mat-form-field -->\n <input matInput hidden\n [(ngModel)]=\"propertyValue\"\n [name]=\"key.toString() + 'Helper' + uuid\"\n #model=\"ngModel\"\n [pattern]=\"metadata.required(entity) ? 'true' : '[\\\\s\\\\S]*'\"\n [required]=\"metadata.required(entity)\"\n (ngModelChange)=\"emitChange()\"\n >\n <mat-error>{{getValidationErrorMessage(model)}}</mat-error>\n</mat-form-field>", styles: ["mat-form-field{width:100%}\n"] }]
3028
+ args: [{ selector: 'boolean-toggle-input', template: "<mat-form-field>\n <mat-label>{{metadata.displayName}}</mat-label>\n <mat-slide-toggle\n color=\"primary\"\n (click)=\"model.control.markAsTouched(); updatePropertyValue(); emitChange();\"\n [disabled]=\"isReadOnly\"\n [checked]=\"propertyValue\"\n >\n </mat-slide-toggle>\n <!-- hidden input is needed so that the toggle can be used inside a mat-form-field -->\n <input matInput hidden\n [(ngModel)]=\"propertyValue\"\n [name]=\"key.toString() + uuid\"\n #model=\"ngModel\"\n [pattern]=\"metadata.required(entity) ? 'true' : '[\\\\s\\\\S]*'\"\n [required]=\"metadata.required(entity)\"\n [disabled]=\"isReadOnly\"\n (ngModelChange)=\"emitChange()\"\n >\n <mat-error>{{getValidationErrorMessage(model)}}</mat-error>\n</mat-form-field>", styles: ["mat-form-field{width:100%}\n"] }]
2709
3029
  }] });
2710
3030
 
2711
3031
  /* eslint-disable jsdoc/require-jsdoc */
@@ -2788,6 +3108,8 @@ class DateRangeInputComponent extends NgxMatEntityBaseInputComponent {
2788
3108
  this.dateRange.values = values.length ? values : undefined;
2789
3109
  }
2790
3110
  else {
3111
+ this.dateRange.start = undefined;
3112
+ this.dateRange.end = undefined;
2791
3113
  this.dateRange.values = undefined;
2792
3114
  }
2793
3115
  this.propertyValue = this.dateRange;
@@ -2857,66 +3179,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.0", ngImpor
2857
3179
  args: [{ selector: 'date-time-input', template: "<div class=\"date-time\">\n <mat-form-field>\n <mat-label>{{metadata.displayName}}</mat-label>\n <input\n matInput\n [(ngModel)]=\"propertyValue\"\n [name]=\"key.toString() + uuid\"\n #model=\"ngModel\"\n [matDatepicker]=\"picker\"\n [required]=\"metadata.required(entity)\"\n [min]=\"metadata.minDate ? metadata.minDate(propertyValue) : undefined\"\n [max]=\"metadata.maxDate ? metadata.maxDate(propertyValue) : undefined\"\n [matDatepickerFilter]=\"metadata.filterDate ?? defaultDateFilter\"\n (dateChange)=\"setTime()\"\n [disabled]=\"isReadOnly\"\n >\n <mat-datepicker-toggle matSuffix [for]=\"picker\"></mat-datepicker-toggle>\n <mat-datepicker #picker></mat-datepicker>\n <mat-error>{{getValidationErrorMessage(model)}}</mat-error>\n </mat-form-field>\n <mat-form-field class=\"timepicker\">\n <mat-label>{{metadata.timeDisplayName}}</mat-label>\n <mat-select\n [(ngModel)]=\"time\"\n [name]=\"key.toString() + 'time' + uuid\"\n #timeModel=\"ngModel\"\n [required]=\"metadata.required(entity)\"\n [compareWith]=\"compareTimes\"\n [disabled]=\"isReadOnly\"\n (selectionChange)=\"setTime()\"\n >\n <mat-option *ngFor=\"let validTime of DateUtilities.getValidTimesForDropdown(\n metadata.times,\n propertyValue,\n metadata.minTime,\n metadata.maxTime,\n metadata.filterTime\n )\"\n [value]=\"validTime.value\"\n >\n {{validTime.displayName}}\n </mat-option>\n </mat-select>\n <mat-error>{{getValidationErrorMessage(timeModel)}}</mat-error>\n </mat-form-field>\n</div>", styles: ["mat-form-field{width:100%}.date-time{display:flex;align-items:baseline}.date-time .timepicker{margin-left:10px}\n"] }]
2858
3180
  }] });
2859
3181
 
2860
- /**
2861
- * Adds drag and drop functionality to an element.
2862
- */
2863
- class DragDropDirective {
2864
- /**
2865
- * Emits the dropped files to the parent.
2866
- */
2867
- files = new EventEmitter();
2868
- constructor() { }
2869
- /**
2870
- * Prevents the event default.
2871
- *
2872
- * @param evt - The Event when dragged files hover over the parent.
2873
- */
2874
- onDragOver(evt) {
2875
- evt.preventDefault();
2876
- evt.stopPropagation();
2877
- }
2878
- /**
2879
- * Prevents the event default.
2880
- *
2881
- * @param evt - The Event when dragged files leave the parent.
2882
- */
2883
- onDragLeave(evt) {
2884
- evt.preventDefault();
2885
- evt.stopPropagation();
2886
- }
2887
- /**
2888
- * Prevents the event default and emits the dropped files with the output.
2889
- *
2890
- * @param evt - The Event when files are dropped.
2891
- */
2892
- onDrop(evt) {
2893
- evt.preventDefault();
2894
- evt.stopPropagation();
2895
- if (evt.dataTransfer && evt.dataTransfer.files.length > 0) {
2896
- this.files.emit(Array.from(evt.dataTransfer.files));
2897
- }
2898
- }
2899
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.0", ngImport: i0, type: DragDropDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
2900
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.0", type: DragDropDirective, selector: "[dragDrop]", outputs: { files: "files" }, host: { listeners: { "dragover": "onDragOver($event)", "dragleave": "onDragLeave($event)", "drop": "onDrop($event)" } }, ngImport: i0 });
2901
- }
2902
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.0", ngImport: i0, type: DragDropDirective, decorators: [{
2903
- type: Directive,
2904
- args: [{
2905
- selector: '[dragDrop]'
2906
- }]
2907
- }], ctorParameters: function () { return []; }, propDecorators: { files: [{
2908
- type: Output
2909
- }], onDragOver: [{
2910
- type: HostListener,
2911
- args: ['dragover', ['$event']]
2912
- }], onDragLeave: [{
2913
- type: HostListener,
2914
- args: ['dragleave', ['$event']]
2915
- }], onDrop: [{
2916
- type: HostListener,
2917
- args: ['drop', ['$event']]
2918
- }] } });
2919
-
2920
3182
  /* eslint-disable jsdoc/require-jsdoc */
2921
3183
  class FileInputComponent {
2922
3184
  dialog;
@@ -3205,11 +3467,11 @@ class FileImageInputComponent extends NgxMatEntityBaseInputComponent {
3205
3467
  this.imageIndex = index;
3206
3468
  }
3207
3469
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.0", ngImport: i0, type: FileImageInputComponent, deps: [{ token: i2.HttpClient }], target: i0.ɵɵFactoryTarget.Component });
3208
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.0", type: FileImageInputComponent, selector: "file-image-input", usesInheritance: true, ngImport: i0, template: "<div *ngIf=\"!metadata.dragAndDrop && !metadata.preview\">\n <file-input\n (fileDataChangeEvent)=\"refreshFileData($event)\"\n [propertyValue]=\"propertyValue\"\n [metadata]=\"metadata\"\n [getValidationErrorMessage]=\"getValidationErrorMessage\"\n [isReadOnly]=\"isReadOnly\"\n [entity]=\"entity\"\n [key]=\"key\"\n >\n </file-input>\n</div>\n\n<div *ngIf=\"metadata.dragAndDrop || metadata.preview\" class=\"file-input mat-elevation-z8\">\n <file-input\n (fileDataChangeEvent)=\"refreshFileData($event)\"\n [propertyValue]=\"propertyValue\"\n [metadata]=\"metadata\"\n [getValidationErrorMessage]=\"getValidationErrorMessage\"\n [isReadOnly]=\"isReadOnly\"\n [entity]=\"entity\"\n [key]=\"key\"\n >\n </file-input>\n\n <div class=\"image-preview\" *ngIf=\"metadata.preview && metadata.multiple\">\n <i (click)=\"prev()\" [class.disabled]=\"imageIndex === 0\" class=\"prev-button fa-solid fa-angle-left\"></i>\n <img *ngIf=\"multiPreviewImages?.[imageIndex]\" class=\"mat-elevation-z2\" [src]=\"multiPreviewImages?.[imageIndex]\">\n <img *ngIf=\"!multiPreviewImages?.[imageIndex]\" class=\"mat-elevation-z2\" [src]=\"metadata.previewPlaceholderUrl ?? placeHolder\">\n <i (click)=\"next()\"\n [class.disabled]=\"!multiPreviewImages || !multiPreviewImages.length || imageIndex === (multiPreviewImages.length - 1)\"\n class=\"next-button fa-solid fa-angle-right\"\n >\n </i>\n </div>\n <div class=\"preview-nav\" *ngIf=\"metadata.preview && metadata.multiple\">\n <button type=\"button\" (click)=\"setIndex(imageIndex-4)\" mat-icon-button *ngIf=\"\n this.multiPreviewImages\n && multiPreviewImages[imageIndex-4]\n && imageIndex === (this.multiPreviewImages.length - 1)\"\n >\n <span class=\"dot\"></span>\n <span class=\"image-index\">{{imageIndex - 3}}</span>\n </button>\n <!-- eslint-disable-next-line @angular-eslint/template/conditional-complexity -->\n <button type=\"button\" (click)=\"setIndex(imageIndex-3)\" mat-icon-button *ngIf=\"this.multiPreviewImages\n && multiPreviewImages[imageIndex-3]\n && (\n imageIndex === (this.multiPreviewImages.length - 2)\n || imageIndex === (this.multiPreviewImages.length - 1)\n )\"\n >\n <span class=\"dot\"></span>\n <span class=\"image-index\">{{imageIndex - 2}}</span>\n </button>\n <button type=\"button\" (click)=\"setIndex(imageIndex-2)\" mat-icon-button *ngIf=\"multiPreviewImages?.[imageIndex-2]\">\n <span class=\"dot\"></span>\n <span class=\"image-index\">{{imageIndex - 1}}</span>\n </button>\n <button type=\"button\" (click)=\"setIndex(imageIndex-1)\" mat-icon-button *ngIf=\"multiPreviewImages?.[imageIndex-1]\">\n <i class=\"dot\"></i>\n <span class=\"image-index\">{{imageIndex}}</span>\n </button>\n <button type=\"button\" mat-icon-button disabled>\n <i class=\"dot selected\"></i>\n <span class=\"image-index\">{{imageIndex + 1}}</span>\n </button>\n <button type=\"button\" (click)=\"setIndex(imageIndex+1)\" mat-icon-button *ngIf=\"multiPreviewImages?.[imageIndex+1]\">\n <span class=\"dot\"></span>\n <span class=\"image-index\">{{imageIndex + 2}}</span>\n </button>\n <button type=\"button\" (click)=\"setIndex(imageIndex+2)\" mat-icon-button *ngIf=\"multiPreviewImages?.[imageIndex+2]\">\n <span class=\"dot\"></span>\n <span class=\"image-index\">{{imageIndex + 3}}</span>\n </button>\n <button type=\"button\" (click)=\"setIndex(imageIndex+3)\" mat-icon-button *ngIf=\"multiPreviewImages?.[imageIndex+3] && imageIndex <= 1\">\n <span class=\"dot\"></span>\n <span class=\"image-index\">{{imageIndex + 4}}</span>\n </button>\n <button type=\"button\" (click)=\"setIndex(imageIndex+4)\" mat-icon-button *ngIf=\"multiPreviewImages?.[imageIndex+4] && imageIndex === 0\">\n <span class=\"dot\"></span>\n <span class=\"image-index\">{{imageIndex + 5}}</span>\n </button>\n </div>\n\n <div class=\"image-preview\" *ngIf=\"metadata.preview && !metadata.multiple\">\n <i class=\"prev-button disabled fa-solid fa-angle-left\"></i>\n <img class=\"mat-elevation-z2\" [src]=\"singlePreviewImage ?? metadata.previewPlaceholderUrl ?? placeHolder\">\n <i class=\"next-button disabled fa-solid fa-angle-right\"></i>\n </div>\n <div class=\"preview-nav\" *ngIf=\"metadata.preview && !metadata.multiple\">\n <button type=\"button\" disabled mat-icon-button>\n <span class=\"dot selected\"></span>\n <span class=\"image-index\">1</span>\n </button>\n </div>\n</div>", styles: [".file-input{margin-top:15px;margin-bottom:15px;padding:15px;border-radius:5px}.image-preview{height:250px;display:flex;align-items:center;padding-top:15px;padding-bottom:15px}.image-preview .prev-button{font-size:100px;margin-left:5px;color:#0000008a}.image-preview .next-button{font-size:100px;margin-right:5px;color:#0000008a}.image-preview .prev-button:hover,.image-preview .next-button:hover{cursor:pointer;color:#000000b3;transition:all .5s ease}.image-preview .prev-button.disabled,.image-preview .next-button.disabled{color:#00000042}.image-preview .prev-button.disabled:hover,.image-preview .next-button.disabled:hover{color:#00000042;transition:none;cursor:default}.image-preview img{max-width:calc(100% - 100px);max-height:100%;margin-left:auto;margin-right:auto;border-radius:3px}.preview-nav{text-align:center}.preview-nav button{display:inline-block;width:18px;height:18px;margin-left:5px;margin-right:5px}.preview-nav button .dot{height:100%;width:100%;background-color:#00000061;border-radius:50%;display:block}.preview-nav button .dot.selected{background-color:#0000008a}.preview-nav button .image-index{position:absolute;height:100%;width:100%;display:block;top:-11.5px;color:#fff}.preview-nav button:hover .dot{background-color:#0000008a;transition:all .3s ease}\n"], dependencies: [{ kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i5.MatIconButton, selector: "button[mat-icon-button]", inputs: ["disabled", "disableRipple", "color"], exportAs: ["matButton"] }, { kind: "component", type: FileInputComponent, selector: "file-input", inputs: ["propertyValue", "entity", "key", "metadata", "getValidationErrorMessage", "isReadOnly"], outputs: ["fileDataChangeEvent"] }] });
3470
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.0", type: FileImageInputComponent, selector: "file-image-input", usesInheritance: true, ngImport: i0, template: "<div *ngIf=\"!metadata.dragAndDrop && !metadata.preview\">\n <file-input\n (fileDataChangeEvent)=\"refreshFileData($event)\"\n [propertyValue]=\"propertyValue\"\n [metadata]=\"metadata\"\n [getValidationErrorMessage]=\"getValidationErrorMessage\"\n [isReadOnly]=\"isReadOnly\"\n [entity]=\"entity\"\n [key]=\"key\"\n >\n </file-input>\n</div>\n\n<div *ngIf=\"metadata.dragAndDrop || metadata.preview\" class=\"file-input mat-elevation-z8\">\n <file-input\n (fileDataChangeEvent)=\"refreshFileData($event)\"\n [propertyValue]=\"propertyValue\"\n [metadata]=\"metadata\"\n [getValidationErrorMessage]=\"getValidationErrorMessage\"\n [isReadOnly]=\"isReadOnly\"\n [entity]=\"entity\"\n [key]=\"key\"\n >\n </file-input>\n\n <div class=\"image-preview\" *ngIf=\"metadata.preview && metadata.multiple\">\n <i (click)=\"prev()\" [class.disabled]=\"imageIndex === 0\" class=\"prev-button fa-solid fa-angle-left\"></i>\n <img *ngIf=\"multiPreviewImages?.[imageIndex]\" class=\"mat-elevation-z2\" [src]=\"multiPreviewImages?.[imageIndex]\">\n <img *ngIf=\"!multiPreviewImages?.[imageIndex]\" class=\"mat-elevation-z2\" [src]=\"metadata.previewPlaceholderUrl ?? placeHolder\">\n <i (click)=\"next()\"\n [class.disabled]=\"!multiPreviewImages || !multiPreviewImages.length || imageIndex === (multiPreviewImages.length - 1)\"\n class=\"next-button fa-solid fa-angle-right\"\n >\n </i>\n </div>\n\n <div class=\"preview-nav\" *ngIf=\"metadata.preview && metadata.multiple\">\n <button type=\"button\" (click)=\"setIndex(imageIndex-4)\" mat-icon-button *ngIf=\"\n this.multiPreviewImages\n && multiPreviewImages[imageIndex-4]\n && imageIndex === (this.multiPreviewImages.length - 1)\"\n >\n <div class=\"dot\">\n <div class=\"image-index\">{{imageIndex - 3}}</div>\n </div>\n </button>\n <!-- eslint-disable-next-line @angular-eslint/template/conditional-complexity -->\n <button type=\"button\" (click)=\"setIndex(imageIndex-3)\" mat-icon-button *ngIf=\"this.multiPreviewImages\n && multiPreviewImages[imageIndex-3]\n && (\n imageIndex === (this.multiPreviewImages.length - 2)\n || imageIndex === (this.multiPreviewImages.length - 1)\n )\"\n >\n <div class=\"dot\">\n <div class=\"image-index\">{{imageIndex - 2}}</div>\n </div>\n </button>\n <button type=\"button\" (click)=\"setIndex(imageIndex-2)\" mat-icon-button *ngIf=\"multiPreviewImages?.[imageIndex-2]\">\n <div class=\"dot\">\n <div class=\"image-index\">{{imageIndex - 1}}</div>\n </div>\n </button>\n <button type=\"button\" (click)=\"setIndex(imageIndex-1)\" mat-icon-button *ngIf=\"multiPreviewImages?.[imageIndex-1]\">\n <div class=\"dot\">\n <div class=\"image-index\">{{imageIndex}}</div>\n </div>\n </button>\n <button type=\"button\" mat-icon-button disabled>\n <div class=\"dot selected\">\n <div class=\"image-index\">{{imageIndex + 1}}</div>\n </div>\n </button>\n <button type=\"button\" (click)=\"setIndex(imageIndex+1)\" mat-icon-button *ngIf=\"multiPreviewImages?.[imageIndex+1]\">\n <div class=\"dot\">\n <div class=\"image-index\">{{imageIndex + 2}}</div>\n </div>\n </button>\n <button type=\"button\" (click)=\"setIndex(imageIndex+2)\" mat-icon-button *ngIf=\"multiPreviewImages?.[imageIndex+2]\">\n <div class=\"dot\">\n <div class=\"image-index\">{{imageIndex + 3}}</div>\n </div>\n </button>\n <button type=\"button\" (click)=\"setIndex(imageIndex+3)\" mat-icon-button *ngIf=\"multiPreviewImages?.[imageIndex+3] && imageIndex <= 1\">\n <div class=\"dot\">\n <div class=\"image-index\">{{imageIndex + 4}}</div>\n </div>\n </button>\n <button type=\"button\" (click)=\"setIndex(imageIndex+4)\" mat-icon-button *ngIf=\"multiPreviewImages?.[imageIndex+4] && imageIndex === 0\">\n <div class=\"dot\">\n <div class=\"image-index\">{{imageIndex + 5}}</div>\n </div>\n </button>\n </div>\n\n <div class=\"image-preview\" *ngIf=\"metadata.preview && !metadata.multiple\">\n <i class=\"prev-button disabled fa-solid fa-angle-left\"></i>\n <img class=\"mat-elevation-z2\" [src]=\"singlePreviewImage ?? metadata.previewPlaceholderUrl ?? placeHolder\">\n <i class=\"next-button disabled fa-solid fa-angle-right\"></i>\n </div>\n\n <div class=\"preview-nav\" *ngIf=\"metadata.preview && !metadata.multiple\">\n <button type=\"button\" disabled mat-icon-button>\n <div class=\"dot selected\">\n <div class=\"image-index\">1</div>\n </div>\n </button>\n </div>\n</div>", styles: [".file-input{margin-top:15px;margin-bottom:15px;padding:15px;border-radius:5px}.image-preview{height:250px;display:flex;align-items:center;padding-top:15px;padding-bottom:15px}.image-preview .prev-button{font-size:100px;margin-left:5px;color:#0000008a}.image-preview .next-button{font-size:100px;margin-right:5px;color:#0000008a}.image-preview .prev-button:hover,.image-preview .next-button:hover{cursor:pointer;color:#000000b3;transition:all .5s ease}.image-preview .prev-button.disabled,.image-preview .next-button.disabled{color:#00000042}.image-preview .prev-button.disabled:hover,.image-preview .next-button.disabled:hover{color:#00000042;transition:none;cursor:default}.image-preview img{max-width:calc(100% - 100px);max-height:100%;margin-left:auto;margin-right:auto;border-radius:3px}.preview-nav{display:flex;align-items:center;justify-content:center;column-gap:5px}.preview-nav button{display:flex;align-items:center;justify-content:center;padding:0;height:auto;width:auto}.preview-nav button .dot{display:flex;align-items:center;justify-content:center;height:25px;width:25px;min-width:25px;background-color:#00000061;border-radius:50%}.preview-nav button .dot .image-index{color:#fff;font-size:20px;font-weight:600}.preview-nav button .dot.selected{background-color:#0000008a}.preview-nav button:hover .dot{background-color:#0000008a;transition:all .3s ease}\n"], dependencies: [{ kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i5.MatIconButton, selector: "button[mat-icon-button]", inputs: ["disabled", "disableRipple", "color"], exportAs: ["matButton"] }, { kind: "component", type: FileInputComponent, selector: "file-input", inputs: ["propertyValue", "entity", "key", "metadata", "getValidationErrorMessage", "isReadOnly"], outputs: ["fileDataChangeEvent"] }] });
3209
3471
  }
3210
3472
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.0", ngImport: i0, type: FileImageInputComponent, decorators: [{
3211
3473
  type: Component,
3212
- args: [{ selector: 'file-image-input', template: "<div *ngIf=\"!metadata.dragAndDrop && !metadata.preview\">\n <file-input\n (fileDataChangeEvent)=\"refreshFileData($event)\"\n [propertyValue]=\"propertyValue\"\n [metadata]=\"metadata\"\n [getValidationErrorMessage]=\"getValidationErrorMessage\"\n [isReadOnly]=\"isReadOnly\"\n [entity]=\"entity\"\n [key]=\"key\"\n >\n </file-input>\n</div>\n\n<div *ngIf=\"metadata.dragAndDrop || metadata.preview\" class=\"file-input mat-elevation-z8\">\n <file-input\n (fileDataChangeEvent)=\"refreshFileData($event)\"\n [propertyValue]=\"propertyValue\"\n [metadata]=\"metadata\"\n [getValidationErrorMessage]=\"getValidationErrorMessage\"\n [isReadOnly]=\"isReadOnly\"\n [entity]=\"entity\"\n [key]=\"key\"\n >\n </file-input>\n\n <div class=\"image-preview\" *ngIf=\"metadata.preview && metadata.multiple\">\n <i (click)=\"prev()\" [class.disabled]=\"imageIndex === 0\" class=\"prev-button fa-solid fa-angle-left\"></i>\n <img *ngIf=\"multiPreviewImages?.[imageIndex]\" class=\"mat-elevation-z2\" [src]=\"multiPreviewImages?.[imageIndex]\">\n <img *ngIf=\"!multiPreviewImages?.[imageIndex]\" class=\"mat-elevation-z2\" [src]=\"metadata.previewPlaceholderUrl ?? placeHolder\">\n <i (click)=\"next()\"\n [class.disabled]=\"!multiPreviewImages || !multiPreviewImages.length || imageIndex === (multiPreviewImages.length - 1)\"\n class=\"next-button fa-solid fa-angle-right\"\n >\n </i>\n </div>\n <div class=\"preview-nav\" *ngIf=\"metadata.preview && metadata.multiple\">\n <button type=\"button\" (click)=\"setIndex(imageIndex-4)\" mat-icon-button *ngIf=\"\n this.multiPreviewImages\n && multiPreviewImages[imageIndex-4]\n && imageIndex === (this.multiPreviewImages.length - 1)\"\n >\n <span class=\"dot\"></span>\n <span class=\"image-index\">{{imageIndex - 3}}</span>\n </button>\n <!-- eslint-disable-next-line @angular-eslint/template/conditional-complexity -->\n <button type=\"button\" (click)=\"setIndex(imageIndex-3)\" mat-icon-button *ngIf=\"this.multiPreviewImages\n && multiPreviewImages[imageIndex-3]\n && (\n imageIndex === (this.multiPreviewImages.length - 2)\n || imageIndex === (this.multiPreviewImages.length - 1)\n )\"\n >\n <span class=\"dot\"></span>\n <span class=\"image-index\">{{imageIndex - 2}}</span>\n </button>\n <button type=\"button\" (click)=\"setIndex(imageIndex-2)\" mat-icon-button *ngIf=\"multiPreviewImages?.[imageIndex-2]\">\n <span class=\"dot\"></span>\n <span class=\"image-index\">{{imageIndex - 1}}</span>\n </button>\n <button type=\"button\" (click)=\"setIndex(imageIndex-1)\" mat-icon-button *ngIf=\"multiPreviewImages?.[imageIndex-1]\">\n <i class=\"dot\"></i>\n <span class=\"image-index\">{{imageIndex}}</span>\n </button>\n <button type=\"button\" mat-icon-button disabled>\n <i class=\"dot selected\"></i>\n <span class=\"image-index\">{{imageIndex + 1}}</span>\n </button>\n <button type=\"button\" (click)=\"setIndex(imageIndex+1)\" mat-icon-button *ngIf=\"multiPreviewImages?.[imageIndex+1]\">\n <span class=\"dot\"></span>\n <span class=\"image-index\">{{imageIndex + 2}}</span>\n </button>\n <button type=\"button\" (click)=\"setIndex(imageIndex+2)\" mat-icon-button *ngIf=\"multiPreviewImages?.[imageIndex+2]\">\n <span class=\"dot\"></span>\n <span class=\"image-index\">{{imageIndex + 3}}</span>\n </button>\n <button type=\"button\" (click)=\"setIndex(imageIndex+3)\" mat-icon-button *ngIf=\"multiPreviewImages?.[imageIndex+3] && imageIndex <= 1\">\n <span class=\"dot\"></span>\n <span class=\"image-index\">{{imageIndex + 4}}</span>\n </button>\n <button type=\"button\" (click)=\"setIndex(imageIndex+4)\" mat-icon-button *ngIf=\"multiPreviewImages?.[imageIndex+4] && imageIndex === 0\">\n <span class=\"dot\"></span>\n <span class=\"image-index\">{{imageIndex + 5}}</span>\n </button>\n </div>\n\n <div class=\"image-preview\" *ngIf=\"metadata.preview && !metadata.multiple\">\n <i class=\"prev-button disabled fa-solid fa-angle-left\"></i>\n <img class=\"mat-elevation-z2\" [src]=\"singlePreviewImage ?? metadata.previewPlaceholderUrl ?? placeHolder\">\n <i class=\"next-button disabled fa-solid fa-angle-right\"></i>\n </div>\n <div class=\"preview-nav\" *ngIf=\"metadata.preview && !metadata.multiple\">\n <button type=\"button\" disabled mat-icon-button>\n <span class=\"dot selected\"></span>\n <span class=\"image-index\">1</span>\n </button>\n </div>\n</div>", styles: [".file-input{margin-top:15px;margin-bottom:15px;padding:15px;border-radius:5px}.image-preview{height:250px;display:flex;align-items:center;padding-top:15px;padding-bottom:15px}.image-preview .prev-button{font-size:100px;margin-left:5px;color:#0000008a}.image-preview .next-button{font-size:100px;margin-right:5px;color:#0000008a}.image-preview .prev-button:hover,.image-preview .next-button:hover{cursor:pointer;color:#000000b3;transition:all .5s ease}.image-preview .prev-button.disabled,.image-preview .next-button.disabled{color:#00000042}.image-preview .prev-button.disabled:hover,.image-preview .next-button.disabled:hover{color:#00000042;transition:none;cursor:default}.image-preview img{max-width:calc(100% - 100px);max-height:100%;margin-left:auto;margin-right:auto;border-radius:3px}.preview-nav{text-align:center}.preview-nav button{display:inline-block;width:18px;height:18px;margin-left:5px;margin-right:5px}.preview-nav button .dot{height:100%;width:100%;background-color:#00000061;border-radius:50%;display:block}.preview-nav button .dot.selected{background-color:#0000008a}.preview-nav button .image-index{position:absolute;height:100%;width:100%;display:block;top:-11.5px;color:#fff}.preview-nav button:hover .dot{background-color:#0000008a;transition:all .3s ease}\n"] }]
3474
+ args: [{ selector: 'file-image-input', template: "<div *ngIf=\"!metadata.dragAndDrop && !metadata.preview\">\n <file-input\n (fileDataChangeEvent)=\"refreshFileData($event)\"\n [propertyValue]=\"propertyValue\"\n [metadata]=\"metadata\"\n [getValidationErrorMessage]=\"getValidationErrorMessage\"\n [isReadOnly]=\"isReadOnly\"\n [entity]=\"entity\"\n [key]=\"key\"\n >\n </file-input>\n</div>\n\n<div *ngIf=\"metadata.dragAndDrop || metadata.preview\" class=\"file-input mat-elevation-z8\">\n <file-input\n (fileDataChangeEvent)=\"refreshFileData($event)\"\n [propertyValue]=\"propertyValue\"\n [metadata]=\"metadata\"\n [getValidationErrorMessage]=\"getValidationErrorMessage\"\n [isReadOnly]=\"isReadOnly\"\n [entity]=\"entity\"\n [key]=\"key\"\n >\n </file-input>\n\n <div class=\"image-preview\" *ngIf=\"metadata.preview && metadata.multiple\">\n <i (click)=\"prev()\" [class.disabled]=\"imageIndex === 0\" class=\"prev-button fa-solid fa-angle-left\"></i>\n <img *ngIf=\"multiPreviewImages?.[imageIndex]\" class=\"mat-elevation-z2\" [src]=\"multiPreviewImages?.[imageIndex]\">\n <img *ngIf=\"!multiPreviewImages?.[imageIndex]\" class=\"mat-elevation-z2\" [src]=\"metadata.previewPlaceholderUrl ?? placeHolder\">\n <i (click)=\"next()\"\n [class.disabled]=\"!multiPreviewImages || !multiPreviewImages.length || imageIndex === (multiPreviewImages.length - 1)\"\n class=\"next-button fa-solid fa-angle-right\"\n >\n </i>\n </div>\n\n <div class=\"preview-nav\" *ngIf=\"metadata.preview && metadata.multiple\">\n <button type=\"button\" (click)=\"setIndex(imageIndex-4)\" mat-icon-button *ngIf=\"\n this.multiPreviewImages\n && multiPreviewImages[imageIndex-4]\n && imageIndex === (this.multiPreviewImages.length - 1)\"\n >\n <div class=\"dot\">\n <div class=\"image-index\">{{imageIndex - 3}}</div>\n </div>\n </button>\n <!-- eslint-disable-next-line @angular-eslint/template/conditional-complexity -->\n <button type=\"button\" (click)=\"setIndex(imageIndex-3)\" mat-icon-button *ngIf=\"this.multiPreviewImages\n && multiPreviewImages[imageIndex-3]\n && (\n imageIndex === (this.multiPreviewImages.length - 2)\n || imageIndex === (this.multiPreviewImages.length - 1)\n )\"\n >\n <div class=\"dot\">\n <div class=\"image-index\">{{imageIndex - 2}}</div>\n </div>\n </button>\n <button type=\"button\" (click)=\"setIndex(imageIndex-2)\" mat-icon-button *ngIf=\"multiPreviewImages?.[imageIndex-2]\">\n <div class=\"dot\">\n <div class=\"image-index\">{{imageIndex - 1}}</div>\n </div>\n </button>\n <button type=\"button\" (click)=\"setIndex(imageIndex-1)\" mat-icon-button *ngIf=\"multiPreviewImages?.[imageIndex-1]\">\n <div class=\"dot\">\n <div class=\"image-index\">{{imageIndex}}</div>\n </div>\n </button>\n <button type=\"button\" mat-icon-button disabled>\n <div class=\"dot selected\">\n <div class=\"image-index\">{{imageIndex + 1}}</div>\n </div>\n </button>\n <button type=\"button\" (click)=\"setIndex(imageIndex+1)\" mat-icon-button *ngIf=\"multiPreviewImages?.[imageIndex+1]\">\n <div class=\"dot\">\n <div class=\"image-index\">{{imageIndex + 2}}</div>\n </div>\n </button>\n <button type=\"button\" (click)=\"setIndex(imageIndex+2)\" mat-icon-button *ngIf=\"multiPreviewImages?.[imageIndex+2]\">\n <div class=\"dot\">\n <div class=\"image-index\">{{imageIndex + 3}}</div>\n </div>\n </button>\n <button type=\"button\" (click)=\"setIndex(imageIndex+3)\" mat-icon-button *ngIf=\"multiPreviewImages?.[imageIndex+3] && imageIndex <= 1\">\n <div class=\"dot\">\n <div class=\"image-index\">{{imageIndex + 4}}</div>\n </div>\n </button>\n <button type=\"button\" (click)=\"setIndex(imageIndex+4)\" mat-icon-button *ngIf=\"multiPreviewImages?.[imageIndex+4] && imageIndex === 0\">\n <div class=\"dot\">\n <div class=\"image-index\">{{imageIndex + 5}}</div>\n </div>\n </button>\n </div>\n\n <div class=\"image-preview\" *ngIf=\"metadata.preview && !metadata.multiple\">\n <i class=\"prev-button disabled fa-solid fa-angle-left\"></i>\n <img class=\"mat-elevation-z2\" [src]=\"singlePreviewImage ?? metadata.previewPlaceholderUrl ?? placeHolder\">\n <i class=\"next-button disabled fa-solid fa-angle-right\"></i>\n </div>\n\n <div class=\"preview-nav\" *ngIf=\"metadata.preview && !metadata.multiple\">\n <button type=\"button\" disabled mat-icon-button>\n <div class=\"dot selected\">\n <div class=\"image-index\">1</div>\n </div>\n </button>\n </div>\n</div>", styles: [".file-input{margin-top:15px;margin-bottom:15px;padding:15px;border-radius:5px}.image-preview{height:250px;display:flex;align-items:center;padding-top:15px;padding-bottom:15px}.image-preview .prev-button{font-size:100px;margin-left:5px;color:#0000008a}.image-preview .next-button{font-size:100px;margin-right:5px;color:#0000008a}.image-preview .prev-button:hover,.image-preview .next-button:hover{cursor:pointer;color:#000000b3;transition:all .5s ease}.image-preview .prev-button.disabled,.image-preview .next-button.disabled{color:#00000042}.image-preview .prev-button.disabled:hover,.image-preview .next-button.disabled:hover{color:#00000042;transition:none;cursor:default}.image-preview img{max-width:calc(100% - 100px);max-height:100%;margin-left:auto;margin-right:auto;border-radius:3px}.preview-nav{display:flex;align-items:center;justify-content:center;column-gap:5px}.preview-nav button{display:flex;align-items:center;justify-content:center;padding:0;height:auto;width:auto}.preview-nav button .dot{display:flex;align-items:center;justify-content:center;height:25px;width:25px;min-width:25px;background-color:#00000061;border-radius:50%}.preview-nav button .dot .image-index{color:#fff;font-size:20px;font-weight:600}.preview-nav button .dot.selected{background-color:#0000008a}.preview-nav button:hover .dot{background-color:#0000008a;transition:all .3s ease}\n"] }]
3213
3475
  }], ctorParameters: function () { return [{ type: i2.HttpClient }]; } });
3214
3476
 
3215
3477
  const NGX_GET_VALIDATION_ERROR_MESSAGE = new InjectionToken('Provider for the default getValidationErrorMessage.', {
@@ -3233,6 +3495,10 @@ function getValidationErrorMessage(model) {
3233
3495
  // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
3234
3496
  return `needs to be at least ${model.getError('minlength').requiredLength} characters long`;
3235
3497
  }
3498
+ else if (model.hasError('maxlength')) {
3499
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
3500
+ return `needs to be at most ${model.getError('maxlength').requiredLength} characters long`;
3501
+ }
3236
3502
  else if (model.hasError('min')) {
3237
3503
  // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
3238
3504
  return `needs to be equal or bigger than ${model.getError('min').min}`;
@@ -3241,6 +3507,9 @@ function getValidationErrorMessage(model) {
3241
3507
  // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
3242
3508
  return `needs to be equal or smaller than ${model.getError('max').max}`;
3243
3509
  }
3510
+ else if (model.hasError('passwordMatch')) {
3511
+ return 'Passwords need to match!';
3512
+ }
3244
3513
  else if (model.hasError('required')) {
3245
3514
  return 'required';
3246
3515
  }
@@ -3748,21 +4017,21 @@ class StringPasswordInputComponent extends NgxMatEntityBaseInputComponent {
3748
4017
  this.emitChange();
3749
4018
  }
3750
4019
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.0", ngImport: i0, type: StringPasswordInputComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
3751
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.0", type: StringPasswordInputComponent, selector: "string-password-input", usesInheritance: true, ngImport: i0, template: "<div class=\"password-row\">\n <mat-form-field [class.w-50]=\"metadata.needsConfirmation\" [class.w-100]=\"!metadata.needsConfirmation\">\n <mat-label>{{metadata.displayName}}</mat-label>\n <input\n [type]=\"hide ? 'password' : 'text'\"\n matInput\n [(ngModel)]=\"propertyValue\"\n [name]=\"key.toString() + uuid\"\n #model=\"ngModel\"\n [required]=\"metadata.required(entity)\"\n [pattern]=\"metadata.regex ?? '[\\\\s\\\\S]*'\"\n [minlength]=\"metadata.minLength ?? null\"\n [maxlength]=\"metadata.maxLength ?? null\"\n (ngModelChange)=\"passwordInput()\"\n [disabled]=\"isReadOnly\"\n >\n <button type=\"button\" (click)=\"hide = !hide\" mat-icon-button matSuffix>\n <i *ngIf=\"hide\" class=\"fas fa-eye-slash\"></i>\n <i *ngIf=\"!hide\" class=\"fas fa-eye\"></i>\n </button>\n <mat-error>{{getValidationErrorMessage(model)}}</mat-error>\n </mat-form-field>\n <div *ngIf=\"metadata.needsConfirmation\" class=\"password-spacer\"></div>\n <mat-form-field class=\"w-50\" *ngIf=\"metadata.needsConfirmation\">\n <mat-label>{{metadata.confirmationDisplayName}}</mat-label>\n <input\n [type]=\"hideConfirm ? 'password' : 'text'\"\n matInput\n [(ngModel)]=\"confirmPassword\"\n [name]=\"key.toString() + 'confirmPassword' + uuid\"\n #confirmModel=\"ngModel\"\n [required]=\"confirmRequired\"\n (ngModelChange)=\"passwordInput()\"\n [disabled]=\"isReadOnly\"\n >\n <button type=\"button\" (click)=\"hideConfirm = !hideConfirm\" mat-icon-button matSuffix>\n <i *ngIf=\"hideConfirm\" class=\"fas fa-eye-slash\"></i>\n <i *ngIf=\"!hideConfirm\" class=\"fas fa-eye\"></i>\n </button>\n <mat-error>{{getValidationErrorMessage(confirmModel)}}</mat-error>\n </mat-form-field>\n</div>\n\n<mat-error *ngIf=\"metadata.needsConfirmation && propertyValue && confirmPassword && (confirmPassword !== propertyValue)\">\n {{metadata.passwordsDontMatchErrorMessage}}\n</mat-error>", styles: [".password-row{display:flex;justify-content:space-between}.password-spacer{margin-left:12px;margin-right:12px}.w-50{width:50%}.w-100{width:100%}\n"], dependencies: [{ kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2$1.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "component", type: i2$2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$2.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$2.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i2$2.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "directive", type: i3.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: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i3.MinLengthValidator, selector: "[minlength][formControlName],[minlength][formControl],[minlength][ngModel]", inputs: ["minlength"] }, { kind: "directive", type: i3.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i3.PatternValidator, selector: "[pattern][formControlName],[pattern][formControl],[pattern][ngModel]", inputs: ["pattern"] }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i5.MatIconButton, selector: "button[mat-icon-button]", inputs: ["disabled", "disableRipple", "color"], exportAs: ["matButton"] }] });
4020
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.0", type: StringPasswordInputComponent, selector: "string-password-input", usesInheritance: true, ngImport: i0, template: "<div class=\"password-row\">\n <mat-form-field>\n <mat-label>{{metadata.displayName}}</mat-label>\n <input\n [type]=\"hide ? 'password' : 'text'\"\n matInput\n [(ngModel)]=\"propertyValue\"\n [name]=\"key.toString() + uuid\"\n #model=\"ngModel\"\n [required]=\"metadata.required(entity)\"\n [pattern]=\"metadata.regex ?? '[\\\\s\\\\S]*'\"\n [minlength]=\"metadata.minLength ?? null\"\n [maxlength]=\"metadata.maxLength ?? null\"\n (ngModelChange)=\"passwordInput()\"\n [disabled]=\"isReadOnly\"\n >\n <button type=\"button\" (click)=\"hide = !hide\" mat-icon-button matSuffix>\n <div class=\"d-flex justify-content-center align-items-center\">\n <i *ngIf=\"hide\" class=\"fas fa-eye-slash\"></i>\n <i *ngIf=\"!hide\" class=\"fas fa-eye\"></i>\n </div>\n </button>\n <mat-error>{{getValidationErrorMessage(model)}}</mat-error>\n </mat-form-field>\n <mat-form-field *ngIf=\"metadata.needsConfirmation\">\n <mat-label>{{metadata.confirmationDisplayName}}</mat-label>\n <input\n [type]=\"hideConfirm ? 'password' : 'text'\"\n matInput\n [(ngModel)]=\"confirmPassword\"\n [name]=\"key.toString() + 'confirmPassword' + uuid\"\n #confirmModel=\"ngModel\"\n [required]=\"confirmRequired\"\n (ngModelChange)=\"passwordInput()\"\n [disabled]=\"isReadOnly\"\n [passwordMatch]=\"propertyValue\"\n >\n <button type=\"button\" (click)=\"hideConfirm = !hideConfirm\" mat-icon-button matSuffix>\n <div class=\"d-flex justify-content-center align-items-center\">\n <i *ngIf=\"hideConfirm\" class=\"fas fa-eye-slash\"></i>\n <i *ngIf=\"!hideConfirm\" class=\"fas fa-eye\"></i>\n </div>\n </button>\n <mat-error>{{getValidationErrorMessage(confirmModel)}}</mat-error>\n </mat-form-field>\n</div>", styles: [".password-row{display:flex;justify-content:space-evenly;column-gap:24px}.password-row mat-form-field{flex:1 1 0}\n"], dependencies: [{ kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2$1.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "component", type: i2$2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$2.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$2.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i2$2.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "directive", type: i3.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: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i3.MinLengthValidator, selector: "[minlength][formControlName],[minlength][formControl],[minlength][ngModel]", inputs: ["minlength"] }, { kind: "directive", type: i3.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i3.PatternValidator, selector: "[pattern][formControlName],[pattern][formControl],[pattern][ngModel]", inputs: ["pattern"] }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i5.MatIconButton, selector: "button[mat-icon-button]", inputs: ["disabled", "disableRipple", "color"], exportAs: ["matButton"] }, { kind: "directive", type: PasswordMatchValidatorDirective, selector: "[passwordMatch]", inputs: ["passwordMatch"] }] });
3752
4021
  }
3753
4022
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.0", ngImport: i0, type: StringPasswordInputComponent, decorators: [{
3754
4023
  type: Component,
3755
- args: [{ selector: 'string-password-input', template: "<div class=\"password-row\">\n <mat-form-field [class.w-50]=\"metadata.needsConfirmation\" [class.w-100]=\"!metadata.needsConfirmation\">\n <mat-label>{{metadata.displayName}}</mat-label>\n <input\n [type]=\"hide ? 'password' : 'text'\"\n matInput\n [(ngModel)]=\"propertyValue\"\n [name]=\"key.toString() + uuid\"\n #model=\"ngModel\"\n [required]=\"metadata.required(entity)\"\n [pattern]=\"metadata.regex ?? '[\\\\s\\\\S]*'\"\n [minlength]=\"metadata.minLength ?? null\"\n [maxlength]=\"metadata.maxLength ?? null\"\n (ngModelChange)=\"passwordInput()\"\n [disabled]=\"isReadOnly\"\n >\n <button type=\"button\" (click)=\"hide = !hide\" mat-icon-button matSuffix>\n <i *ngIf=\"hide\" class=\"fas fa-eye-slash\"></i>\n <i *ngIf=\"!hide\" class=\"fas fa-eye\"></i>\n </button>\n <mat-error>{{getValidationErrorMessage(model)}}</mat-error>\n </mat-form-field>\n <div *ngIf=\"metadata.needsConfirmation\" class=\"password-spacer\"></div>\n <mat-form-field class=\"w-50\" *ngIf=\"metadata.needsConfirmation\">\n <mat-label>{{metadata.confirmationDisplayName}}</mat-label>\n <input\n [type]=\"hideConfirm ? 'password' : 'text'\"\n matInput\n [(ngModel)]=\"confirmPassword\"\n [name]=\"key.toString() + 'confirmPassword' + uuid\"\n #confirmModel=\"ngModel\"\n [required]=\"confirmRequired\"\n (ngModelChange)=\"passwordInput()\"\n [disabled]=\"isReadOnly\"\n >\n <button type=\"button\" (click)=\"hideConfirm = !hideConfirm\" mat-icon-button matSuffix>\n <i *ngIf=\"hideConfirm\" class=\"fas fa-eye-slash\"></i>\n <i *ngIf=\"!hideConfirm\" class=\"fas fa-eye\"></i>\n </button>\n <mat-error>{{getValidationErrorMessage(confirmModel)}}</mat-error>\n </mat-form-field>\n</div>\n\n<mat-error *ngIf=\"metadata.needsConfirmation && propertyValue && confirmPassword && (confirmPassword !== propertyValue)\">\n {{metadata.passwordsDontMatchErrorMessage}}\n</mat-error>", styles: [".password-row{display:flex;justify-content:space-between}.password-spacer{margin-left:12px;margin-right:12px}.w-50{width:50%}.w-100{width:100%}\n"] }]
4024
+ args: [{ selector: 'string-password-input', template: "<div class=\"password-row\">\n <mat-form-field>\n <mat-label>{{metadata.displayName}}</mat-label>\n <input\n [type]=\"hide ? 'password' : 'text'\"\n matInput\n [(ngModel)]=\"propertyValue\"\n [name]=\"key.toString() + uuid\"\n #model=\"ngModel\"\n [required]=\"metadata.required(entity)\"\n [pattern]=\"metadata.regex ?? '[\\\\s\\\\S]*'\"\n [minlength]=\"metadata.minLength ?? null\"\n [maxlength]=\"metadata.maxLength ?? null\"\n (ngModelChange)=\"passwordInput()\"\n [disabled]=\"isReadOnly\"\n >\n <button type=\"button\" (click)=\"hide = !hide\" mat-icon-button matSuffix>\n <div class=\"d-flex justify-content-center align-items-center\">\n <i *ngIf=\"hide\" class=\"fas fa-eye-slash\"></i>\n <i *ngIf=\"!hide\" class=\"fas fa-eye\"></i>\n </div>\n </button>\n <mat-error>{{getValidationErrorMessage(model)}}</mat-error>\n </mat-form-field>\n <mat-form-field *ngIf=\"metadata.needsConfirmation\">\n <mat-label>{{metadata.confirmationDisplayName}}</mat-label>\n <input\n [type]=\"hideConfirm ? 'password' : 'text'\"\n matInput\n [(ngModel)]=\"confirmPassword\"\n [name]=\"key.toString() + 'confirmPassword' + uuid\"\n #confirmModel=\"ngModel\"\n [required]=\"confirmRequired\"\n (ngModelChange)=\"passwordInput()\"\n [disabled]=\"isReadOnly\"\n [passwordMatch]=\"propertyValue\"\n >\n <button type=\"button\" (click)=\"hideConfirm = !hideConfirm\" mat-icon-button matSuffix>\n <div class=\"d-flex justify-content-center align-items-center\">\n <i *ngIf=\"hideConfirm\" class=\"fas fa-eye-slash\"></i>\n <i *ngIf=\"!hideConfirm\" class=\"fas fa-eye\"></i>\n </div>\n </button>\n <mat-error>{{getValidationErrorMessage(confirmModel)}}</mat-error>\n </mat-form-field>\n</div>", styles: [".password-row{display:flex;justify-content:space-evenly;column-gap:24px}.password-row mat-form-field{flex:1 1 0}\n"] }]
3756
4025
  }] });
3757
4026
 
3758
4027
  /* eslint-disable jsdoc/require-jsdoc */
3759
4028
  class NumberInputComponent extends NgxMatEntityBaseInputComponent {
3760
4029
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.0", ngImport: i0, type: NumberInputComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
3761
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.0", type: NumberInputComponent, selector: "number-input", usesInheritance: true, ngImport: i0, template: "<mat-form-field>\n <mat-label>{{metadata.displayName}}</mat-label>\n <input\n matInput\n type=\"number\"\n [(ngModel)]=\"propertyValue\"\n [name]=\"key.toString() + uuid\"\n #model=\"ngModel\"\n [required]=\"metadata.required(entity)\"\n [min]=\"metadata.min ?? null\"\n [max]=\"metadata.max ?? null\"\n (ngModelChange)=\"emitChange()\"\n [disabled]=\"isReadOnly\"\n >\n <mat-error>{{getValidationErrorMessage(model)}}</mat-error>\n</mat-form-field>", styles: ["mat-form-field{width:100%}\n"], dependencies: [{ kind: "directive", type: i2$1.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "component", type: i2$2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$2.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$2.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i3.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: i3.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i3.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i3.MaxValidator, selector: "input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]", inputs: ["max"] }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }] });
4030
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.0", type: NumberInputComponent, selector: "number-input", usesInheritance: true, ngImport: i0, template: "<mat-form-field>\n <mat-label>{{metadata.displayName}}</mat-label>\n <input\n matInput\n type=\"number\"\n [(ngModel)]=\"propertyValue\"\n [name]=\"key.toString() + uuid\"\n #model=\"ngModel\"\n [required]=\"metadata.required(entity)\"\n [min]=\"metadata.min ?? null\"\n [max]=\"metadata.max ?? null\"\n (ngModelChange)=\"emitChange()\"\n [disabled]=\"isReadOnly\"\n number\n >\n <mat-error>{{getValidationErrorMessage(model)}}</mat-error>\n</mat-form-field>", styles: ["mat-form-field{width:100%}\n"], dependencies: [{ kind: "directive", type: i2$1.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "component", type: i2$2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$2.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$2.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i3.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: i3.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i3.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i3.MaxValidator, selector: "input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]", inputs: ["max"] }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: NumberDirective, selector: "[number]" }] });
3762
4031
  }
3763
4032
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.0", ngImport: i0, type: NumberInputComponent, decorators: [{
3764
4033
  type: Component,
3765
- args: [{ selector: 'number-input', template: "<mat-form-field>\n <mat-label>{{metadata.displayName}}</mat-label>\n <input\n matInput\n type=\"number\"\n [(ngModel)]=\"propertyValue\"\n [name]=\"key.toString() + uuid\"\n #model=\"ngModel\"\n [required]=\"metadata.required(entity)\"\n [min]=\"metadata.min ?? null\"\n [max]=\"metadata.max ?? null\"\n (ngModelChange)=\"emitChange()\"\n [disabled]=\"isReadOnly\"\n >\n <mat-error>{{getValidationErrorMessage(model)}}</mat-error>\n</mat-form-field>", styles: ["mat-form-field{width:100%}\n"] }]
4034
+ args: [{ selector: 'number-input', template: "<mat-form-field>\n <mat-label>{{metadata.displayName}}</mat-label>\n <input\n matInput\n type=\"number\"\n [(ngModel)]=\"propertyValue\"\n [name]=\"key.toString() + uuid\"\n #model=\"ngModel\"\n [required]=\"metadata.required(entity)\"\n [min]=\"metadata.min ?? null\"\n [max]=\"metadata.max ?? null\"\n (ngModelChange)=\"emitChange()\"\n [disabled]=\"isReadOnly\"\n number\n >\n <mat-error>{{getValidationErrorMessage(model)}}</mat-error>\n</mat-form-field>", styles: ["mat-form-field{width:100%}\n"] }]
3766
4035
  }] });
3767
4036
 
3768
4037
  /* eslint-disable jsdoc/require-jsdoc */
@@ -3782,12 +4051,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.0", ngImpor
3782
4051
 
3783
4052
  /* eslint-disable jsdoc/require-jsdoc */
3784
4053
  class NumberSliderInputComponent extends NgxMatEntityBaseInputComponent {
4054
+ updatePropertyValue(value) {
4055
+ this.propertyValue = value;
4056
+ }
3785
4057
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.0", ngImport: i0, type: NumberSliderInputComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
3786
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.0", type: NumberSliderInputComponent, selector: "number-slider-input", usesInheritance: true, ngImport: i0, template: "<mat-slider\n id=\"slider\"\n color=\"primary\"\n (click)=\"model.control.markAsTouched()\"\n [min]=\"metadata.min ?? null\"\n [max]=\"metadata.max ?? null\"\n [step]=\"metadata.step\"\n [discrete]=\"true\"\n [displayWith]=\"metadata.formatThumbLabelValue\"\n [showTickMarks]=\"metadata.showTickMarks\"\n [disabled]=\"isReadOnly\"\n>\n <input matSliderThumb\n [(ngModel)]=\"propertyValue\"\n [name]=\"key.toString() + 'Helper' + uuid\"\n #model=\"ngModel\"\n [required]=\"metadata.required(entity)\"\n [min]=\"metadata.min ?? null\"\n [max]=\"metadata.max ?? null\"\n (ngModelChange)=\"emitChange()\"\n [disabled]=\"isReadOnly\"\n >\n</mat-slider>\n\n<mat-form-field floatLabel=\"always\">\n <mat-label>{{metadata.displayName}}</mat-label>\n <!-- hidden input is needed so that the slider can be used inside a mat-form-field -->\n <input matInput style=\"opacity: 0%;\">\n <mat-error>{{getValidationErrorMessage(model)}}</mat-error>\n</mat-form-field>", styles: ["mat-form-field{width:100%;margin-top:-40px}mat-slider{width:calc(100% - 40px);margin-left:20px;margin-right:20px;margin-bottom:-40px;z-index:999}::ng-deep #slider .mdc-slider__value-indicator-container{top:23px;left:57px;display:flex;transform:rotate(90deg) scale(1);border-radius:50%;pointer-events:auto}::ng-deep #slider .mdc-slider__value-indicator{padding-right:5px;padding-left:5px;transition:transform .1s 0ms cubic-bezier(0,0,.2,1);transform:scale(1)}::ng-deep #slider .mdc-slider__value-indicator-text{transform:rotate(270deg)}\n"], dependencies: [{ kind: "directive", type: i2$1.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "component", type: i2$2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$2.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$2.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i3.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: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i4$4.MatSlider, selector: "mat-slider", inputs: ["color", "disableRipple", "disabled", "discrete", "showTickMarks", "min", "max", "step", "displayWith"], exportAs: ["matSlider"] }, { kind: "directive", type: i4$4.MatSliderThumb, selector: "input[matSliderThumb]", inputs: ["value"], outputs: ["valueChange", "dragStart", "dragEnd"], exportAs: ["matSliderThumb"] }] });
4058
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.0", type: NumberSliderInputComponent, selector: "number-slider-input", usesInheritance: true, ngImport: i0, template: "<mat-slider\n id=\"slider\"\n color=\"primary\"\n (click)=\"model.control.markAsTouched()\"\n [min]=\"metadata.min ?? null\"\n [max]=\"metadata.max ?? null\"\n [step]=\"metadata.step\"\n [discrete]=\"true\"\n [displayWith]=\"metadata.formatThumbLabelValue\"\n [showTickMarks]=\"metadata.showTickMarks\"\n [disabled]=\"isReadOnly\"\n>\n <input matSliderThumb\n (valueChange)=\"updatePropertyValue($event)\"\n [disabled]=\"isReadOnly\"\n >\n</mat-slider>\n\n<mat-form-field floatLabel=\"always\">\n <mat-label>{{metadata.displayName}}</mat-label>\n <!-- hidden input is needed so that the slider can be used inside a mat-form-field -->\n <input matInput style=\"opacity: 0%;\"\n [(ngModel)]=\"propertyValue\"\n [name]=\"key.toString() + uuid\"\n #model=\"ngModel\"\n [required]=\"metadata.required(entity)\"\n [min]=\"metadata.min ?? null\"\n [max]=\"metadata.max ?? null\"\n (ngModelChange)=\"emitChange()\"\n [disabled]=\"isReadOnly\"\n >\n \n <mat-error>{{getValidationErrorMessage(model)}}</mat-error>\n</mat-form-field>", styles: ["mat-form-field{width:100%;margin-top:-40px}mat-slider{width:calc(100% - 40px);margin-left:20px;margin-right:20px;margin-bottom:-40px;z-index:999}::ng-deep #slider .mdc-slider__value-indicator-container{top:23px;left:57px;display:flex;transform:rotate(90deg) scale(1);border-radius:50%;pointer-events:auto}::ng-deep #slider .mdc-slider__value-indicator{padding-right:5px;padding-left:5px;transition:transform .1s 0ms cubic-bezier(0,0,.2,1);transform:scale(1)}::ng-deep #slider .mdc-slider__value-indicator-text{transform:rotate(270deg)}\n"], dependencies: [{ kind: "directive", type: i2$1.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "component", type: i2$2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$2.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$2.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i3.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: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i4$4.MatSlider, selector: "mat-slider", inputs: ["color", "disableRipple", "disabled", "discrete", "showTickMarks", "min", "max", "step", "displayWith"], exportAs: ["matSlider"] }, { kind: "directive", type: i4$4.MatSliderThumb, selector: "input[matSliderThumb]", inputs: ["value"], outputs: ["valueChange", "dragStart", "dragEnd"], exportAs: ["matSliderThumb"] }] });
3787
4059
  }
3788
4060
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.0", ngImport: i0, type: NumberSliderInputComponent, decorators: [{
3789
4061
  type: Component,
3790
- args: [{ selector: 'number-slider-input', template: "<mat-slider\n id=\"slider\"\n color=\"primary\"\n (click)=\"model.control.markAsTouched()\"\n [min]=\"metadata.min ?? null\"\n [max]=\"metadata.max ?? null\"\n [step]=\"metadata.step\"\n [discrete]=\"true\"\n [displayWith]=\"metadata.formatThumbLabelValue\"\n [showTickMarks]=\"metadata.showTickMarks\"\n [disabled]=\"isReadOnly\"\n>\n <input matSliderThumb\n [(ngModel)]=\"propertyValue\"\n [name]=\"key.toString() + 'Helper' + uuid\"\n #model=\"ngModel\"\n [required]=\"metadata.required(entity)\"\n [min]=\"metadata.min ?? null\"\n [max]=\"metadata.max ?? null\"\n (ngModelChange)=\"emitChange()\"\n [disabled]=\"isReadOnly\"\n >\n</mat-slider>\n\n<mat-form-field floatLabel=\"always\">\n <mat-label>{{metadata.displayName}}</mat-label>\n <!-- hidden input is needed so that the slider can be used inside a mat-form-field -->\n <input matInput style=\"opacity: 0%;\">\n <mat-error>{{getValidationErrorMessage(model)}}</mat-error>\n</mat-form-field>", styles: ["mat-form-field{width:100%;margin-top:-40px}mat-slider{width:calc(100% - 40px);margin-left:20px;margin-right:20px;margin-bottom:-40px;z-index:999}::ng-deep #slider .mdc-slider__value-indicator-container{top:23px;left:57px;display:flex;transform:rotate(90deg) scale(1);border-radius:50%;pointer-events:auto}::ng-deep #slider .mdc-slider__value-indicator{padding-right:5px;padding-left:5px;transition:transform .1s 0ms cubic-bezier(0,0,.2,1);transform:scale(1)}::ng-deep #slider .mdc-slider__value-indicator-text{transform:rotate(270deg)}\n"] }]
4062
+ args: [{ selector: 'number-slider-input', template: "<mat-slider\n id=\"slider\"\n color=\"primary\"\n (click)=\"model.control.markAsTouched()\"\n [min]=\"metadata.min ?? null\"\n [max]=\"metadata.max ?? null\"\n [step]=\"metadata.step\"\n [discrete]=\"true\"\n [displayWith]=\"metadata.formatThumbLabelValue\"\n [showTickMarks]=\"metadata.showTickMarks\"\n [disabled]=\"isReadOnly\"\n>\n <input matSliderThumb\n (valueChange)=\"updatePropertyValue($event)\"\n [disabled]=\"isReadOnly\"\n >\n</mat-slider>\n\n<mat-form-field floatLabel=\"always\">\n <mat-label>{{metadata.displayName}}</mat-label>\n <!-- hidden input is needed so that the slider can be used inside a mat-form-field -->\n <input matInput style=\"opacity: 0%;\"\n [(ngModel)]=\"propertyValue\"\n [name]=\"key.toString() + uuid\"\n #model=\"ngModel\"\n [required]=\"metadata.required(entity)\"\n [min]=\"metadata.min ?? null\"\n [max]=\"metadata.max ?? null\"\n (ngModelChange)=\"emitChange()\"\n [disabled]=\"isReadOnly\"\n >\n \n <mat-error>{{getValidationErrorMessage(model)}}</mat-error>\n</mat-form-field>", styles: ["mat-form-field{width:100%;margin-top:-40px}mat-slider{width:calc(100% - 40px);margin-left:20px;margin-right:20px;margin-bottom:-40px;z-index:999}::ng-deep #slider .mdc-slider__value-indicator-container{top:23px;left:57px;display:flex;transform:rotate(90deg) scale(1);border-radius:50%;pointer-events:auto}::ng-deep #slider .mdc-slider__value-indicator{padding-right:5px;padding-left:5px;transition:transform .1s 0ms cubic-bezier(0,0,.2,1);transform:scale(1)}::ng-deep #slider .mdc-slider__value-indicator-text{transform:rotate(270deg)}\n"] }]
3791
4063
  }] });
3792
4064
 
3793
4065
  /* eslint-disable jsdoc/require-jsdoc */
@@ -3962,8 +4234,26 @@ class NgxMatEntityInputComponent {
3962
4234
  editArrayItemDialogData;
3963
4235
  metadataHasMany;
3964
4236
  hasManyIsLoading = true;
3965
- hasManyPaginator;
3966
- hasManySort;
4237
+ /**
4238
+ * A setter for the has many paginator.
4239
+ * Is needed because the paginator is inside a switch case,
4240
+ * which means that at ngOnInit it can't be initialized.
4241
+ */
4242
+ set hasManyPaginator(paginator) {
4243
+ if (!this.hasManyDataSource.paginator) {
4244
+ this.hasManyDataSource.paginator = paginator;
4245
+ }
4246
+ }
4247
+ /**
4248
+ * A setter for the has many sort.
4249
+ * Is needed because the sort is inside a switch case,
4250
+ * which means that at ngOnInit it can't be initialized.
4251
+ */
4252
+ set hasManySort(sort) {
4253
+ if (!this.hasManyDataSource.sort) {
4254
+ this.hasManyDataSource.sort = sort;
4255
+ }
4256
+ }
3967
4257
  hasManyFilter;
3968
4258
  displayedHasManyColumns;
3969
4259
  hasManyDataSource = new MatTableDataSource();
@@ -4106,7 +4396,6 @@ class NgxMatEntityInputComponent {
4106
4396
  return this.metadataHasMany.tableData.baseData.displayColumns.find((dp) => dp.displayName === header)?.value(entity);
4107
4397
  });
4108
4398
  };
4109
- this.hasManyDataSource.sort = this.hasManySort;
4110
4399
  this.hasManyDataSource.filterPredicate = (entity, filter) => {
4111
4400
  const searchStr = this.metadataHasMany.tableData.baseData.searchString(entity);
4112
4401
  const formattedSearchString = searchStr.toLowerCase();
@@ -4114,7 +4403,6 @@ class NgxMatEntityInputComponent {
4114
4403
  return formattedSearchString.includes(formattedFilterString);
4115
4404
  };
4116
4405
  this.hasManyDataSource.filter = this.hasManyFilter;
4117
- this.hasManyDataSource.paginator = this.hasManyPaginator;
4118
4406
  this.hasManyEntityService.entitiesSubject.subscribe((entities) => {
4119
4407
  this.hasManyDataSource.data = entities;
4120
4408
  this.hasManySelection.clear();
@@ -4254,6 +4542,47 @@ class NgxMatEntityInputComponent {
4254
4542
  return this.metadataHasMany.tableData.baseData.allowDelete(entity);
4255
4543
  });
4256
4544
  }
4545
+ /**
4546
+ * Checks if an EditAction is disabled (e.g. Because the current entry doesn't fullfil the requirements).
4547
+ *
4548
+ * @param action - The EditAction to check.
4549
+ * @returns Whether or not the Action can be used.
4550
+ */
4551
+ hasManyEditActionDisabled(action) {
4552
+ return this.injector.runInContext(() => {
4553
+ return !action.enabled(this.hasManyEntityPriorChanges);
4554
+ });
4555
+ }
4556
+ /**
4557
+ * Runs the edit action on the entity.
4558
+ *
4559
+ * @param action - The action to run.
4560
+ */
4561
+ hasManyRunEditAction(action) {
4562
+ const requireConfirmDialog = this.injector.runInContext(() => {
4563
+ return action.requireConfirmDialog(this.hasManyEntityPriorChanges);
4564
+ });
4565
+ if (!requireConfirmDialog) {
4566
+ this.confirmHasManyRunEditAction(action);
4567
+ return;
4568
+ }
4569
+ const dialogRef = this.dialog.open(NgxMatEntityConfirmDialogComponent, {
4570
+ data: action.confirmDialogData,
4571
+ autoFocus: false,
4572
+ restoreFocus: false
4573
+ });
4574
+ dialogRef.afterClosed().subscribe(res => {
4575
+ if (res == true) {
4576
+ this.confirmHasManyRunEditAction(action);
4577
+ }
4578
+ });
4579
+ }
4580
+ confirmHasManyRunEditAction(action) {
4581
+ void this.injector.runInContext(async () => {
4582
+ await action.action(this.hasManyEntity, this.hasManyEntityPriorChanges);
4583
+ await this.checkHasManyEntity();
4584
+ });
4585
+ }
4257
4586
  editHasManyDefaultPage(entity) {
4258
4587
  void this.router.navigate(['', this.hasManyEntityService.editBaseRoute, entity[this.hasManyEntityService.idKey]]);
4259
4588
  }
@@ -4490,7 +4819,7 @@ class NgxMatEntityInputComponent {
4490
4819
  * @param omit - Whether values omitted for create or update should be left out.
4491
4820
  */
4492
4821
  checkIsHasManyEntityValid(omit) {
4493
- this.isHasManyEntityValid = EntityUtilities.isEntityValid(this.hasManyEntity, omit);
4822
+ this.isHasManyEntityValid = ValidationUtilities.isEntityValid(this.hasManyEntity, omit);
4494
4823
  }
4495
4824
  /**
4496
4825
  * Checks whether the array item is valid and if the array item is dirty.
@@ -4509,7 +4838,7 @@ class NgxMatEntityInputComponent {
4509
4838
  * Checks if the arrayItem is valid.
4510
4839
  */
4511
4840
  checkIsArrayItemValid() {
4512
- this.isArrayItemValid = EntityUtilities.isEntityValid(this.arrayItem, 'create');
4841
+ this.isArrayItemValid = ValidationUtilities.isEntityValid(this.arrayItem, 'create');
4513
4842
  }
4514
4843
  /**
4515
4844
  * Emits that a the value has been changed.
@@ -4616,11 +4945,11 @@ class NgxMatEntityInputComponent {
4616
4945
  this.emitChange();
4617
4946
  }
4618
4947
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.0", ngImport: i0, type: NgxMatEntityInputComponent, deps: [{ token: i1.MatDialog }, { token: i0.EnvironmentInjector }, { token: i2$3.Router }, { token: NGX_GET_VALIDATION_ERROR_MESSAGE }, { token: i2.HttpClient }], target: i0.ɵɵFactoryTarget.Component });
4619
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.0", type: NgxMatEntityInputComponent, selector: "ngx-mat-entity-input", inputs: { entity: "entity", propertyKey: "propertyKey", getValidationErrorMessage: "getValidationErrorMessage", hideOmitForCreate: "hideOmitForCreate", hideOmitForEdit: "hideOmitForEdit", validEmpty: "validEmpty", isReadOnly: "isReadOnly" }, outputs: { inputChangeEvent: "inputChangeEvent" }, viewQueries: [{ propertyName: "addArrayItemDialog", first: true, predicate: ["addArrayItemDialog"], descendants: true }, { propertyName: "editArrayItemDialog", first: true, predicate: ["editArrayItemDialog"], descendants: true }, { propertyName: "hasManyPaginator", first: true, predicate: MatPaginator, descendants: true, static: true }, { propertyName: "hasManySort", first: true, predicate: MatSort, descendants: true, static: true }, { propertyName: "hasManyFilter", first: true, predicate: ["filter"], descendants: true, static: true }, { propertyName: "createHasManyDialog", first: true, predicate: ["createHasManyDialog"], descendants: true }, { propertyName: "editHasManyDialog", first: true, predicate: ["editHasManyDialog"], descendants: true }], ngImport: i0, template: "<div [ngSwitch]=\"type\" *ngIf=\"!(hideOmitForCreate && metadata.omitForCreate) && !(hideOmitForEdit && metadata.omitForUpdate)\">\n <!-------------------------------------------->\n <!-----------------Strings-------------------->\n <!-------------------------------------------->\n <div *ngSwitchCase=\"DecoratorTypes.STRING\">\n <string-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </string-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.STRING_TEXTBOX\">\n <string-textbox-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </string-textbox-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.STRING_AUTOCOMPLETE\">\n <string-autocomplete-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </string-autocomplete-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.STRING_DROPDOWN\">\n <string-dropdown-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </string-dropdown-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.STRING_PASSWORD\">\n <string-password-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </string-password-input>\n </div>\n\n <!-------------------------------------------->\n <!-----------------Booleans------------------->\n <!-------------------------------------------->\n <div *ngSwitchCase=\"DecoratorTypes.BOOLEAN_CHECKBOX\">\n <boolean-checkbox-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </boolean-checkbox-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.BOOLEAN_TOGGLE\">\n <boolean-toggle-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </boolean-toggle-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.BOOLEAN_DROPDOWN\">\n <boolean-dropdown-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </boolean-dropdown-input>\n </div>\n\n <!-------------------------------------------->\n <!------------------Numbers------------------->\n <!-------------------------------------------->\n <div *ngSwitchCase=\"DecoratorTypes.NUMBER\">\n <number-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </number-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.NUMBER_DROPDOWN\">\n <number-dropdown-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </number-dropdown-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.NUMBER_SLIDER\">\n <number-slider-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </number-slider-input>\n </div>\n\n <!-------------------------------------------->\n <!-------------------Array-------------------->\n <!-------------------------------------------->\n <div *ngSwitchCase=\"DecoratorTypes.ARRAY_DATE\">\n <array-date-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </array-date-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.ARRAY_DATE_TIME\">\n <array-date-time-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </array-date-time-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.ARRAY_DATE_RANGE\">\n <array-date-range-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </array-date-range-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.ARRAY_STRING_CHIPS\">\n <array-string-chips-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </array-string-chips-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.ARRAY_STRING_AUTOCOMPLETE_CHIPS\">\n <array-string-autocomplete-chips\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </array-string-autocomplete-chips>\n </div>\n\n <!-------------------------------------------->\n <!-------------------Dates-------------------->\n <!-------------------------------------------->\n <div *ngSwitchCase=\"DecoratorTypes.DATE\">\n <date-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </date-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.DATE_RANGE\">\n <date-range-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </date-range-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.DATE_TIME\">\n <date-time-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </date-time-input>\n </div>\n\n <!-------------------------------------------->\n <!-------------------Files-------------------->\n <!-------------------------------------------->\n <div *ngSwitchCase=\"DecoratorTypes.FILE_DEFAULT\">\n <file-default-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </file-default-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.FILE_IMAGE\">\n <file-image-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </file-image-input>\n </div>\n\n <!-------------------------------------------->\n <!-------------- references many ------------->\n <!-------------------------------------------->\n <div *ngSwitchCase=\"DecoratorTypes.REFERENCES_MANY\">\n <references-many-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </references-many-input>\n </div>\n\n <!-------------------------------------------->\n <!-------------------Custom------------------->\n <!-------------------------------------------->\n <div *ngSwitchCase=\"DecoratorTypes.CUSTOM\">\n <custom-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </custom-input>\n </div>\n\n <!-------------------------------------------->\n <!-------------------Object------------------->\n <!-------------------------------------------->\n <div *ngSwitchCase=\"DecoratorTypes.OBJECT\">\n <b>{{metadataDefaultObject.displayName}}</b>\n <!-- iterates over the object properties -->\n <mat-tab-group *ngIf=\"objectPropertyTabs.length > 1\" preserveContent>\n <mat-tab *ngFor=\"let tab of objectPropertyTabs; let tI = index; trackBy: trackByFn\" [label]=\"tab.tabName\">\n <div class=\"row\" *ngFor=\"let row of tab.rows;\">\n <ngx-mat-entity-input *ngFor=\"let key of row.keys; let rI = index; trackBy: trackByFn\"\n [entity]=\"objectProperty\"\n [propertyKey]=\"key\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [hideOmitForCreate]=\"hideOmitForCreate\"\n [hideOmitForEdit]=\"hideOmitForEdit\"\n [validEmpty]=\"!metadata.required(entity)\"\n [isReadOnly]=\"isPropertyReadOnly(objectProperty, key)\"\n class=\"col-lg-{{EntityUtilities.getWidth(objectProperty, key, 'lg')}} col-md-{{EntityUtilities.getWidth(objectProperty, key, 'md')}} col-sm-{{EntityUtilities.getWidth(objectProperty, key, 'sm')}}\"\n (inputChangeEvent)=\"emitChange()\"\n >\n </ngx-mat-entity-input>\n </div>\n </mat-tab>\n </mat-tab-group>\n\n <div *ngIf=\"objectPropertyTabs.length <= 1\">\n <div class=\"row\" *ngFor=\"let row of objectPropertyTabs[0].rows\">\n <ngx-mat-entity-input *ngFor=\"let key of row.keys; let i = index; trackBy: trackByFn\"\n [entity]=\"objectProperty\"\n [propertyKey]=\"key\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [hideOmitForCreate]=\"hideOmitForCreate\"\n [hideOmitForEdit]=\"hideOmitForEdit\"\n [validEmpty]=\"!metadata.required(entity)\"\n [isReadOnly]=\"isPropertyReadOnly(objectProperty, key)\"\n class=\"col-lg-{{EntityUtilities.getWidth(objectProperty, key, 'lg')}} col-md-{{EntityUtilities.getWidth(objectProperty, key, 'md')}} col-sm-{{EntityUtilities.getWidth(objectProperty, key, 'sm')}}\"\n (inputChangeEvent)=\"emitChange()\"\n >\n </ngx-mat-entity-input>\n </div>\n </div>\n </div>\n\n <!-------------------------------------------->\n <!-------------- references one ------------->\n <!-------------------------------------------->\n <div *ngSwitchCase=\"DecoratorTypes.REFERENCES_ONE\">\n <mat-form-field>\n <mat-label>{{metadata.displayName}}</mat-label>\n <mat-select [(ngModel)]=\"internalEntity[internalPropertyKey]\"\n [name]=\"internalPropertyKey.toString() + 'input' + referencesOneUUID\"\n #inputModel=\"ngModel\"\n [disabled]=\"internalIsReadOnly\"\n [required]=\"metadata.required(entity)\"\n (ngModelChange)=\"setReferencesOneObject()\"\n >\n <mat-option *ngIf=\"!metadata.required(entity)\">-</mat-option>\n <mat-option *ngFor=\"let value of referencesOneDropdownValues\" [value]=\"value.value\">{{value.displayName}}</mat-option>\n </mat-select>\n <mat-error>{{internalGetValidationErrorMessage(inputModel)}}</mat-error>\n </mat-form-field>\n <!-- iterates over the references one properties -->\n <mat-tab-group *ngIf=\"referencesOnePropertyTabs && referencesOnePropertyTabs.length > 1\" preserveContent>\n <mat-tab *ngFor=\"let tab of referencesOnePropertyTabs; let tI = index; trackBy: trackByFn\" [label]=\"tab.tabName\">\n <div class=\"row\" *ngFor=\"let row of tab.rows;\">\n <ngx-mat-entity-input *ngFor=\"let key of row.keys; let rI = index; trackBy: trackByFn\"\n [entity]=\"referencesOneObject\"\n [propertyKey]=\"key\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [hideOmitForCreate]=\"hideOmitForCreate\"\n [hideOmitForEdit]=\"hideOmitForEdit\"\n [validEmpty]=\"!metadata.required(entity)\"\n [isReadOnly]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(referencesOneObject, key, 'lg')}} col-md-{{EntityUtilities.getWidth(referencesOneObject, key, 'md')}} col-sm-{{EntityUtilities.getWidth(referencesOneObject, key, 'sm')}}\"\n (inputChangeEvent)=\"emitChange()\"\n >\n </ngx-mat-entity-input>\n </div>\n </mat-tab>\n </mat-tab-group>\n\n <div *ngIf=\"referencesOnePropertyTabs && referencesOnePropertyTabs.length <= 1\">\n <div class=\"row\" *ngFor=\"let row of referencesOnePropertyTabs[0].rows\">\n <ngx-mat-entity-input *ngFor=\"let key of row.keys; let i = index; trackBy: trackByFn\"\n [entity]=\"referencesOneObject\"\n [propertyKey]=\"key\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [hideOmitForCreate]=\"hideOmitForCreate\"\n [hideOmitForEdit]=\"hideOmitForEdit\"\n [validEmpty]=\"!metadata.required(entity)\"\n [isReadOnly]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(referencesOneObject, key, 'lg')}} col-md-{{EntityUtilities.getWidth(referencesOneObject, key, 'md')}} col-sm-{{EntityUtilities.getWidth(referencesOneObject, key, 'sm')}}\"\n (inputChangeEvent)=\"emitChange()\"\n >\n </ngx-mat-entity-input>\n </div>\n </div>\n </div>\n\n <!-------------------------------------------->\n <!-------------------Array-------------------->\n <!-------------------------------------------->\n <div class=\"entityArray\" *ngSwitchCase=\"DecoratorTypes.ARRAY\">\n <div class=\"mat-elevation-z8 elevation-container\">\n <div class=\"array-headline\">\n <b>{{metadataEntityArray.displayName}}</b>\n </div>\n <div *ngIf=\"metadataEntityArray.createInline && !internalIsReadOnly\">\n <mat-tab-group *ngIf=\"arrayItemInlineTabs.length > 1\" preserveContent>\n <mat-tab *ngFor=\"let tab of arrayItemInlineTabs\" [label]=\"tab.tabName\">\n <div class=\"row\" *ngFor=\"let row of tab.rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys; let i = index; trackBy: trackByFn\"\n [entity]=\"arrayItem\"\n [propertyKey]=\"key\"\n [hideOmitForCreate]=\"true\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n class=\"col-lg-{{EntityUtilities.getWidth(arrayItem, key, 'lg')}} col-md-{{EntityUtilities.getWidth(arrayItem, key, 'md')}} col-sm-{{EntityUtilities.getWidth(arrayItem, key, 'sm')}}\"\n (inputChangeEvent)=\"checkIsArrayItemValid()\"\n >\n </ngx-mat-entity-input>\n </div>\n </mat-tab>\n </mat-tab-group>\n\n <div *ngIf=\"arrayItemInlineTabs.length <= 1\">\n <div class=\"row\" *ngFor=\"let row of arrayItemInlineTabs[0].rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys; let i = index; trackBy: trackByFn\"\n [entity]=\"arrayItem\"\n [propertyKey]=\"key\"\n [hideOmitForCreate]=\"true\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n class=\"col-lg-{{EntityUtilities.getWidth(arrayItem, key, 'lg')}} col-md-{{EntityUtilities.getWidth(arrayItem, key, 'md')}} col-sm-{{EntityUtilities.getWidth(arrayItem, key, 'sm')}}\"\n (inputChangeEvent)=\"checkIsArrayItemValid()\"\n >\n </ngx-mat-entity-input>\n </div>\n </div>\n </div>\n\n <div class=\"buttons\" *ngIf=\"!internalIsReadOnly\">\n <button type=\"button\" mat-raised-button\n [disabled]=\"metadataEntityArray.createInline && !isArrayItemValid\"\n (click)=\"addEntity()\">\n {{metadataEntityArray.addButtonLabel}}\n </button>\n <button type=\"button\" mat-raised-button\n [disabled]=\"!entityArraySelection.selected.length\"\n (click)=\"removeFromEntityArray()\">\n {{metadataEntityArray.removeButtonLabel}}\n </button>\n </div>\n \n <mat-table [dataSource]=\"entityArrayDataSource\">\n <!-- select Column -->\n <ng-container matColumnDef=\"select\" *ngIf=\"!internalIsReadOnly\">\n <mat-header-cell *matHeaderCellDef>\n <mat-checkbox\n [disabled]=\"!entityArrayDataSource.data.length\" (change)=\"$event ? SelectionUtilities.masterToggle(entityArraySelection, entityArrayDataSource) : null\"\n [checked]=\"entityArraySelection.hasValue() && SelectionUtilities.isAllSelected(entityArraySelection, entityArrayDataSource)\"\n [indeterminate]=\"entityArraySelection.hasValue() && !SelectionUtilities.isAllSelected(entityArraySelection, entityArrayDataSource)\">\n </mat-checkbox>\n </mat-header-cell>\n <mat-cell *matCellDef=\"let entity\">\n <mat-checkbox (click)=\"$event.stopPropagation()\" (change)=\"$event ? entityArraySelection.toggle(entity) : null\" [checked]=\"entityArraySelection.isSelected(entity)\"></mat-checkbox>\n </mat-cell>\n </ng-container>\n \n <ng-container *ngFor=\"let dCol of metadataEntityArray.displayColumns\" [matColumnDef]=\"dCol.displayName\">\n <mat-header-cell *matHeaderCellDef>\n {{dCol.displayName}}\n </mat-header-cell>\n <mat-cell [class.enabled]=\"!dCol.disableClick\" (click)=\"editArrayItem(entity, dCol)\" *matCellDef=\"let entity\">\n <ng-container *ngIf=\"dCol.Component\">\n <display-column-value [entity]=\"entity\" [ComponentClass]=\"dCol.Component\"></display-column-value>\n </ng-container>\n <ng-container *ngIf=\"!dCol.Component\">\n {{getDisplayColumnValue(entity, dCol)}}\n </ng-container>\n </mat-cell>\n </ng-container>\n \n <mat-header-row *matHeaderRowDef=\"entityArrayDisplayedColumns\"></mat-header-row>\n <mat-row *matRowDef=\"let row; columns: entityArrayDisplayedColumns\"></mat-row>\n </mat-table>\n \n <div class=\"array-error\" *ngIf=\"metadataEntityArray.required(entity) && !entityArrayDataSource.data.length\">\n {{metadataEntityArray.missingErrorMessage}}\n </div>\n </div>\n </div>\n\n <!-------------------------------------------->\n <!------------------ has many ---------------->\n <!-------------------------------------------->\n <div class=\"hasMany\" *ngSwitchCase=\"DecoratorTypes.HAS_MANY\">\n <h2 class=\"title\">{{metadataHasMany.tableData.baseData.title}}</h2>\n\n <div class=\"row\">\n <mat-form-field class=\"col-lg-8 col-md-6 col-sm-12\">\n <mat-label>{{metadataHasMany.tableData.baseData.searchLabel}}</mat-label>\n <input matInput (keyup)=\"applyHasManyFilter($event)\">\n </mat-form-field>\n <div\n *ngIf=\"metadataHasMany.tableData.baseData.tableActions.length\"\n [class.col-lg-2]=\"hasManyAllowCreate\"\n [class.col-lg-4]=\"!hasManyAllowCreate\"\n [class.col-md-3]=\"hasManyAllowCreate\"\n [class.col-md-6]=\"!hasManyAllowCreate\"\n [class.col-sm-6]=\"hasManyAllowCreate\"\n [class.col-sm-12]=\"!hasManyAllowCreate\"\n >\n <button type=\"button\" class=\"actions-button\" [matMenuTriggerFor]=\"menu\" mat-raised-button>\n {{metadataHasMany.tableData.baseData.tableActionsLabel}}\n </button>\n </div>\n <mat-menu #menu=\"matMenu\">\n <button *ngIf=\"metadataHasMany.tableData.baseData.allowJsonImport\" type=\"button\" [disabled]=\"hasManyTableActionDisabled(hasManyImportAction)\" (click)=\"runHasManyTableAction(hasManyImportAction)\" mat-menu-item>\n {{hasManyImportAction.displayName}}\n </button>\n <button type=\"button\" *ngFor=\"let action of metadataHasMany.tableData.baseData.tableActions\" [disabled]=\"hasManyTableActionDisabled(action)\" (click)=\"runHasManyTableAction(action)\" mat-menu-item>\n {{action.displayName}}\n </button>\n </mat-menu>\n\n <div\n *ngIf=\"hasManyAllowCreate\"\n [class.col-lg-2]=\"metadataHasMany.tableData.baseData.tableActions.length\"\n [class.col-lg-4]=\"!metadataHasMany.tableData.baseData.tableActions.length\"\n [class.col-md-3]=\"metadataHasMany.tableData.baseData.tableActions.length\"\n [class.col-md-6]=\"!metadataHasMany.tableData.baseData.tableActions.length\"\n [class.col-sm-6]=\"metadataHasMany.tableData.baseData.tableActions.length\"\n [class.col-sm-12]=\"!metadataHasMany.tableData.baseData.tableActions.length\"\n >\n <button type=\"button\" class=\"create-button\" (click)=\"createHasManyEntity()\" mat-raised-button>\n {{metadataHasMany.tableData.baseData.createButtonLabel}}\n </button>\n </div>\n </div>\n\n <div class=\"mat-elevation-z8 elevation-container\">\n <mat-table [dataSource]=\"hasManyDataSource\" matSort>\n <!-- select Column -->\n <ng-container matColumnDef=\"select\">\n <mat-header-cell *matHeaderCellDef>\n <mat-checkbox (change)=\"$event ? SelectionUtilities.masterToggle(hasManySelection, hasManyDataSource) : null\" [checked]=\"hasManySelection.hasValue() && SelectionUtilities.isAllSelected(hasManySelection, hasManyDataSource)\" [indeterminate]=\"hasManySelection.hasValue() && !SelectionUtilities.isAllSelected(hasManySelection, hasManyDataSource)\"> </mat-checkbox>\n </mat-header-cell>\n <mat-cell *matCellDef=\"let entity\" class=\"enabled\">\n <mat-checkbox (click)=\"$event.stopPropagation()\" (change)=\"$event ? hasManySelection.toggle(entity) : null\" [checked]=\"hasManySelection.isSelected(entity)\"> </mat-checkbox>\n </mat-cell>\n </ng-container>\n\n <ng-container *ngFor=\"let dCol of metadataHasMany.tableData.baseData.displayColumns\" [matColumnDef]=\"dCol.displayName\">\n <mat-header-cell *matHeaderCellDef mat-sort-header>\n {{dCol.displayName}}\n </mat-header-cell>\n <mat-cell [class.enabled]=\"!dCol.disableClick && (hasManyAllowUpdate(entity) || hasManyAllowRead(entity))\" (click)=\"editHasManyEntity(entity, dCol)\" *matCellDef=\"let entity\">\n <ng-container *ngIf=\"dCol.Component\">\n <display-column-value [entity]=\"entity\" [ComponentClass]=\"dCol.Component\"></display-column-value>\n </ng-container>\n <ng-container *ngIf=\"!dCol.Component\">\n {{getDisplayColumnValue(entity, dCol)}}\n </ng-container>\n </mat-cell>\n </ng-container>\n\n <mat-header-row *matHeaderRowDef=\"displayedHasManyColumns\"></mat-header-row>\n <mat-row *matRowDef=\"let row; columns: displayedHasManyColumns\"></mat-row>\n </mat-table>\n\n <mat-spinner *ngIf=\"hasManyIsLoading && metadataHasMany.tableData.baseData.displayLoadingSpinner\">\n </mat-spinner>\n\n <mat-paginator [length]=\"hasManyDataSource.filteredData.length\" [pageIndex]=\"0\" [pageSize]=\"10\" [pageSizeOptions]=\"[5, 10, 25, 50]\"></mat-paginator>\n </div>\n </div>\n\n <div *ngSwitchDefault>ERROR: The type {{type}} is not known.</div>\n</div>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!--------------------------------------------------------->\n<!--------------------Add Array Item Dialog---------------->\n<!--------------------------------------------------------->\n<ng-template #addArrayItemDialog>\n <div class=\"mat-dialog-title\">\n <div>{{addArrayItemDialogData.title}}</div>\n </div>\n\n <form>\n <mat-dialog-content>\n <mat-tab-group *ngIf=\"arrayItemDialogTabs.length > 1\" preserveContent>\n <mat-tab *ngFor=\"let tab of arrayItemDialogTabs\" [label]=\"tab.tabName\">\n <div class=\"row\" *ngFor=\"let row of tab.rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"arrayItem\"\n [propertyKey]=\"key\"\n [hideOmitForCreate]=\"true\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n class=\"col-lg-{{EntityUtilities.getWidth(arrayItem, key, 'lg')}} col-md-{{EntityUtilities.getWidth(arrayItem, key, 'md')}} col-sm-{{EntityUtilities.getWidth(arrayItem, key, 'sm')}}\"\n (inputChangeEvent)=\"checkIsArrayItemValid()\"\n >\n </ngx-mat-entity-input>\n </div>\n </mat-tab>\n </mat-tab-group>\n \n <div *ngIf=\"arrayItemDialogTabs.length <= 1\">\n <div class=\"row\" *ngFor=\"let row of arrayItemDialogTabs[0].rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"arrayItem\"\n [propertyKey]=\"key\"\n [hideOmitForCreate]=\"true\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n class=\"col-lg-{{EntityUtilities.getWidth(arrayItem, key, 'lg')}} col-md-{{EntityUtilities.getWidth(arrayItem, key, 'md')}} col-sm-{{EntityUtilities.getWidth(arrayItem, key, 'sm')}}\"\n (inputChangeEvent)=\"checkIsArrayItemValid()\"\n >\n </ngx-mat-entity-input>\n </div>\n </div>\n </mat-dialog-content>\n \n <mat-dialog-actions>\n <button type=\"submit\" mat-raised-button (click)=\"addArrayItem()\" [disabled]=\"!isArrayItemValid\">\n {{addArrayItemDialogData.createButtonLabel}}\n </button>\n <button type=\"button\" mat-raised-button (click)=\"closeAddArrayItemDialog()\" class=\"cancel-button\">\n {{addArrayItemDialogData.cancelButtonLabel}}\n </button>\n </mat-dialog-actions>\n </form>\n\n</ng-template>\n\n<!--------------------------------------------------------->\n<!--------------------Edit Array Item Dialog---------------->\n<!--------------------------------------------------------->\n<ng-template #editArrayItemDialog>\n <div class=\"mat-dialog-title\">\n <div>{{editArrayItemDialogData.title(arrayItemPriorChanges)}}</div>\n </div>\n \n <form>\n <mat-dialog-content>\n <mat-tab-group *ngIf=\"arrayItemDialogTabs.length > 1\" preserveContent>\n <mat-tab *ngFor=\"let tab of arrayItemDialogTabs\" [label]=\"tab.tabName\">\n <div class=\"row\" *ngFor=\"let row of tab.rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"arrayItem\"\n [propertyKey]=\"key\"\n [hideOmitForEdit]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(arrayItem, key, 'lg')}} col-md-{{EntityUtilities.getWidth(arrayItem, key, 'md')}} col-sm-{{EntityUtilities.getWidth(arrayItem, key, 'sm')}}\"\n (inputChangeEvent)=\"checkArrayItem()\"\n [isReadOnly]=\"isPropertyReadOnly(arrayItem, key)\"\n >\n </ngx-mat-entity-input>\n </div>\n </mat-tab>\n </mat-tab-group>\n \n <div *ngIf=\"arrayItemDialogTabs.length <= 1\">\n <div class=\"row\" *ngFor=\"let row of arrayItemDialogTabs[0].rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"arrayItem\"\n [propertyKey]=\"key\"\n [hideOmitForEdit]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(arrayItem, key, 'lg')}} col-md-{{EntityUtilities.getWidth(arrayItem, key, 'md')}} col-sm-{{EntityUtilities.getWidth(arrayItem, key, 'sm')}}\"\n (inputChangeEvent)=\"checkArrayItem()\"\n [isReadOnly]=\"isPropertyReadOnly(arrayItem, key)\"\n >\n </ngx-mat-entity-input>\n </div>\n </div>\n </mat-dialog-content>\n \n <mat-dialog-actions>\n <button type=\"submit\" (click)=\"saveArrayItem()\" mat-raised-button [disabled]=\"internalIsReadOnly || !isArrayItemValid || !isArrayItemDirty\">\n {{editArrayItemDialogData.confirmButtonLabel}}\n </button>\n <button type=\"button\" mat-raised-button (click)=\"closeEditArrayItemDialog()\" class=\"cancel-button\">\n {{editArrayItemDialogData.cancelButtonLabel}}\n </button>\n </mat-dialog-actions>\n </form>\n</ng-template>\n\n<!--------------------------------------------------------->\n<!--------------------Create Has Many Dialog---------------->\n<!--------------------------------------------------------->\n<ng-template #createHasManyDialog>\n <div class=\"mat-dialog-title\">\n <div>{{metadataHasMany.tableData.createDialogData.title}}</div>\n </div>\n \n <form>\n <mat-dialog-content>\n <mat-tab-group *ngIf=\"hasManyCreateTabs.length > 1\" preserveContent>\n <mat-tab *ngFor=\"let tab of hasManyCreateTabs\" [label]=\"tab.tabName\">\n <div class=\"row\" *ngFor=\"let row of tab.rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"hasManyEntity\"\n [propertyKey]=\"key\"\n [hideOmitForCreate]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(hasManyEntity, key, 'lg')}} col-md-{{EntityUtilities.getWidth(hasManyEntity, key, 'md')}} col-sm-{{EntityUtilities.getWidth(hasManyEntity, key, 'sm')}}\"\n (inputChangeEvent)=\"checkIsHasManyEntityValid('create')\"\n >\n </ngx-mat-entity-input>\n </div>\n </mat-tab>\n </mat-tab-group>\n \n <div *ngIf=\"hasManyCreateTabs.length <= 1\">\n <div class=\"row\" *ngFor=\"let row of hasManyCreateTabs[0].rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"hasManyEntity\"\n [propertyKey]=\"key\"\n [hideOmitForCreate]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(hasManyEntity, key, 'lg')}} col-md-{{EntityUtilities.getWidth(hasManyEntity, key, 'md')}} col-sm-{{EntityUtilities.getWidth(hasManyEntity, key, 'sm')}}\"\n (inputChangeEvent)=\"checkIsHasManyEntityValid('create')\"\n >\n </ngx-mat-entity-input>\n </div>\n </div>\n </mat-dialog-content>\n \n <mat-dialog-actions>\n <button type=\"submit\" (click)=\"dialogCreateHasMany()\" mat-raised-button [disabled]=\"!isHasManyEntityValid\">\n {{metadataHasMany.tableData.createDialogData.createButtonLabel}}\n </button>\n <button type=\"button\" mat-raised-button (click)=\"dialogCancelCreateHasMany()\" class=\"cancel-button\">\n {{metadataHasMany.tableData.createDialogData.cancelButtonLabel}}\n </button>\n </mat-dialog-actions>\n </form>\n \n</ng-template>\n\n<ng-template #editHasManyDialog>\n <div class=\"mat-dialog-title\">\n <div>{{metadataHasMany.tableData.editData.title(hasManyEntityPriorChanges)}}</div>\n <button type=\"button\" *ngIf=\"hasManyAllowDelete(hasManyEntity)\" mat-raised-button (click)=\"deleteHasManyEntity()\" color=\"warn\" class=\"delete-button\" tabindex=\"-1\">\n {{metadataHasMany.tableData.editData.deleteButtonLabel}}\n </button>\n </div>\n \n <form>\n <mat-dialog-content>\n <mat-tab-group *ngIf=\"hasManyUpdateTabs.length > 1\" preserveContent>\n <mat-tab *ngFor=\"let tab of hasManyUpdateTabs\" [label]=\"tab.tabName\">\n <div class=\"row\" *ngFor=\"let row of tab.rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"hasManyEntity\"\n [propertyKey]=\"key\"\n [hideOmitForEdit]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(hasManyEntity, key, 'lg')}} col-md-{{EntityUtilities.getWidth(hasManyEntity, key, 'md')}} col-sm-{{EntityUtilities.getWidth(hasManyEntity, key, 'sm')}}\"\n (inputChangeEvent)=\"checkHasManyEntity()\"\n [isReadOnly]=\"isPropertyReadOnly(hasManyEntity, key)\"\n >\n </ngx-mat-entity-input>\n </div>\n </mat-tab>\n </mat-tab-group>\n \n <div *ngIf=\"hasManyUpdateTabs.length <= 1\">\n <div class=\"row\" *ngFor=\"let row of hasManyUpdateTabs[0].rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"hasManyEntity\"\n [propertyKey]=\"key\"\n [hideOmitForEdit]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(hasManyEntity, key, 'lg')}} col-md-{{EntityUtilities.getWidth(hasManyEntity, key, 'md')}} col-sm-{{EntityUtilities.getWidth(hasManyEntity, key, 'sm')}}\"\n (inputChangeEvent)=\"checkHasManyEntity()\"\n [isReadOnly]=\"isPropertyReadOnly(hasManyEntity, key)\"\n >\n </ngx-mat-entity-input>\n </div>\n </div>\n </mat-dialog-content>\n \n <mat-dialog-actions>\n <button type=\"submit\" (click)=\"dialogEditHasMany()\" mat-raised-button [disabled]=\"internalIsReadOnly || !isHasManyEntityValid || !isHasManyEntityDirty\">\n {{metadataHasMany.tableData.editData.confirmButtonLabel}}\n </button>\n <button type=\"button\" mat-raised-button (click)=\"dialogCancelEditHasMany()\" class=\"cancel-button\">\n {{metadataHasMany.tableData.editData.cancelButtonLabel}}\n </button>\n </mat-dialog-actions>\n </form> \n</ng-template>", styles: [".mdc-data-table__row:last-child .mdc-data-table__cell{border-bottom:1px solid rgba(0,0,0,.12)}.mat-dialog-title{padding:12px 20px;display:flex;justify-content:space-between;align-items:center}.mat-dialog-title div{font-size:var(--mdc-dialog-subhead-size, 14px);font-weight:var(--mdc-dialog-subhead-weight, 500)}::ng-deep .mdc-dialog .mdc-dialog__content{padding:6px 20px!important}mat-dialog-actions{justify-content:space-between;align-items:center;padding-left:20px;padding-right:20px}mat-spinner{margin:10px auto}::ng-deep .mat-mdc-tab-body-wrapper{margin-left:-12px;margin-right:-12px}::ng-deep mat-tab-body{padding-top:10px;padding-left:12px;padding-right:12px}::ng-deep mat-tab-body .mat-mdc-tab-body-content{overflow:initial}::ng-deep .mat-mdc-form-field.mat-form-field-disabled label{color:#0009}::ng-deep .mat-mdc-form-field.mat-form-field-disabled input,::ng-deep .mat-mdc-form-field.mat-form-field-disabled textarea,::ng-deep .mat-mdc-form-field.mat-form-field-disabled .mat-mdc-select-disabled,::ng-deep .mat-mdc-form-field.mat-form-field-disabled .mat-mdc-select-value{color:#000}::ng-deep .mat-mdc-form-field.mat-form-field-disabled .mat-mdc-chip.mat-mdc-standard-chip.mat-mdc-chip-disabled{opacity:1}::ng-deep .mat-mdc-form-field.mat-form-field-disabled .mat-mdc-chip.mat-mdc-standard-chip.mat-mdc-chip-disabled button{opacity:.2}::ng-deep .mat-mdc-form-field.mat-form-field-disabled .mat-date-range-input-inner:disabled{color:#000}::ng-deep .mat-mdc-form-field .mat-mdc-slide-toggle{opacity:1}.entityArray .elevation-container{border-radius:5px;padding:15px;margin-bottom:15px;margin-top:15px}.entityArray .array-headline{padding-bottom:10px}.entityArray .buttons{display:flex;justify-content:space-between;margin-bottom:10px;margin-top:5px}.entityArray mat-table{border:1px solid #E0E0E0;border-radius:5px;padding-top:5px;padding-bottom:25px}.entityArray .mat-column-select{flex:0 0 75px}.entityArray .enabled:hover{cursor:pointer}.entityArray .array-error{display:flex;align-items:center;justify-content:center;margin-top:-25.8px;border:1px solid #E0E0E0;background-color:#f8d3d7;color:#721c24;height:25.8px;border-bottom-left-radius:5px;border-bottom-right-radius:5px}.hasMany button{width:100%;height:56px;line-height:24px;font-size:16px}.hasMany .title{text-align:center}.hasMany .elevation-container{border-radius:5px;padding:5px;margin-bottom:15px}.hasMany .mat-column-select{flex:0 0 75px}.hasMany .enabled:hover{cursor:pointer}\n"], dependencies: [{ kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$1.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i1$1.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: i1$1.NgSwitchDefault, selector: "[ngSwitchDefault]" }, { kind: "directive", type: i2$1.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "component", type: i2$2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$2.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$2.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i3.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i3.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: i4$2.MatSelect, selector: "mat-select", inputs: ["disabled", "disableRipple", "tabIndex", "panelWidth", "hideSingleSelectionIndicator"], exportAs: ["matSelect"] }, { kind: "component", type: i5$1.MatOption, selector: "mat-option", exportAs: ["matOption"] }, { kind: "component", type: i4.MatCheckbox, selector: "mat-checkbox", inputs: ["disableRipple", "color", "tabIndex"], exportAs: ["matCheckbox"] }, { kind: "component", type: i6.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i6.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i6.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i6.MatColumnDef, selector: "[matColumnDef]", inputs: ["sticky", "matColumnDef"] }, { kind: "directive", type: i6.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i6.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i6.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i6.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i6.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i6.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "directive", type: i1.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "directive", type: i1.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "component", type: i5.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", inputs: ["disabled", "disableRipple", "color"], exportAs: ["matButton"] }, { kind: "component", type: i6$2.MatTab, selector: "mat-tab", inputs: ["disabled"], exportAs: ["matTab"] }, { kind: "component", type: i6$2.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "disableRipple", "fitInkBarToContent", "mat-stretch-tabs"], exportAs: ["matTabGroup"] }, { kind: "component", type: i9.MatMenu, selector: "mat-menu", exportAs: ["matMenu"] }, { kind: "component", type: i9.MatMenuItem, selector: "[mat-menu-item]", inputs: ["disabled", "disableRipple", "role"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i9.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", exportAs: ["matMenuTrigger"] }, { kind: "component", type: i15.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "component", type: i16.MatPaginator, selector: "mat-paginator", inputs: ["disabled"], exportAs: ["matPaginator"] }, { kind: "component", type: DisplayColumnValueComponent, selector: "display-column-value", inputs: ["entity", "ComponentClass"] }, { kind: "component", type: StringInputComponent, selector: "string-input" }, { kind: "component", type: StringTextboxInputComponent, selector: "string-textbox-input" }, { kind: "component", type: StringAutocompleteInputComponent, selector: "string-autocomplete-input" }, { kind: "component", type: StringDropdownInputComponent, selector: "string-dropdown-input" }, { kind: "component", type: StringPasswordInputComponent, selector: "string-password-input" }, { kind: "component", type: BooleanCheckboxInputComponent, selector: "boolean-checkbox-input" }, { kind: "component", type: BooleanToggleInputComponent, selector: "boolean-toggle-input" }, { kind: "component", type: BooleanDropdownInputComponent, selector: "boolean-dropdown-input" }, { kind: "component", type: NumberInputComponent, selector: "number-input" }, { kind: "component", type: NumberDropdownInputComponent, selector: "number-dropdown-input" }, { kind: "component", type: NumberSliderInputComponent, selector: "number-slider-input" }, { kind: "component", type: ArrayStringChipsInputComponent, selector: "array-string-chips-input" }, { kind: "component", type: ArrayStringAutocompleteChipsComponent, selector: "array-string-autocomplete-chips" }, { kind: "component", type: DateInputComponent, selector: "date-input" }, { kind: "component", type: DateRangeInputComponent, selector: "date-range-input" }, { kind: "component", type: DateTimeInputComponent, selector: "date-time-input" }, { kind: "component", type: ArrayDateInputComponent, selector: "array-date-input" }, { kind: "component", type: ArrayDateTimeInputComponent, selector: "array-date-time-input" }, { kind: "component", type: ArrayDateRangeInputComponent, selector: "array-date-range-input" }, { kind: "component", type: FileImageInputComponent, selector: "file-image-input" }, { kind: "component", type: FileDefaultInputComponent, selector: "file-default-input" }, { kind: "component", type: ReferencesManyInputComponent, selector: "references-many-input" }, { kind: "component", type: CustomInputComponent, selector: "custom-input" }, { kind: "component", type: NgxMatEntityInputComponent, selector: "ngx-mat-entity-input", inputs: ["entity", "propertyKey", "getValidationErrorMessage", "hideOmitForCreate", "hideOmitForEdit", "validEmpty", "isReadOnly"], outputs: ["inputChangeEvent"] }] });
4948
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.0", type: NgxMatEntityInputComponent, selector: "ngx-mat-entity-input", inputs: { entity: "entity", propertyKey: "propertyKey", getValidationErrorMessage: "getValidationErrorMessage", hideOmitForCreate: "hideOmitForCreate", hideOmitForEdit: "hideOmitForEdit", validEmpty: "validEmpty", isReadOnly: "isReadOnly" }, outputs: { inputChangeEvent: "inputChangeEvent" }, viewQueries: [{ propertyName: "addArrayItemDialog", first: true, predicate: ["addArrayItemDialog"], descendants: true }, { propertyName: "editArrayItemDialog", first: true, predicate: ["editArrayItemDialog"], descendants: true }, { propertyName: "hasManyPaginator", first: true, predicate: MatPaginator, descendants: true }, { propertyName: "hasManySort", first: true, predicate: MatSort, descendants: true }, { propertyName: "hasManyFilter", first: true, predicate: ["filter"], descendants: true, static: true }, { propertyName: "createHasManyDialog", first: true, predicate: ["createHasManyDialog"], descendants: true }, { propertyName: "editHasManyDialog", first: true, predicate: ["editHasManyDialog"], descendants: true }], ngImport: i0, template: "<div [ngSwitch]=\"type\" *ngIf=\"!(hideOmitForCreate && metadata.omitForCreate) && !(hideOmitForEdit && metadata.omitForUpdate)\">\n <!-------------------------------------------->\n <!-----------------Strings-------------------->\n <!-------------------------------------------->\n <div *ngSwitchCase=\"DecoratorTypes.STRING\">\n <string-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </string-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.STRING_TEXTBOX\">\n <string-textbox-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </string-textbox-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.STRING_AUTOCOMPLETE\">\n <string-autocomplete-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </string-autocomplete-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.STRING_DROPDOWN\">\n <string-dropdown-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </string-dropdown-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.STRING_PASSWORD\">\n <string-password-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </string-password-input>\n </div>\n\n <!-------------------------------------------->\n <!-----------------Booleans------------------->\n <!-------------------------------------------->\n <div *ngSwitchCase=\"DecoratorTypes.BOOLEAN_CHECKBOX\">\n <boolean-checkbox-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </boolean-checkbox-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.BOOLEAN_TOGGLE\">\n <boolean-toggle-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </boolean-toggle-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.BOOLEAN_DROPDOWN\">\n <boolean-dropdown-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </boolean-dropdown-input>\n </div>\n\n <!-------------------------------------------->\n <!------------------Numbers------------------->\n <!-------------------------------------------->\n <div *ngSwitchCase=\"DecoratorTypes.NUMBER\">\n <number-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </number-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.NUMBER_DROPDOWN\">\n <number-dropdown-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </number-dropdown-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.NUMBER_SLIDER\">\n <number-slider-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </number-slider-input>\n </div>\n\n <!-------------------------------------------->\n <!-------------------Array-------------------->\n <!-------------------------------------------->\n <div *ngSwitchCase=\"DecoratorTypes.ARRAY_DATE\">\n <array-date-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </array-date-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.ARRAY_DATE_TIME\">\n <array-date-time-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </array-date-time-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.ARRAY_DATE_RANGE\">\n <array-date-range-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </array-date-range-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.ARRAY_STRING_CHIPS\">\n <array-string-chips-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </array-string-chips-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.ARRAY_STRING_AUTOCOMPLETE_CHIPS\">\n <array-string-autocomplete-chips\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </array-string-autocomplete-chips>\n </div>\n\n <!-------------------------------------------->\n <!-------------------Dates-------------------->\n <!-------------------------------------------->\n <div *ngSwitchCase=\"DecoratorTypes.DATE\">\n <date-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </date-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.DATE_RANGE\">\n <date-range-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </date-range-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.DATE_TIME\">\n <date-time-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </date-time-input>\n </div>\n\n <!-------------------------------------------->\n <!-------------------Files-------------------->\n <!-------------------------------------------->\n <div *ngSwitchCase=\"DecoratorTypes.FILE_DEFAULT\">\n <file-default-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </file-default-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.FILE_IMAGE\">\n <file-image-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </file-image-input>\n </div>\n\n <!-------------------------------------------->\n <!-------------- references many ------------->\n <!-------------------------------------------->\n <div *ngSwitchCase=\"DecoratorTypes.REFERENCES_MANY\">\n <references-many-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </references-many-input>\n </div>\n\n <!-------------------------------------------->\n <!-------------------Custom------------------->\n <!-------------------------------------------->\n <div *ngSwitchCase=\"DecoratorTypes.CUSTOM\">\n <custom-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </custom-input>\n </div>\n\n <!-------------------------------------------->\n <!-------------------Object------------------->\n <!-------------------------------------------->\n <div *ngSwitchCase=\"DecoratorTypes.OBJECT\">\n <b>{{metadataDefaultObject.displayName}}</b>\n <!-- iterates over the object properties -->\n <mat-tab-group *ngIf=\"objectPropertyTabs.length > 1\" preserveContent>\n <mat-tab *ngFor=\"let tab of objectPropertyTabs; let tI = index; trackBy: trackByFn\" [label]=\"tab.tabName\">\n <div class=\"row\" *ngFor=\"let row of tab.rows;\">\n <ngx-mat-entity-input *ngFor=\"let key of row.keys; let rI = index; trackBy: trackByFn\"\n [entity]=\"objectProperty\"\n [propertyKey]=\"key\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [hideOmitForCreate]=\"hideOmitForCreate\"\n [hideOmitForEdit]=\"hideOmitForEdit\"\n [validEmpty]=\"!metadata.required(entity)\"\n [isReadOnly]=\"isPropertyReadOnly(objectProperty, key)\"\n class=\"col-lg-{{EntityUtilities.getWidth(objectProperty, key, 'lg')}} col-md-{{EntityUtilities.getWidth(objectProperty, key, 'md')}} col-sm-{{EntityUtilities.getWidth(objectProperty, key, 'sm')}}\"\n (inputChangeEvent)=\"emitChange()\"\n >\n </ngx-mat-entity-input>\n </div>\n </mat-tab>\n </mat-tab-group>\n\n <div *ngIf=\"objectPropertyTabs.length <= 1\">\n <div class=\"row\" *ngFor=\"let row of objectPropertyTabs[0].rows\">\n <ngx-mat-entity-input *ngFor=\"let key of row.keys; let i = index; trackBy: trackByFn\"\n [entity]=\"objectProperty\"\n [propertyKey]=\"key\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [hideOmitForCreate]=\"hideOmitForCreate\"\n [hideOmitForEdit]=\"hideOmitForEdit\"\n [validEmpty]=\"!metadata.required(entity)\"\n [isReadOnly]=\"isPropertyReadOnly(objectProperty, key)\"\n class=\"col-lg-{{EntityUtilities.getWidth(objectProperty, key, 'lg')}} col-md-{{EntityUtilities.getWidth(objectProperty, key, 'md')}} col-sm-{{EntityUtilities.getWidth(objectProperty, key, 'sm')}}\"\n (inputChangeEvent)=\"emitChange()\"\n >\n </ngx-mat-entity-input>\n </div>\n </div>\n </div>\n\n <!-------------------------------------------->\n <!-------------- references one ------------->\n <!-------------------------------------------->\n <div *ngSwitchCase=\"DecoratorTypes.REFERENCES_ONE\">\n <mat-form-field>\n <mat-label>{{metadata.displayName}}</mat-label>\n <mat-select [(ngModel)]=\"internalEntity[internalPropertyKey]\"\n [name]=\"internalPropertyKey.toString() + 'input' + referencesOneUUID\"\n #inputModel=\"ngModel\"\n [disabled]=\"internalIsReadOnly\"\n [required]=\"metadata.required(entity)\"\n (ngModelChange)=\"setReferencesOneObject()\"\n >\n <mat-option *ngIf=\"!metadata.required(entity)\">-</mat-option>\n <mat-option *ngFor=\"let value of referencesOneDropdownValues\" [value]=\"value.value\">{{value.displayName}}</mat-option>\n </mat-select>\n <mat-error>{{internalGetValidationErrorMessage(inputModel)}}</mat-error>\n </mat-form-field>\n <!-- iterates over the references one properties -->\n <mat-tab-group *ngIf=\"referencesOnePropertyTabs && referencesOnePropertyTabs.length > 1\" preserveContent>\n <mat-tab *ngFor=\"let tab of referencesOnePropertyTabs; let tI = index; trackBy: trackByFn\" [label]=\"tab.tabName\">\n <div class=\"row\" *ngFor=\"let row of tab.rows;\">\n <ngx-mat-entity-input *ngFor=\"let key of row.keys; let rI = index; trackBy: trackByFn\"\n [entity]=\"referencesOneObject\"\n [propertyKey]=\"key\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [hideOmitForCreate]=\"hideOmitForCreate\"\n [hideOmitForEdit]=\"hideOmitForEdit\"\n [validEmpty]=\"!metadata.required(entity)\"\n [isReadOnly]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(referencesOneObject, key, 'lg')}} col-md-{{EntityUtilities.getWidth(referencesOneObject, key, 'md')}} col-sm-{{EntityUtilities.getWidth(referencesOneObject, key, 'sm')}}\"\n (inputChangeEvent)=\"emitChange()\"\n >\n </ngx-mat-entity-input>\n </div>\n </mat-tab>\n </mat-tab-group>\n\n <div *ngIf=\"referencesOnePropertyTabs && referencesOnePropertyTabs.length <= 1\">\n <div class=\"row\" *ngFor=\"let row of referencesOnePropertyTabs[0].rows\">\n <ngx-mat-entity-input *ngFor=\"let key of row.keys; let i = index; trackBy: trackByFn\"\n [entity]=\"referencesOneObject\"\n [propertyKey]=\"key\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [hideOmitForCreate]=\"hideOmitForCreate\"\n [hideOmitForEdit]=\"hideOmitForEdit\"\n [validEmpty]=\"!metadata.required(entity)\"\n [isReadOnly]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(referencesOneObject, key, 'lg')}} col-md-{{EntityUtilities.getWidth(referencesOneObject, key, 'md')}} col-sm-{{EntityUtilities.getWidth(referencesOneObject, key, 'sm')}}\"\n (inputChangeEvent)=\"emitChange()\"\n >\n </ngx-mat-entity-input>\n </div>\n </div>\n </div>\n\n <!-------------------------------------------->\n <!-------------------Array-------------------->\n <!-------------------------------------------->\n <div class=\"entityArray\" *ngSwitchCase=\"DecoratorTypes.ARRAY\">\n <div class=\"mat-elevation-z8 elevation-container\">\n <div class=\"array-headline\">\n <b>{{metadataEntityArray.displayName}}</b>\n </div>\n <div *ngIf=\"metadataEntityArray.createInline && !internalIsReadOnly\">\n <mat-tab-group *ngIf=\"arrayItemInlineTabs.length > 1\" preserveContent>\n <mat-tab *ngFor=\"let tab of arrayItemInlineTabs\" [label]=\"tab.tabName\">\n <div class=\"row\" *ngFor=\"let row of tab.rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys; let i = index; trackBy: trackByFn\"\n [entity]=\"arrayItem\"\n [propertyKey]=\"key\"\n [hideOmitForCreate]=\"true\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n class=\"col-lg-{{EntityUtilities.getWidth(arrayItem, key, 'lg')}} col-md-{{EntityUtilities.getWidth(arrayItem, key, 'md')}} col-sm-{{EntityUtilities.getWidth(arrayItem, key, 'sm')}}\"\n (inputChangeEvent)=\"checkIsArrayItemValid()\"\n >\n </ngx-mat-entity-input>\n </div>\n </mat-tab>\n </mat-tab-group>\n\n <div *ngIf=\"arrayItemInlineTabs.length <= 1\">\n <div class=\"row\" *ngFor=\"let row of arrayItemInlineTabs[0].rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys; let i = index; trackBy: trackByFn\"\n [entity]=\"arrayItem\"\n [propertyKey]=\"key\"\n [hideOmitForCreate]=\"true\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n class=\"col-lg-{{EntityUtilities.getWidth(arrayItem, key, 'lg')}} col-md-{{EntityUtilities.getWidth(arrayItem, key, 'md')}} col-sm-{{EntityUtilities.getWidth(arrayItem, key, 'sm')}}\"\n (inputChangeEvent)=\"checkIsArrayItemValid()\"\n >\n </ngx-mat-entity-input>\n </div>\n </div>\n </div>\n\n <div class=\"buttons\" *ngIf=\"!internalIsReadOnly\">\n <button type=\"button\" mat-raised-button\n [disabled]=\"metadataEntityArray.createInline && !isArrayItemValid\"\n (click)=\"addEntity()\">\n {{metadataEntityArray.addButtonLabel}}\n </button>\n <button type=\"button\" mat-raised-button\n [disabled]=\"!entityArraySelection.selected.length\"\n (click)=\"removeFromEntityArray()\">\n {{metadataEntityArray.removeButtonLabel}}\n </button>\n </div>\n \n <mat-table [dataSource]=\"entityArrayDataSource\">\n <!-- select Column -->\n <ng-container matColumnDef=\"select\" *ngIf=\"!internalIsReadOnly\">\n <mat-header-cell *matHeaderCellDef>\n <mat-checkbox\n [disabled]=\"!entityArrayDataSource.data.length\" (change)=\"$event ? SelectionUtilities.masterToggle(entityArraySelection, entityArrayDataSource) : null\"\n [checked]=\"entityArraySelection.hasValue() && SelectionUtilities.isAllSelected(entityArraySelection, entityArrayDataSource)\"\n [indeterminate]=\"entityArraySelection.hasValue() && !SelectionUtilities.isAllSelected(entityArraySelection, entityArrayDataSource)\">\n </mat-checkbox>\n </mat-header-cell>\n <mat-cell *matCellDef=\"let entity\">\n <mat-checkbox (click)=\"$event.stopPropagation()\" (change)=\"$event ? entityArraySelection.toggle(entity) : null\" [checked]=\"entityArraySelection.isSelected(entity)\"></mat-checkbox>\n </mat-cell>\n </ng-container>\n \n <ng-container *ngFor=\"let dCol of metadataEntityArray.displayColumns\" [matColumnDef]=\"dCol.displayName\">\n <mat-header-cell *matHeaderCellDef>\n {{dCol.displayName}}\n </mat-header-cell>\n <mat-cell [class.enabled]=\"!dCol.disableClick\" (click)=\"editArrayItem(entity, dCol)\" *matCellDef=\"let entity\">\n <ng-container *ngIf=\"dCol.Component\">\n <display-column-value [entity]=\"entity\" [ComponentClass]=\"dCol.Component\"></display-column-value>\n </ng-container>\n <ng-container *ngIf=\"!dCol.Component\">\n {{getDisplayColumnValue(entity, dCol)}}\n </ng-container>\n </mat-cell>\n </ng-container>\n \n <mat-header-row *matHeaderRowDef=\"entityArrayDisplayedColumns\"></mat-header-row>\n <mat-row *matRowDef=\"let row; columns: entityArrayDisplayedColumns\"></mat-row>\n </mat-table>\n \n <div class=\"array-error\" *ngIf=\"metadataEntityArray.required(entity) && !entityArrayDataSource.data.length\">\n {{metadataEntityArray.missingErrorMessage}}\n </div>\n </div>\n </div>\n\n <!-------------------------------------------->\n <!------------------ has many ---------------->\n <!-------------------------------------------->\n <div class=\"hasMany\" *ngSwitchCase=\"DecoratorTypes.HAS_MANY\">\n <h2 class=\"title\">{{metadataHasMany.tableData.baseData.title}}</h2>\n\n <div class=\"row\">\n <mat-form-field class=\"col-lg-8 col-md-6 col-sm-12\">\n <mat-label>{{metadataHasMany.tableData.baseData.searchLabel}}</mat-label>\n <input matInput (keyup)=\"applyHasManyFilter($event)\">\n </mat-form-field>\n <div\n *ngIf=\"metadataHasMany.tableData.baseData.tableActions.length\"\n [class.col-lg-2]=\"hasManyAllowCreate\"\n [class.col-lg-4]=\"!hasManyAllowCreate\"\n [class.col-md-3]=\"hasManyAllowCreate\"\n [class.col-md-6]=\"!hasManyAllowCreate\"\n [class.col-sm-6]=\"hasManyAllowCreate\"\n [class.col-sm-12]=\"!hasManyAllowCreate\"\n >\n <button type=\"button\" class=\"actions-button\" [matMenuTriggerFor]=\"menu\" mat-raised-button>\n {{metadataHasMany.tableData.baseData.tableActionsLabel}}\n </button>\n </div>\n <mat-menu #menu=\"matMenu\">\n <button *ngIf=\"metadataHasMany.tableData.baseData.allowJsonImport\" type=\"button\" [disabled]=\"hasManyTableActionDisabled(hasManyImportAction)\" (click)=\"runHasManyTableAction(hasManyImportAction)\" mat-menu-item>\n {{hasManyImportAction.displayName}}\n </button>\n <button type=\"button\" *ngFor=\"let action of metadataHasMany.tableData.baseData.tableActions\" [disabled]=\"hasManyTableActionDisabled(action)\" (click)=\"runHasManyTableAction(action)\" mat-menu-item>\n {{action.displayName}}\n </button>\n </mat-menu>\n\n <div\n *ngIf=\"hasManyAllowCreate\"\n [class.col-lg-2]=\"metadataHasMany.tableData.baseData.tableActions.length\"\n [class.col-lg-4]=\"!metadataHasMany.tableData.baseData.tableActions.length\"\n [class.col-md-3]=\"metadataHasMany.tableData.baseData.tableActions.length\"\n [class.col-md-6]=\"!metadataHasMany.tableData.baseData.tableActions.length\"\n [class.col-sm-6]=\"metadataHasMany.tableData.baseData.tableActions.length\"\n [class.col-sm-12]=\"!metadataHasMany.tableData.baseData.tableActions.length\"\n >\n <button type=\"button\" class=\"create-button\" (click)=\"createHasManyEntity()\" mat-raised-button>\n {{metadataHasMany.tableData.baseData.createButtonLabel}}\n </button>\n </div>\n </div>\n\n <div class=\"mat-elevation-z8 elevation-container\">\n <mat-table [dataSource]=\"hasManyDataSource\" matSort>\n <!-- select Column -->\n <ng-container matColumnDef=\"select\">\n <mat-header-cell *matHeaderCellDef>\n <mat-checkbox (change)=\"$event ? SelectionUtilities.masterToggle(hasManySelection, hasManyDataSource) : null\"\n [checked]=\"hasManySelection.hasValue() && SelectionUtilities.isAllSelected(hasManySelection, hasManyDataSource)\"\n [indeterminate]=\"hasManySelection.hasValue() && !SelectionUtilities.isAllSelected(hasManySelection, hasManyDataSource)\">\n </mat-checkbox>\n </mat-header-cell>\n <mat-cell *matCellDef=\"let entity\" class=\"enabled\">\n <mat-checkbox (click)=\"$event.stopPropagation()\"\n (change)=\"$event ? hasManySelection.toggle(entity) : null\"\n [checked]=\"hasManySelection.isSelected(entity)\">\n </mat-checkbox>\n </mat-cell>\n </ng-container>\n\n <ng-container *ngFor=\"let dCol of metadataHasMany.tableData.baseData.displayColumns\" [matColumnDef]=\"dCol.displayName\">\n <mat-header-cell *matHeaderCellDef mat-sort-header>\n {{dCol.displayName}}\n </mat-header-cell>\n <mat-cell [class.enabled]=\"!dCol.disableClick && (hasManyAllowUpdate(entity) || hasManyAllowRead(entity))\"\n (click)=\"editHasManyEntity(entity, dCol)\"\n *matCellDef=\"let entity\"\n >\n <ng-container *ngIf=\"dCol.Component\">\n <display-column-value [entity]=\"entity\" [ComponentClass]=\"dCol.Component\"></display-column-value>\n </ng-container>\n <ng-container *ngIf=\"!dCol.Component\">\n {{getDisplayColumnValue(entity, dCol)}}\n </ng-container>\n </mat-cell>\n </ng-container>\n\n <mat-header-row *matHeaderRowDef=\"displayedHasManyColumns\"></mat-header-row>\n <mat-row *matRowDef=\"let row; columns: displayedHasManyColumns\"></mat-row>\n </mat-table>\n\n <mat-spinner *ngIf=\"hasManyIsLoading && metadataHasMany.tableData.baseData.displayLoadingSpinner\">\n </mat-spinner>\n\n <mat-paginator [length]=\"hasManyDataSource.filteredData.length\" [pageIndex]=\"0\" [pageSize]=\"10\" [pageSizeOptions]=\"[5, 10, 25, 50]\"></mat-paginator>\n </div>\n </div>\n\n <div *ngSwitchDefault>ERROR: The type {{type}} is not known.</div>\n</div>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!--------------------------------------------------------->\n<!--------------------Add Array Item Dialog---------------->\n<!--------------------------------------------------------->\n<ng-template #addArrayItemDialog>\n <div class=\"mat-dialog-title\">\n <div>{{addArrayItemDialogData.title}}</div>\n </div>\n\n <form>\n <mat-dialog-content>\n <mat-tab-group *ngIf=\"arrayItemDialogTabs.length > 1\" preserveContent>\n <mat-tab *ngFor=\"let tab of arrayItemDialogTabs\" [label]=\"tab.tabName\">\n <div class=\"row\" *ngFor=\"let row of tab.rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"arrayItem\"\n [propertyKey]=\"key\"\n [hideOmitForCreate]=\"true\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n class=\"col-lg-{{EntityUtilities.getWidth(arrayItem, key, 'lg')}} col-md-{{EntityUtilities.getWidth(arrayItem, key, 'md')}} col-sm-{{EntityUtilities.getWidth(arrayItem, key, 'sm')}}\"\n (inputChangeEvent)=\"checkIsArrayItemValid()\"\n >\n </ngx-mat-entity-input>\n </div>\n </mat-tab>\n </mat-tab-group>\n \n <div *ngIf=\"arrayItemDialogTabs.length <= 1\">\n <div class=\"row\" *ngFor=\"let row of arrayItemDialogTabs[0].rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"arrayItem\"\n [propertyKey]=\"key\"\n [hideOmitForCreate]=\"true\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n class=\"col-lg-{{EntityUtilities.getWidth(arrayItem, key, 'lg')}} col-md-{{EntityUtilities.getWidth(arrayItem, key, 'md')}} col-sm-{{EntityUtilities.getWidth(arrayItem, key, 'sm')}}\"\n (inputChangeEvent)=\"checkIsArrayItemValid()\"\n >\n </ngx-mat-entity-input>\n </div>\n </div>\n </mat-dialog-content>\n \n <mat-dialog-actions>\n <button type=\"submit\" mat-raised-button (click)=\"addArrayItem()\" [disabled]=\"!isArrayItemValid\">\n {{addArrayItemDialogData.createButtonLabel}}\n </button>\n <button type=\"button\" mat-raised-button (click)=\"closeAddArrayItemDialog()\" class=\"cancel-button\">\n {{addArrayItemDialogData.cancelButtonLabel}}\n </button>\n </mat-dialog-actions>\n </form>\n\n</ng-template>\n\n<!--------------------------------------------------------->\n<!--------------------Edit Array Item Dialog---------------->\n<!--------------------------------------------------------->\n<ng-template #editArrayItemDialog>\n <div class=\"mat-dialog-title\">\n <div>{{editArrayItemDialogData.title(arrayItemPriorChanges)}}</div>\n </div>\n \n <form>\n <mat-dialog-content>\n <mat-tab-group *ngIf=\"arrayItemDialogTabs.length > 1\" preserveContent>\n <mat-tab *ngFor=\"let tab of arrayItemDialogTabs\" [label]=\"tab.tabName\">\n <div class=\"row\" *ngFor=\"let row of tab.rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"arrayItem\"\n [propertyKey]=\"key\"\n [hideOmitForEdit]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(arrayItem, key, 'lg')}} col-md-{{EntityUtilities.getWidth(arrayItem, key, 'md')}} col-sm-{{EntityUtilities.getWidth(arrayItem, key, 'sm')}}\"\n (inputChangeEvent)=\"checkArrayItem()\"\n [isReadOnly]=\"isPropertyReadOnly(arrayItem, key)\"\n >\n </ngx-mat-entity-input>\n </div>\n </mat-tab>\n </mat-tab-group>\n \n <div *ngIf=\"arrayItemDialogTabs.length <= 1\">\n <div class=\"row\" *ngFor=\"let row of arrayItemDialogTabs[0].rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"arrayItem\"\n [propertyKey]=\"key\"\n [hideOmitForEdit]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(arrayItem, key, 'lg')}} col-md-{{EntityUtilities.getWidth(arrayItem, key, 'md')}} col-sm-{{EntityUtilities.getWidth(arrayItem, key, 'sm')}}\"\n (inputChangeEvent)=\"checkArrayItem()\"\n [isReadOnly]=\"isPropertyReadOnly(arrayItem, key)\"\n >\n </ngx-mat-entity-input>\n </div>\n </div>\n </mat-dialog-content>\n \n <mat-dialog-actions>\n <button type=\"submit\" (click)=\"saveArrayItem()\" mat-raised-button [disabled]=\"internalIsReadOnly || !isArrayItemValid || !isArrayItemDirty\">\n {{editArrayItemDialogData.confirmButtonLabel}}\n </button>\n <button type=\"button\" mat-raised-button (click)=\"closeEditArrayItemDialog()\" class=\"cancel-button\">\n {{editArrayItemDialogData.cancelButtonLabel}}\n </button>\n </mat-dialog-actions>\n </form>\n</ng-template>\n\n<!--------------------------------------------------------->\n<!--------------------Create Has Many Dialog---------------->\n<!--------------------------------------------------------->\n<ng-template #createHasManyDialog>\n <div class=\"mat-dialog-title\">\n <div>{{metadataHasMany.tableData.createDialogData.title}}</div>\n </div>\n \n <form>\n <mat-dialog-content>\n <mat-tab-group *ngIf=\"hasManyCreateTabs.length > 1\" preserveContent>\n <mat-tab *ngFor=\"let tab of hasManyCreateTabs\" [label]=\"tab.tabName\">\n <div class=\"row\" *ngFor=\"let row of tab.rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"hasManyEntity\"\n [propertyKey]=\"key\"\n [hideOmitForCreate]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(hasManyEntity, key, 'lg')}} col-md-{{EntityUtilities.getWidth(hasManyEntity, key, 'md')}} col-sm-{{EntityUtilities.getWidth(hasManyEntity, key, 'sm')}}\"\n (inputChangeEvent)=\"checkIsHasManyEntityValid('create')\"\n >\n </ngx-mat-entity-input>\n </div>\n </mat-tab>\n </mat-tab-group>\n \n <div *ngIf=\"hasManyCreateTabs.length <= 1\">\n <div class=\"row\" *ngFor=\"let row of hasManyCreateTabs[0].rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"hasManyEntity\"\n [propertyKey]=\"key\"\n [hideOmitForCreate]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(hasManyEntity, key, 'lg')}} col-md-{{EntityUtilities.getWidth(hasManyEntity, key, 'md')}} col-sm-{{EntityUtilities.getWidth(hasManyEntity, key, 'sm')}}\"\n (inputChangeEvent)=\"checkIsHasManyEntityValid('create')\"\n >\n </ngx-mat-entity-input>\n </div>\n </div>\n </mat-dialog-content>\n \n <mat-dialog-actions>\n <button type=\"submit\" (click)=\"dialogCreateHasMany()\" mat-raised-button [disabled]=\"!isHasManyEntityValid\">\n {{metadataHasMany.tableData.createDialogData.createButtonLabel}}\n </button>\n <button type=\"button\" mat-raised-button (click)=\"dialogCancelCreateHasMany()\" class=\"cancel-button\">\n {{metadataHasMany.tableData.createDialogData.cancelButtonLabel}}\n </button>\n </mat-dialog-actions>\n </form>\n \n</ng-template>\n\n<ng-template #editHasManyDialog>\n <div class=\"mat-dialog-title\">\n <div>{{metadataHasMany.tableData.editData.title(hasManyEntityPriorChanges)}}</div>\n\n <div class=\"actions-container\">\n <button *ngIf=\"metadataHasMany.tableData.editData.actions.length\" type=\"button\" [matMenuTriggerFor]=\"menu\" mat-raised-button>\n {{metadataHasMany.tableData.editData.actionsLabel}}\n </button>\n <mat-menu #menu=\"matMenu\">\n <button type=\"button\" *ngFor=\"let action of metadataHasMany.tableData.editData.actions\" [disabled]=\"hasManyEditActionDisabled(action)\" (click)=\"hasManyRunEditAction(action)\" mat-menu-item>\n {{action.displayName}}\n </button>\n </mat-menu>\n \n <button type=\"button\" *ngIf=\"hasManyAllowDelete(hasManyEntity)\" mat-raised-button (click)=\"deleteHasManyEntity()\" color=\"warn\" class=\"delete-button\" tabindex=\"-1\">\n {{metadataHasMany.tableData.editData.deleteButtonLabel}}\n </button>\n </div>\n </div>\n \n <form>\n <mat-dialog-content>\n <mat-tab-group *ngIf=\"hasManyUpdateTabs.length > 1\" preserveContent>\n <mat-tab *ngFor=\"let tab of hasManyUpdateTabs\" [label]=\"tab.tabName\">\n <div class=\"row\" *ngFor=\"let row of tab.rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"hasManyEntity\"\n [propertyKey]=\"key\"\n [hideOmitForEdit]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(hasManyEntity, key, 'lg')}} col-md-{{EntityUtilities.getWidth(hasManyEntity, key, 'md')}} col-sm-{{EntityUtilities.getWidth(hasManyEntity, key, 'sm')}}\"\n (inputChangeEvent)=\"checkHasManyEntity()\"\n [isReadOnly]=\"isPropertyReadOnly(hasManyEntity, key)\"\n >\n </ngx-mat-entity-input>\n </div>\n </mat-tab>\n </mat-tab-group>\n \n <div *ngIf=\"hasManyUpdateTabs.length <= 1\">\n <div class=\"row\" *ngFor=\"let row of hasManyUpdateTabs[0].rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"hasManyEntity\"\n [propertyKey]=\"key\"\n [hideOmitForEdit]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(hasManyEntity, key, 'lg')}} col-md-{{EntityUtilities.getWidth(hasManyEntity, key, 'md')}} col-sm-{{EntityUtilities.getWidth(hasManyEntity, key, 'sm')}}\"\n (inputChangeEvent)=\"checkHasManyEntity()\"\n [isReadOnly]=\"isPropertyReadOnly(hasManyEntity, key)\"\n >\n </ngx-mat-entity-input>\n </div>\n </div>\n </mat-dialog-content>\n \n <mat-dialog-actions>\n <button type=\"submit\" (click)=\"dialogEditHasMany()\" mat-raised-button [disabled]=\"internalIsReadOnly || !isHasManyEntityValid || !isHasManyEntityDirty\">\n {{metadataHasMany.tableData.editData.confirmButtonLabel}}\n </button>\n <button type=\"button\" mat-raised-button (click)=\"dialogCancelEditHasMany()\" class=\"cancel-button\">\n {{metadataHasMany.tableData.editData.cancelButtonLabel}}\n </button>\n </mat-dialog-actions>\n </form> \n</ng-template>", styles: [".mdc-data-table__row:last-child .mdc-data-table__cell{border-bottom:1px solid rgba(0,0,0,.12)}.mat-dialog-title{padding:12px 20px;display:flex;justify-content:space-between;align-items:center}.mat-dialog-title div{font-size:var(--mdc-dialog-subhead-size, 14px);font-weight:var(--mdc-dialog-subhead-weight, 500)}::ng-deep .mdc-dialog .mdc-dialog__content{padding:6px 20px!important}mat-dialog-actions{justify-content:space-between;align-items:center;padding-left:20px;padding-right:20px}mat-spinner{margin:10px auto}::ng-deep .mat-mdc-tab-body-wrapper{margin-left:-12px;margin-right:-12px}::ng-deep mat-tab-body{padding-top:10px;padding-left:12px;padding-right:12px}::ng-deep mat-tab-body .mat-mdc-tab-body-content{overflow:initial}::ng-deep .mat-mdc-form-field.mat-form-field-disabled label{color:#0009}::ng-deep .mat-mdc-form-field.mat-form-field-disabled input,::ng-deep .mat-mdc-form-field.mat-form-field-disabled textarea,::ng-deep .mat-mdc-form-field.mat-form-field-disabled .mat-mdc-select-disabled,::ng-deep .mat-mdc-form-field.mat-form-field-disabled .mat-mdc-select-value{color:#000!important}::ng-deep .mat-mdc-form-field.mat-form-field-disabled .mdc-checkbox .mdc-checkbox__native-control[disabled]:not(:checked):not(:indeterminate):not([data-indeterminate=true])~.mdc-checkbox__background{border-color:#707070}::ng-deep .mat-mdc-form-field.mat-form-field-disabled .mdc-checkbox .mdc-checkbox__native-control[disabled]:checked~.mdc-checkbox__background,::ng-deep .mat-mdc-form-field.mat-form-field-disabled .mdc-checkbox .mdc-checkbox__native-control[disabled]:indeterminate~.mdc-checkbox__background,::ng-deep .mat-mdc-form-field.mat-form-field-disabled .mdc-checkbox .mdc-checkbox__native-control[data-indeterminate=true][disabled]~.mdc-checkbox__background{background-color:#000}::ng-deep .mat-mdc-form-field.mat-form-field-disabled .mat-mdc-chip.mat-mdc-standard-chip.mat-mdc-chip-disabled{opacity:1}::ng-deep .mat-mdc-form-field.mat-form-field-disabled .mat-mdc-chip.mat-mdc-standard-chip.mat-mdc-chip-disabled button{opacity:.2}::ng-deep .mat-mdc-form-field.mat-form-field-disabled .mdc-switch.mdc-switch--selected:disabled .mdc-switch__icons,::ng-deep .mat-mdc-form-field.mat-form-field-disabled .mdc-switch.mdc-switch--unselected:disabled .mdc-switch__icons{opacity:1}::ng-deep .mat-mdc-form-field.mat-form-field-disabled .mdc-switch.mdc-switch--selected:disabled .mdc-switch__handle:after,::ng-deep .mat-mdc-form-field.mat-form-field-disabled .mdc-switch.mdc-switch--unselected:disabled .mdc-switch__handle:after{opacity:1;background:black}::ng-deep .mat-mdc-form-field.mat-form-field-disabled .mdc-switch.mdc-switch--selected:disabled .mdc-switch__track,::ng-deep .mat-mdc-form-field.mat-form-field-disabled .mdc-switch.mdc-switch--unselected:disabled .mdc-switch__track{opacity:.3}::ng-deep .mat-mdc-form-field.mat-form-field-disabled .mat-date-range-input-inner:disabled{color:#000}::ng-deep #slider.mat-mdc-slider.mdc-slider--disabled{opacity:1}::ng-deep #slider.mat-mdc-slider.mdc-slider--disabled .mdc-slider__input{cursor:default}::ng-deep #slider.mat-mdc-slider.mdc-slider--disabled mat-slider-visual-thumb .mat-mdc-slider,::ng-deep #slider.mat-mdc-slider.mdc-slider--disabled mat-slider-visual-thumb .mdc-slider__thumb:hover,::ng-deep #slider.mat-mdc-slider.mdc-slider--disabled mat-slider-visual-thumb .mdc-slider__thumb-knob{background-color:#000;border-color:#000}::ng-deep .mat-mdc-form-field .mat-mdc-slide-toggle{opacity:1}.entityArray .elevation-container{border-radius:5px;padding:15px;margin-bottom:15px;margin-top:15px}.entityArray .array-headline{padding-bottom:10px}.entityArray .buttons{display:flex;justify-content:space-between;margin-bottom:10px;margin-top:5px}.entityArray mat-table{border:1px solid #E0E0E0;border-radius:5px;padding-top:5px;padding-bottom:25px}.entityArray .mat-column-select{flex:0 0 75px}.entityArray .enabled:hover{cursor:pointer}.entityArray .array-error{display:flex;align-items:center;justify-content:center;margin-top:-25.8px;border:1px solid #E0E0E0;background-color:#f8d3d7;color:#721c24;height:25.8px;border-bottom-left-radius:5px;border-bottom-right-radius:5px}.hasMany button{width:100%;height:56px;line-height:24px;font-size:16px}.hasMany .title{text-align:center}.hasMany .elevation-container{border-radius:5px;padding:5px;margin-bottom:15px}.hasMany .mat-column-select{flex:0 0 75px}.hasMany .enabled:hover{cursor:pointer}\n"], dependencies: [{ kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$1.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i1$1.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: i1$1.NgSwitchDefault, selector: "[ngSwitchDefault]" }, { kind: "directive", type: i2$1.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "component", type: i2$2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$2.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2$2.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i3.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i3.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: i4$2.MatSelect, selector: "mat-select", inputs: ["disabled", "disableRipple", "tabIndex", "panelWidth", "hideSingleSelectionIndicator"], exportAs: ["matSelect"] }, { kind: "component", type: i5$1.MatOption, selector: "mat-option", exportAs: ["matOption"] }, { kind: "component", type: i4.MatCheckbox, selector: "mat-checkbox", inputs: ["disableRipple", "color", "tabIndex"], exportAs: ["matCheckbox"] }, { kind: "component", type: i6.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i6.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i6.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i6.MatColumnDef, selector: "[matColumnDef]", inputs: ["sticky", "matColumnDef"] }, { kind: "directive", type: i6.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i6.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i6.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i6.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i6.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i6.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "directive", type: i1.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "directive", type: i1.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "component", type: i5.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", inputs: ["disabled", "disableRipple", "color"], exportAs: ["matButton"] }, { kind: "component", type: i6$2.MatTab, selector: "mat-tab", inputs: ["disabled"], exportAs: ["matTab"] }, { kind: "component", type: i6$2.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "disableRipple", "fitInkBarToContent", "mat-stretch-tabs"], exportAs: ["matTabGroup"] }, { kind: "component", type: i14.MatMenu, selector: "mat-menu", exportAs: ["matMenu"] }, { kind: "component", type: i14.MatMenuItem, selector: "[mat-menu-item]", inputs: ["disabled", "disableRipple", "role"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i14.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", exportAs: ["matMenuTrigger"] }, { kind: "component", type: i15.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "component", type: i16.MatPaginator, selector: "mat-paginator", inputs: ["disabled"], exportAs: ["matPaginator"] }, { kind: "component", type: DisplayColumnValueComponent, selector: "display-column-value", inputs: ["entity", "ComponentClass"] }, { kind: "directive", type: i18.MatSort, selector: "[matSort]", inputs: ["matSortDisabled", "matSortActive", "matSortStart", "matSortDirection", "matSortDisableClear"], outputs: ["matSortChange"], exportAs: ["matSort"] }, { kind: "component", type: i18.MatSortHeader, selector: "[mat-sort-header]", inputs: ["disabled", "mat-sort-header", "arrowPosition", "start", "sortActionDescription", "disableClear"], exportAs: ["matSortHeader"] }, { kind: "component", type: StringInputComponent, selector: "string-input" }, { kind: "component", type: StringTextboxInputComponent, selector: "string-textbox-input" }, { kind: "component", type: StringAutocompleteInputComponent, selector: "string-autocomplete-input" }, { kind: "component", type: StringDropdownInputComponent, selector: "string-dropdown-input" }, { kind: "component", type: StringPasswordInputComponent, selector: "string-password-input" }, { kind: "component", type: BooleanCheckboxInputComponent, selector: "boolean-checkbox-input" }, { kind: "component", type: BooleanToggleInputComponent, selector: "boolean-toggle-input" }, { kind: "component", type: BooleanDropdownInputComponent, selector: "boolean-dropdown-input" }, { kind: "component", type: NumberInputComponent, selector: "number-input" }, { kind: "component", type: NumberDropdownInputComponent, selector: "number-dropdown-input" }, { kind: "component", type: NumberSliderInputComponent, selector: "number-slider-input" }, { kind: "component", type: ArrayStringChipsInputComponent, selector: "array-string-chips-input" }, { kind: "component", type: ArrayStringAutocompleteChipsComponent, selector: "array-string-autocomplete-chips" }, { kind: "component", type: DateInputComponent, selector: "date-input" }, { kind: "component", type: DateRangeInputComponent, selector: "date-range-input" }, { kind: "component", type: DateTimeInputComponent, selector: "date-time-input" }, { kind: "component", type: ArrayDateInputComponent, selector: "array-date-input" }, { kind: "component", type: ArrayDateTimeInputComponent, selector: "array-date-time-input" }, { kind: "component", type: ArrayDateRangeInputComponent, selector: "array-date-range-input" }, { kind: "component", type: FileImageInputComponent, selector: "file-image-input" }, { kind: "component", type: FileDefaultInputComponent, selector: "file-default-input" }, { kind: "component", type: ReferencesManyInputComponent, selector: "references-many-input" }, { kind: "component", type: CustomInputComponent, selector: "custom-input" }, { kind: "component", type: NgxMatEntityInputComponent, selector: "ngx-mat-entity-input", inputs: ["entity", "propertyKey", "getValidationErrorMessage", "hideOmitForCreate", "hideOmitForEdit", "validEmpty", "isReadOnly"], outputs: ["inputChangeEvent"] }] });
4620
4949
  }
4621
4950
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.0", ngImport: i0, type: NgxMatEntityInputComponent, decorators: [{
4622
4951
  type: Component,
4623
- args: [{ selector: 'ngx-mat-entity-input', template: "<div [ngSwitch]=\"type\" *ngIf=\"!(hideOmitForCreate && metadata.omitForCreate) && !(hideOmitForEdit && metadata.omitForUpdate)\">\n <!-------------------------------------------->\n <!-----------------Strings-------------------->\n <!-------------------------------------------->\n <div *ngSwitchCase=\"DecoratorTypes.STRING\">\n <string-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </string-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.STRING_TEXTBOX\">\n <string-textbox-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </string-textbox-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.STRING_AUTOCOMPLETE\">\n <string-autocomplete-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </string-autocomplete-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.STRING_DROPDOWN\">\n <string-dropdown-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </string-dropdown-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.STRING_PASSWORD\">\n <string-password-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </string-password-input>\n </div>\n\n <!-------------------------------------------->\n <!-----------------Booleans------------------->\n <!-------------------------------------------->\n <div *ngSwitchCase=\"DecoratorTypes.BOOLEAN_CHECKBOX\">\n <boolean-checkbox-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </boolean-checkbox-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.BOOLEAN_TOGGLE\">\n <boolean-toggle-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </boolean-toggle-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.BOOLEAN_DROPDOWN\">\n <boolean-dropdown-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </boolean-dropdown-input>\n </div>\n\n <!-------------------------------------------->\n <!------------------Numbers------------------->\n <!-------------------------------------------->\n <div *ngSwitchCase=\"DecoratorTypes.NUMBER\">\n <number-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </number-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.NUMBER_DROPDOWN\">\n <number-dropdown-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </number-dropdown-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.NUMBER_SLIDER\">\n <number-slider-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </number-slider-input>\n </div>\n\n <!-------------------------------------------->\n <!-------------------Array-------------------->\n <!-------------------------------------------->\n <div *ngSwitchCase=\"DecoratorTypes.ARRAY_DATE\">\n <array-date-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </array-date-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.ARRAY_DATE_TIME\">\n <array-date-time-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </array-date-time-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.ARRAY_DATE_RANGE\">\n <array-date-range-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </array-date-range-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.ARRAY_STRING_CHIPS\">\n <array-string-chips-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </array-string-chips-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.ARRAY_STRING_AUTOCOMPLETE_CHIPS\">\n <array-string-autocomplete-chips\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </array-string-autocomplete-chips>\n </div>\n\n <!-------------------------------------------->\n <!-------------------Dates-------------------->\n <!-------------------------------------------->\n <div *ngSwitchCase=\"DecoratorTypes.DATE\">\n <date-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </date-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.DATE_RANGE\">\n <date-range-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </date-range-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.DATE_TIME\">\n <date-time-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </date-time-input>\n </div>\n\n <!-------------------------------------------->\n <!-------------------Files-------------------->\n <!-------------------------------------------->\n <div *ngSwitchCase=\"DecoratorTypes.FILE_DEFAULT\">\n <file-default-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </file-default-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.FILE_IMAGE\">\n <file-image-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </file-image-input>\n </div>\n\n <!-------------------------------------------->\n <!-------------- references many ------------->\n <!-------------------------------------------->\n <div *ngSwitchCase=\"DecoratorTypes.REFERENCES_MANY\">\n <references-many-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </references-many-input>\n </div>\n\n <!-------------------------------------------->\n <!-------------------Custom------------------->\n <!-------------------------------------------->\n <div *ngSwitchCase=\"DecoratorTypes.CUSTOM\">\n <custom-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </custom-input>\n </div>\n\n <!-------------------------------------------->\n <!-------------------Object------------------->\n <!-------------------------------------------->\n <div *ngSwitchCase=\"DecoratorTypes.OBJECT\">\n <b>{{metadataDefaultObject.displayName}}</b>\n <!-- iterates over the object properties -->\n <mat-tab-group *ngIf=\"objectPropertyTabs.length > 1\" preserveContent>\n <mat-tab *ngFor=\"let tab of objectPropertyTabs; let tI = index; trackBy: trackByFn\" [label]=\"tab.tabName\">\n <div class=\"row\" *ngFor=\"let row of tab.rows;\">\n <ngx-mat-entity-input *ngFor=\"let key of row.keys; let rI = index; trackBy: trackByFn\"\n [entity]=\"objectProperty\"\n [propertyKey]=\"key\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [hideOmitForCreate]=\"hideOmitForCreate\"\n [hideOmitForEdit]=\"hideOmitForEdit\"\n [validEmpty]=\"!metadata.required(entity)\"\n [isReadOnly]=\"isPropertyReadOnly(objectProperty, key)\"\n class=\"col-lg-{{EntityUtilities.getWidth(objectProperty, key, 'lg')}} col-md-{{EntityUtilities.getWidth(objectProperty, key, 'md')}} col-sm-{{EntityUtilities.getWidth(objectProperty, key, 'sm')}}\"\n (inputChangeEvent)=\"emitChange()\"\n >\n </ngx-mat-entity-input>\n </div>\n </mat-tab>\n </mat-tab-group>\n\n <div *ngIf=\"objectPropertyTabs.length <= 1\">\n <div class=\"row\" *ngFor=\"let row of objectPropertyTabs[0].rows\">\n <ngx-mat-entity-input *ngFor=\"let key of row.keys; let i = index; trackBy: trackByFn\"\n [entity]=\"objectProperty\"\n [propertyKey]=\"key\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [hideOmitForCreate]=\"hideOmitForCreate\"\n [hideOmitForEdit]=\"hideOmitForEdit\"\n [validEmpty]=\"!metadata.required(entity)\"\n [isReadOnly]=\"isPropertyReadOnly(objectProperty, key)\"\n class=\"col-lg-{{EntityUtilities.getWidth(objectProperty, key, 'lg')}} col-md-{{EntityUtilities.getWidth(objectProperty, key, 'md')}} col-sm-{{EntityUtilities.getWidth(objectProperty, key, 'sm')}}\"\n (inputChangeEvent)=\"emitChange()\"\n >\n </ngx-mat-entity-input>\n </div>\n </div>\n </div>\n\n <!-------------------------------------------->\n <!-------------- references one ------------->\n <!-------------------------------------------->\n <div *ngSwitchCase=\"DecoratorTypes.REFERENCES_ONE\">\n <mat-form-field>\n <mat-label>{{metadata.displayName}}</mat-label>\n <mat-select [(ngModel)]=\"internalEntity[internalPropertyKey]\"\n [name]=\"internalPropertyKey.toString() + 'input' + referencesOneUUID\"\n #inputModel=\"ngModel\"\n [disabled]=\"internalIsReadOnly\"\n [required]=\"metadata.required(entity)\"\n (ngModelChange)=\"setReferencesOneObject()\"\n >\n <mat-option *ngIf=\"!metadata.required(entity)\">-</mat-option>\n <mat-option *ngFor=\"let value of referencesOneDropdownValues\" [value]=\"value.value\">{{value.displayName}}</mat-option>\n </mat-select>\n <mat-error>{{internalGetValidationErrorMessage(inputModel)}}</mat-error>\n </mat-form-field>\n <!-- iterates over the references one properties -->\n <mat-tab-group *ngIf=\"referencesOnePropertyTabs && referencesOnePropertyTabs.length > 1\" preserveContent>\n <mat-tab *ngFor=\"let tab of referencesOnePropertyTabs; let tI = index; trackBy: trackByFn\" [label]=\"tab.tabName\">\n <div class=\"row\" *ngFor=\"let row of tab.rows;\">\n <ngx-mat-entity-input *ngFor=\"let key of row.keys; let rI = index; trackBy: trackByFn\"\n [entity]=\"referencesOneObject\"\n [propertyKey]=\"key\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [hideOmitForCreate]=\"hideOmitForCreate\"\n [hideOmitForEdit]=\"hideOmitForEdit\"\n [validEmpty]=\"!metadata.required(entity)\"\n [isReadOnly]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(referencesOneObject, key, 'lg')}} col-md-{{EntityUtilities.getWidth(referencesOneObject, key, 'md')}} col-sm-{{EntityUtilities.getWidth(referencesOneObject, key, 'sm')}}\"\n (inputChangeEvent)=\"emitChange()\"\n >\n </ngx-mat-entity-input>\n </div>\n </mat-tab>\n </mat-tab-group>\n\n <div *ngIf=\"referencesOnePropertyTabs && referencesOnePropertyTabs.length <= 1\">\n <div class=\"row\" *ngFor=\"let row of referencesOnePropertyTabs[0].rows\">\n <ngx-mat-entity-input *ngFor=\"let key of row.keys; let i = index; trackBy: trackByFn\"\n [entity]=\"referencesOneObject\"\n [propertyKey]=\"key\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [hideOmitForCreate]=\"hideOmitForCreate\"\n [hideOmitForEdit]=\"hideOmitForEdit\"\n [validEmpty]=\"!metadata.required(entity)\"\n [isReadOnly]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(referencesOneObject, key, 'lg')}} col-md-{{EntityUtilities.getWidth(referencesOneObject, key, 'md')}} col-sm-{{EntityUtilities.getWidth(referencesOneObject, key, 'sm')}}\"\n (inputChangeEvent)=\"emitChange()\"\n >\n </ngx-mat-entity-input>\n </div>\n </div>\n </div>\n\n <!-------------------------------------------->\n <!-------------------Array-------------------->\n <!-------------------------------------------->\n <div class=\"entityArray\" *ngSwitchCase=\"DecoratorTypes.ARRAY\">\n <div class=\"mat-elevation-z8 elevation-container\">\n <div class=\"array-headline\">\n <b>{{metadataEntityArray.displayName}}</b>\n </div>\n <div *ngIf=\"metadataEntityArray.createInline && !internalIsReadOnly\">\n <mat-tab-group *ngIf=\"arrayItemInlineTabs.length > 1\" preserveContent>\n <mat-tab *ngFor=\"let tab of arrayItemInlineTabs\" [label]=\"tab.tabName\">\n <div class=\"row\" *ngFor=\"let row of tab.rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys; let i = index; trackBy: trackByFn\"\n [entity]=\"arrayItem\"\n [propertyKey]=\"key\"\n [hideOmitForCreate]=\"true\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n class=\"col-lg-{{EntityUtilities.getWidth(arrayItem, key, 'lg')}} col-md-{{EntityUtilities.getWidth(arrayItem, key, 'md')}} col-sm-{{EntityUtilities.getWidth(arrayItem, key, 'sm')}}\"\n (inputChangeEvent)=\"checkIsArrayItemValid()\"\n >\n </ngx-mat-entity-input>\n </div>\n </mat-tab>\n </mat-tab-group>\n\n <div *ngIf=\"arrayItemInlineTabs.length <= 1\">\n <div class=\"row\" *ngFor=\"let row of arrayItemInlineTabs[0].rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys; let i = index; trackBy: trackByFn\"\n [entity]=\"arrayItem\"\n [propertyKey]=\"key\"\n [hideOmitForCreate]=\"true\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n class=\"col-lg-{{EntityUtilities.getWidth(arrayItem, key, 'lg')}} col-md-{{EntityUtilities.getWidth(arrayItem, key, 'md')}} col-sm-{{EntityUtilities.getWidth(arrayItem, key, 'sm')}}\"\n (inputChangeEvent)=\"checkIsArrayItemValid()\"\n >\n </ngx-mat-entity-input>\n </div>\n </div>\n </div>\n\n <div class=\"buttons\" *ngIf=\"!internalIsReadOnly\">\n <button type=\"button\" mat-raised-button\n [disabled]=\"metadataEntityArray.createInline && !isArrayItemValid\"\n (click)=\"addEntity()\">\n {{metadataEntityArray.addButtonLabel}}\n </button>\n <button type=\"button\" mat-raised-button\n [disabled]=\"!entityArraySelection.selected.length\"\n (click)=\"removeFromEntityArray()\">\n {{metadataEntityArray.removeButtonLabel}}\n </button>\n </div>\n \n <mat-table [dataSource]=\"entityArrayDataSource\">\n <!-- select Column -->\n <ng-container matColumnDef=\"select\" *ngIf=\"!internalIsReadOnly\">\n <mat-header-cell *matHeaderCellDef>\n <mat-checkbox\n [disabled]=\"!entityArrayDataSource.data.length\" (change)=\"$event ? SelectionUtilities.masterToggle(entityArraySelection, entityArrayDataSource) : null\"\n [checked]=\"entityArraySelection.hasValue() && SelectionUtilities.isAllSelected(entityArraySelection, entityArrayDataSource)\"\n [indeterminate]=\"entityArraySelection.hasValue() && !SelectionUtilities.isAllSelected(entityArraySelection, entityArrayDataSource)\">\n </mat-checkbox>\n </mat-header-cell>\n <mat-cell *matCellDef=\"let entity\">\n <mat-checkbox (click)=\"$event.stopPropagation()\" (change)=\"$event ? entityArraySelection.toggle(entity) : null\" [checked]=\"entityArraySelection.isSelected(entity)\"></mat-checkbox>\n </mat-cell>\n </ng-container>\n \n <ng-container *ngFor=\"let dCol of metadataEntityArray.displayColumns\" [matColumnDef]=\"dCol.displayName\">\n <mat-header-cell *matHeaderCellDef>\n {{dCol.displayName}}\n </mat-header-cell>\n <mat-cell [class.enabled]=\"!dCol.disableClick\" (click)=\"editArrayItem(entity, dCol)\" *matCellDef=\"let entity\">\n <ng-container *ngIf=\"dCol.Component\">\n <display-column-value [entity]=\"entity\" [ComponentClass]=\"dCol.Component\"></display-column-value>\n </ng-container>\n <ng-container *ngIf=\"!dCol.Component\">\n {{getDisplayColumnValue(entity, dCol)}}\n </ng-container>\n </mat-cell>\n </ng-container>\n \n <mat-header-row *matHeaderRowDef=\"entityArrayDisplayedColumns\"></mat-header-row>\n <mat-row *matRowDef=\"let row; columns: entityArrayDisplayedColumns\"></mat-row>\n </mat-table>\n \n <div class=\"array-error\" *ngIf=\"metadataEntityArray.required(entity) && !entityArrayDataSource.data.length\">\n {{metadataEntityArray.missingErrorMessage}}\n </div>\n </div>\n </div>\n\n <!-------------------------------------------->\n <!------------------ has many ---------------->\n <!-------------------------------------------->\n <div class=\"hasMany\" *ngSwitchCase=\"DecoratorTypes.HAS_MANY\">\n <h2 class=\"title\">{{metadataHasMany.tableData.baseData.title}}</h2>\n\n <div class=\"row\">\n <mat-form-field class=\"col-lg-8 col-md-6 col-sm-12\">\n <mat-label>{{metadataHasMany.tableData.baseData.searchLabel}}</mat-label>\n <input matInput (keyup)=\"applyHasManyFilter($event)\">\n </mat-form-field>\n <div\n *ngIf=\"metadataHasMany.tableData.baseData.tableActions.length\"\n [class.col-lg-2]=\"hasManyAllowCreate\"\n [class.col-lg-4]=\"!hasManyAllowCreate\"\n [class.col-md-3]=\"hasManyAllowCreate\"\n [class.col-md-6]=\"!hasManyAllowCreate\"\n [class.col-sm-6]=\"hasManyAllowCreate\"\n [class.col-sm-12]=\"!hasManyAllowCreate\"\n >\n <button type=\"button\" class=\"actions-button\" [matMenuTriggerFor]=\"menu\" mat-raised-button>\n {{metadataHasMany.tableData.baseData.tableActionsLabel}}\n </button>\n </div>\n <mat-menu #menu=\"matMenu\">\n <button *ngIf=\"metadataHasMany.tableData.baseData.allowJsonImport\" type=\"button\" [disabled]=\"hasManyTableActionDisabled(hasManyImportAction)\" (click)=\"runHasManyTableAction(hasManyImportAction)\" mat-menu-item>\n {{hasManyImportAction.displayName}}\n </button>\n <button type=\"button\" *ngFor=\"let action of metadataHasMany.tableData.baseData.tableActions\" [disabled]=\"hasManyTableActionDisabled(action)\" (click)=\"runHasManyTableAction(action)\" mat-menu-item>\n {{action.displayName}}\n </button>\n </mat-menu>\n\n <div\n *ngIf=\"hasManyAllowCreate\"\n [class.col-lg-2]=\"metadataHasMany.tableData.baseData.tableActions.length\"\n [class.col-lg-4]=\"!metadataHasMany.tableData.baseData.tableActions.length\"\n [class.col-md-3]=\"metadataHasMany.tableData.baseData.tableActions.length\"\n [class.col-md-6]=\"!metadataHasMany.tableData.baseData.tableActions.length\"\n [class.col-sm-6]=\"metadataHasMany.tableData.baseData.tableActions.length\"\n [class.col-sm-12]=\"!metadataHasMany.tableData.baseData.tableActions.length\"\n >\n <button type=\"button\" class=\"create-button\" (click)=\"createHasManyEntity()\" mat-raised-button>\n {{metadataHasMany.tableData.baseData.createButtonLabel}}\n </button>\n </div>\n </div>\n\n <div class=\"mat-elevation-z8 elevation-container\">\n <mat-table [dataSource]=\"hasManyDataSource\" matSort>\n <!-- select Column -->\n <ng-container matColumnDef=\"select\">\n <mat-header-cell *matHeaderCellDef>\n <mat-checkbox (change)=\"$event ? SelectionUtilities.masterToggle(hasManySelection, hasManyDataSource) : null\" [checked]=\"hasManySelection.hasValue() && SelectionUtilities.isAllSelected(hasManySelection, hasManyDataSource)\" [indeterminate]=\"hasManySelection.hasValue() && !SelectionUtilities.isAllSelected(hasManySelection, hasManyDataSource)\"> </mat-checkbox>\n </mat-header-cell>\n <mat-cell *matCellDef=\"let entity\" class=\"enabled\">\n <mat-checkbox (click)=\"$event.stopPropagation()\" (change)=\"$event ? hasManySelection.toggle(entity) : null\" [checked]=\"hasManySelection.isSelected(entity)\"> </mat-checkbox>\n </mat-cell>\n </ng-container>\n\n <ng-container *ngFor=\"let dCol of metadataHasMany.tableData.baseData.displayColumns\" [matColumnDef]=\"dCol.displayName\">\n <mat-header-cell *matHeaderCellDef mat-sort-header>\n {{dCol.displayName}}\n </mat-header-cell>\n <mat-cell [class.enabled]=\"!dCol.disableClick && (hasManyAllowUpdate(entity) || hasManyAllowRead(entity))\" (click)=\"editHasManyEntity(entity, dCol)\" *matCellDef=\"let entity\">\n <ng-container *ngIf=\"dCol.Component\">\n <display-column-value [entity]=\"entity\" [ComponentClass]=\"dCol.Component\"></display-column-value>\n </ng-container>\n <ng-container *ngIf=\"!dCol.Component\">\n {{getDisplayColumnValue(entity, dCol)}}\n </ng-container>\n </mat-cell>\n </ng-container>\n\n <mat-header-row *matHeaderRowDef=\"displayedHasManyColumns\"></mat-header-row>\n <mat-row *matRowDef=\"let row; columns: displayedHasManyColumns\"></mat-row>\n </mat-table>\n\n <mat-spinner *ngIf=\"hasManyIsLoading && metadataHasMany.tableData.baseData.displayLoadingSpinner\">\n </mat-spinner>\n\n <mat-paginator [length]=\"hasManyDataSource.filteredData.length\" [pageIndex]=\"0\" [pageSize]=\"10\" [pageSizeOptions]=\"[5, 10, 25, 50]\"></mat-paginator>\n </div>\n </div>\n\n <div *ngSwitchDefault>ERROR: The type {{type}} is not known.</div>\n</div>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!--------------------------------------------------------->\n<!--------------------Add Array Item Dialog---------------->\n<!--------------------------------------------------------->\n<ng-template #addArrayItemDialog>\n <div class=\"mat-dialog-title\">\n <div>{{addArrayItemDialogData.title}}</div>\n </div>\n\n <form>\n <mat-dialog-content>\n <mat-tab-group *ngIf=\"arrayItemDialogTabs.length > 1\" preserveContent>\n <mat-tab *ngFor=\"let tab of arrayItemDialogTabs\" [label]=\"tab.tabName\">\n <div class=\"row\" *ngFor=\"let row of tab.rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"arrayItem\"\n [propertyKey]=\"key\"\n [hideOmitForCreate]=\"true\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n class=\"col-lg-{{EntityUtilities.getWidth(arrayItem, key, 'lg')}} col-md-{{EntityUtilities.getWidth(arrayItem, key, 'md')}} col-sm-{{EntityUtilities.getWidth(arrayItem, key, 'sm')}}\"\n (inputChangeEvent)=\"checkIsArrayItemValid()\"\n >\n </ngx-mat-entity-input>\n </div>\n </mat-tab>\n </mat-tab-group>\n \n <div *ngIf=\"arrayItemDialogTabs.length <= 1\">\n <div class=\"row\" *ngFor=\"let row of arrayItemDialogTabs[0].rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"arrayItem\"\n [propertyKey]=\"key\"\n [hideOmitForCreate]=\"true\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n class=\"col-lg-{{EntityUtilities.getWidth(arrayItem, key, 'lg')}} col-md-{{EntityUtilities.getWidth(arrayItem, key, 'md')}} col-sm-{{EntityUtilities.getWidth(arrayItem, key, 'sm')}}\"\n (inputChangeEvent)=\"checkIsArrayItemValid()\"\n >\n </ngx-mat-entity-input>\n </div>\n </div>\n </mat-dialog-content>\n \n <mat-dialog-actions>\n <button type=\"submit\" mat-raised-button (click)=\"addArrayItem()\" [disabled]=\"!isArrayItemValid\">\n {{addArrayItemDialogData.createButtonLabel}}\n </button>\n <button type=\"button\" mat-raised-button (click)=\"closeAddArrayItemDialog()\" class=\"cancel-button\">\n {{addArrayItemDialogData.cancelButtonLabel}}\n </button>\n </mat-dialog-actions>\n </form>\n\n</ng-template>\n\n<!--------------------------------------------------------->\n<!--------------------Edit Array Item Dialog---------------->\n<!--------------------------------------------------------->\n<ng-template #editArrayItemDialog>\n <div class=\"mat-dialog-title\">\n <div>{{editArrayItemDialogData.title(arrayItemPriorChanges)}}</div>\n </div>\n \n <form>\n <mat-dialog-content>\n <mat-tab-group *ngIf=\"arrayItemDialogTabs.length > 1\" preserveContent>\n <mat-tab *ngFor=\"let tab of arrayItemDialogTabs\" [label]=\"tab.tabName\">\n <div class=\"row\" *ngFor=\"let row of tab.rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"arrayItem\"\n [propertyKey]=\"key\"\n [hideOmitForEdit]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(arrayItem, key, 'lg')}} col-md-{{EntityUtilities.getWidth(arrayItem, key, 'md')}} col-sm-{{EntityUtilities.getWidth(arrayItem, key, 'sm')}}\"\n (inputChangeEvent)=\"checkArrayItem()\"\n [isReadOnly]=\"isPropertyReadOnly(arrayItem, key)\"\n >\n </ngx-mat-entity-input>\n </div>\n </mat-tab>\n </mat-tab-group>\n \n <div *ngIf=\"arrayItemDialogTabs.length <= 1\">\n <div class=\"row\" *ngFor=\"let row of arrayItemDialogTabs[0].rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"arrayItem\"\n [propertyKey]=\"key\"\n [hideOmitForEdit]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(arrayItem, key, 'lg')}} col-md-{{EntityUtilities.getWidth(arrayItem, key, 'md')}} col-sm-{{EntityUtilities.getWidth(arrayItem, key, 'sm')}}\"\n (inputChangeEvent)=\"checkArrayItem()\"\n [isReadOnly]=\"isPropertyReadOnly(arrayItem, key)\"\n >\n </ngx-mat-entity-input>\n </div>\n </div>\n </mat-dialog-content>\n \n <mat-dialog-actions>\n <button type=\"submit\" (click)=\"saveArrayItem()\" mat-raised-button [disabled]=\"internalIsReadOnly || !isArrayItemValid || !isArrayItemDirty\">\n {{editArrayItemDialogData.confirmButtonLabel}}\n </button>\n <button type=\"button\" mat-raised-button (click)=\"closeEditArrayItemDialog()\" class=\"cancel-button\">\n {{editArrayItemDialogData.cancelButtonLabel}}\n </button>\n </mat-dialog-actions>\n </form>\n</ng-template>\n\n<!--------------------------------------------------------->\n<!--------------------Create Has Many Dialog---------------->\n<!--------------------------------------------------------->\n<ng-template #createHasManyDialog>\n <div class=\"mat-dialog-title\">\n <div>{{metadataHasMany.tableData.createDialogData.title}}</div>\n </div>\n \n <form>\n <mat-dialog-content>\n <mat-tab-group *ngIf=\"hasManyCreateTabs.length > 1\" preserveContent>\n <mat-tab *ngFor=\"let tab of hasManyCreateTabs\" [label]=\"tab.tabName\">\n <div class=\"row\" *ngFor=\"let row of tab.rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"hasManyEntity\"\n [propertyKey]=\"key\"\n [hideOmitForCreate]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(hasManyEntity, key, 'lg')}} col-md-{{EntityUtilities.getWidth(hasManyEntity, key, 'md')}} col-sm-{{EntityUtilities.getWidth(hasManyEntity, key, 'sm')}}\"\n (inputChangeEvent)=\"checkIsHasManyEntityValid('create')\"\n >\n </ngx-mat-entity-input>\n </div>\n </mat-tab>\n </mat-tab-group>\n \n <div *ngIf=\"hasManyCreateTabs.length <= 1\">\n <div class=\"row\" *ngFor=\"let row of hasManyCreateTabs[0].rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"hasManyEntity\"\n [propertyKey]=\"key\"\n [hideOmitForCreate]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(hasManyEntity, key, 'lg')}} col-md-{{EntityUtilities.getWidth(hasManyEntity, key, 'md')}} col-sm-{{EntityUtilities.getWidth(hasManyEntity, key, 'sm')}}\"\n (inputChangeEvent)=\"checkIsHasManyEntityValid('create')\"\n >\n </ngx-mat-entity-input>\n </div>\n </div>\n </mat-dialog-content>\n \n <mat-dialog-actions>\n <button type=\"submit\" (click)=\"dialogCreateHasMany()\" mat-raised-button [disabled]=\"!isHasManyEntityValid\">\n {{metadataHasMany.tableData.createDialogData.createButtonLabel}}\n </button>\n <button type=\"button\" mat-raised-button (click)=\"dialogCancelCreateHasMany()\" class=\"cancel-button\">\n {{metadataHasMany.tableData.createDialogData.cancelButtonLabel}}\n </button>\n </mat-dialog-actions>\n </form>\n \n</ng-template>\n\n<ng-template #editHasManyDialog>\n <div class=\"mat-dialog-title\">\n <div>{{metadataHasMany.tableData.editData.title(hasManyEntityPriorChanges)}}</div>\n <button type=\"button\" *ngIf=\"hasManyAllowDelete(hasManyEntity)\" mat-raised-button (click)=\"deleteHasManyEntity()\" color=\"warn\" class=\"delete-button\" tabindex=\"-1\">\n {{metadataHasMany.tableData.editData.deleteButtonLabel}}\n </button>\n </div>\n \n <form>\n <mat-dialog-content>\n <mat-tab-group *ngIf=\"hasManyUpdateTabs.length > 1\" preserveContent>\n <mat-tab *ngFor=\"let tab of hasManyUpdateTabs\" [label]=\"tab.tabName\">\n <div class=\"row\" *ngFor=\"let row of tab.rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"hasManyEntity\"\n [propertyKey]=\"key\"\n [hideOmitForEdit]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(hasManyEntity, key, 'lg')}} col-md-{{EntityUtilities.getWidth(hasManyEntity, key, 'md')}} col-sm-{{EntityUtilities.getWidth(hasManyEntity, key, 'sm')}}\"\n (inputChangeEvent)=\"checkHasManyEntity()\"\n [isReadOnly]=\"isPropertyReadOnly(hasManyEntity, key)\"\n >\n </ngx-mat-entity-input>\n </div>\n </mat-tab>\n </mat-tab-group>\n \n <div *ngIf=\"hasManyUpdateTabs.length <= 1\">\n <div class=\"row\" *ngFor=\"let row of hasManyUpdateTabs[0].rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"hasManyEntity\"\n [propertyKey]=\"key\"\n [hideOmitForEdit]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(hasManyEntity, key, 'lg')}} col-md-{{EntityUtilities.getWidth(hasManyEntity, key, 'md')}} col-sm-{{EntityUtilities.getWidth(hasManyEntity, key, 'sm')}}\"\n (inputChangeEvent)=\"checkHasManyEntity()\"\n [isReadOnly]=\"isPropertyReadOnly(hasManyEntity, key)\"\n >\n </ngx-mat-entity-input>\n </div>\n </div>\n </mat-dialog-content>\n \n <mat-dialog-actions>\n <button type=\"submit\" (click)=\"dialogEditHasMany()\" mat-raised-button [disabled]=\"internalIsReadOnly || !isHasManyEntityValid || !isHasManyEntityDirty\">\n {{metadataHasMany.tableData.editData.confirmButtonLabel}}\n </button>\n <button type=\"button\" mat-raised-button (click)=\"dialogCancelEditHasMany()\" class=\"cancel-button\">\n {{metadataHasMany.tableData.editData.cancelButtonLabel}}\n </button>\n </mat-dialog-actions>\n </form> \n</ng-template>", styles: [".mdc-data-table__row:last-child .mdc-data-table__cell{border-bottom:1px solid rgba(0,0,0,.12)}.mat-dialog-title{padding:12px 20px;display:flex;justify-content:space-between;align-items:center}.mat-dialog-title div{font-size:var(--mdc-dialog-subhead-size, 14px);font-weight:var(--mdc-dialog-subhead-weight, 500)}::ng-deep .mdc-dialog .mdc-dialog__content{padding:6px 20px!important}mat-dialog-actions{justify-content:space-between;align-items:center;padding-left:20px;padding-right:20px}mat-spinner{margin:10px auto}::ng-deep .mat-mdc-tab-body-wrapper{margin-left:-12px;margin-right:-12px}::ng-deep mat-tab-body{padding-top:10px;padding-left:12px;padding-right:12px}::ng-deep mat-tab-body .mat-mdc-tab-body-content{overflow:initial}::ng-deep .mat-mdc-form-field.mat-form-field-disabled label{color:#0009}::ng-deep .mat-mdc-form-field.mat-form-field-disabled input,::ng-deep .mat-mdc-form-field.mat-form-field-disabled textarea,::ng-deep .mat-mdc-form-field.mat-form-field-disabled .mat-mdc-select-disabled,::ng-deep .mat-mdc-form-field.mat-form-field-disabled .mat-mdc-select-value{color:#000}::ng-deep .mat-mdc-form-field.mat-form-field-disabled .mat-mdc-chip.mat-mdc-standard-chip.mat-mdc-chip-disabled{opacity:1}::ng-deep .mat-mdc-form-field.mat-form-field-disabled .mat-mdc-chip.mat-mdc-standard-chip.mat-mdc-chip-disabled button{opacity:.2}::ng-deep .mat-mdc-form-field.mat-form-field-disabled .mat-date-range-input-inner:disabled{color:#000}::ng-deep .mat-mdc-form-field .mat-mdc-slide-toggle{opacity:1}.entityArray .elevation-container{border-radius:5px;padding:15px;margin-bottom:15px;margin-top:15px}.entityArray .array-headline{padding-bottom:10px}.entityArray .buttons{display:flex;justify-content:space-between;margin-bottom:10px;margin-top:5px}.entityArray mat-table{border:1px solid #E0E0E0;border-radius:5px;padding-top:5px;padding-bottom:25px}.entityArray .mat-column-select{flex:0 0 75px}.entityArray .enabled:hover{cursor:pointer}.entityArray .array-error{display:flex;align-items:center;justify-content:center;margin-top:-25.8px;border:1px solid #E0E0E0;background-color:#f8d3d7;color:#721c24;height:25.8px;border-bottom-left-radius:5px;border-bottom-right-radius:5px}.hasMany button{width:100%;height:56px;line-height:24px;font-size:16px}.hasMany .title{text-align:center}.hasMany .elevation-container{border-radius:5px;padding:5px;margin-bottom:15px}.hasMany .mat-column-select{flex:0 0 75px}.hasMany .enabled:hover{cursor:pointer}\n"] }]
4952
+ args: [{ selector: 'ngx-mat-entity-input', template: "<div [ngSwitch]=\"type\" *ngIf=\"!(hideOmitForCreate && metadata.omitForCreate) && !(hideOmitForEdit && metadata.omitForUpdate)\">\n <!-------------------------------------------->\n <!-----------------Strings-------------------->\n <!-------------------------------------------->\n <div *ngSwitchCase=\"DecoratorTypes.STRING\">\n <string-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </string-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.STRING_TEXTBOX\">\n <string-textbox-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </string-textbox-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.STRING_AUTOCOMPLETE\">\n <string-autocomplete-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </string-autocomplete-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.STRING_DROPDOWN\">\n <string-dropdown-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </string-dropdown-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.STRING_PASSWORD\">\n <string-password-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </string-password-input>\n </div>\n\n <!-------------------------------------------->\n <!-----------------Booleans------------------->\n <!-------------------------------------------->\n <div *ngSwitchCase=\"DecoratorTypes.BOOLEAN_CHECKBOX\">\n <boolean-checkbox-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </boolean-checkbox-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.BOOLEAN_TOGGLE\">\n <boolean-toggle-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </boolean-toggle-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.BOOLEAN_DROPDOWN\">\n <boolean-dropdown-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </boolean-dropdown-input>\n </div>\n\n <!-------------------------------------------->\n <!------------------Numbers------------------->\n <!-------------------------------------------->\n <div *ngSwitchCase=\"DecoratorTypes.NUMBER\">\n <number-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </number-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.NUMBER_DROPDOWN\">\n <number-dropdown-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </number-dropdown-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.NUMBER_SLIDER\">\n <number-slider-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </number-slider-input>\n </div>\n\n <!-------------------------------------------->\n <!-------------------Array-------------------->\n <!-------------------------------------------->\n <div *ngSwitchCase=\"DecoratorTypes.ARRAY_DATE\">\n <array-date-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </array-date-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.ARRAY_DATE_TIME\">\n <array-date-time-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </array-date-time-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.ARRAY_DATE_RANGE\">\n <array-date-range-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </array-date-range-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.ARRAY_STRING_CHIPS\">\n <array-string-chips-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </array-string-chips-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.ARRAY_STRING_AUTOCOMPLETE_CHIPS\">\n <array-string-autocomplete-chips\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </array-string-autocomplete-chips>\n </div>\n\n <!-------------------------------------------->\n <!-------------------Dates-------------------->\n <!-------------------------------------------->\n <div *ngSwitchCase=\"DecoratorTypes.DATE\">\n <date-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </date-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.DATE_RANGE\">\n <date-range-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </date-range-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.DATE_TIME\">\n <date-time-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </date-time-input>\n </div>\n\n <!-------------------------------------------->\n <!-------------------Files-------------------->\n <!-------------------------------------------->\n <div *ngSwitchCase=\"DecoratorTypes.FILE_DEFAULT\">\n <file-default-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </file-default-input>\n </div>\n <div *ngSwitchCase=\"DecoratorTypes.FILE_IMAGE\">\n <file-image-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </file-image-input>\n </div>\n\n <!-------------------------------------------->\n <!-------------- references many ------------->\n <!-------------------------------------------->\n <div *ngSwitchCase=\"DecoratorTypes.REFERENCES_MANY\">\n <references-many-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </references-many-input>\n </div>\n\n <!-------------------------------------------->\n <!-------------------Custom------------------->\n <!-------------------------------------------->\n <div *ngSwitchCase=\"DecoratorTypes.CUSTOM\">\n <custom-input\n (inputChangeEvent)=\"emitChange()\"\n [entity]=\"internalEntity\"\n [key]=\"internalPropertyKey\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [isReadOnly]=\"internalIsReadOnly\"\n >\n </custom-input>\n </div>\n\n <!-------------------------------------------->\n <!-------------------Object------------------->\n <!-------------------------------------------->\n <div *ngSwitchCase=\"DecoratorTypes.OBJECT\">\n <b>{{metadataDefaultObject.displayName}}</b>\n <!-- iterates over the object properties -->\n <mat-tab-group *ngIf=\"objectPropertyTabs.length > 1\" preserveContent>\n <mat-tab *ngFor=\"let tab of objectPropertyTabs; let tI = index; trackBy: trackByFn\" [label]=\"tab.tabName\">\n <div class=\"row\" *ngFor=\"let row of tab.rows;\">\n <ngx-mat-entity-input *ngFor=\"let key of row.keys; let rI = index; trackBy: trackByFn\"\n [entity]=\"objectProperty\"\n [propertyKey]=\"key\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [hideOmitForCreate]=\"hideOmitForCreate\"\n [hideOmitForEdit]=\"hideOmitForEdit\"\n [validEmpty]=\"!metadata.required(entity)\"\n [isReadOnly]=\"isPropertyReadOnly(objectProperty, key)\"\n class=\"col-lg-{{EntityUtilities.getWidth(objectProperty, key, 'lg')}} col-md-{{EntityUtilities.getWidth(objectProperty, key, 'md')}} col-sm-{{EntityUtilities.getWidth(objectProperty, key, 'sm')}}\"\n (inputChangeEvent)=\"emitChange()\"\n >\n </ngx-mat-entity-input>\n </div>\n </mat-tab>\n </mat-tab-group>\n\n <div *ngIf=\"objectPropertyTabs.length <= 1\">\n <div class=\"row\" *ngFor=\"let row of objectPropertyTabs[0].rows\">\n <ngx-mat-entity-input *ngFor=\"let key of row.keys; let i = index; trackBy: trackByFn\"\n [entity]=\"objectProperty\"\n [propertyKey]=\"key\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [hideOmitForCreate]=\"hideOmitForCreate\"\n [hideOmitForEdit]=\"hideOmitForEdit\"\n [validEmpty]=\"!metadata.required(entity)\"\n [isReadOnly]=\"isPropertyReadOnly(objectProperty, key)\"\n class=\"col-lg-{{EntityUtilities.getWidth(objectProperty, key, 'lg')}} col-md-{{EntityUtilities.getWidth(objectProperty, key, 'md')}} col-sm-{{EntityUtilities.getWidth(objectProperty, key, 'sm')}}\"\n (inputChangeEvent)=\"emitChange()\"\n >\n </ngx-mat-entity-input>\n </div>\n </div>\n </div>\n\n <!-------------------------------------------->\n <!-------------- references one ------------->\n <!-------------------------------------------->\n <div *ngSwitchCase=\"DecoratorTypes.REFERENCES_ONE\">\n <mat-form-field>\n <mat-label>{{metadata.displayName}}</mat-label>\n <mat-select [(ngModel)]=\"internalEntity[internalPropertyKey]\"\n [name]=\"internalPropertyKey.toString() + 'input' + referencesOneUUID\"\n #inputModel=\"ngModel\"\n [disabled]=\"internalIsReadOnly\"\n [required]=\"metadata.required(entity)\"\n (ngModelChange)=\"setReferencesOneObject()\"\n >\n <mat-option *ngIf=\"!metadata.required(entity)\">-</mat-option>\n <mat-option *ngFor=\"let value of referencesOneDropdownValues\" [value]=\"value.value\">{{value.displayName}}</mat-option>\n </mat-select>\n <mat-error>{{internalGetValidationErrorMessage(inputModel)}}</mat-error>\n </mat-form-field>\n <!-- iterates over the references one properties -->\n <mat-tab-group *ngIf=\"referencesOnePropertyTabs && referencesOnePropertyTabs.length > 1\" preserveContent>\n <mat-tab *ngFor=\"let tab of referencesOnePropertyTabs; let tI = index; trackBy: trackByFn\" [label]=\"tab.tabName\">\n <div class=\"row\" *ngFor=\"let row of tab.rows;\">\n <ngx-mat-entity-input *ngFor=\"let key of row.keys; let rI = index; trackBy: trackByFn\"\n [entity]=\"referencesOneObject\"\n [propertyKey]=\"key\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [hideOmitForCreate]=\"hideOmitForCreate\"\n [hideOmitForEdit]=\"hideOmitForEdit\"\n [validEmpty]=\"!metadata.required(entity)\"\n [isReadOnly]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(referencesOneObject, key, 'lg')}} col-md-{{EntityUtilities.getWidth(referencesOneObject, key, 'md')}} col-sm-{{EntityUtilities.getWidth(referencesOneObject, key, 'sm')}}\"\n (inputChangeEvent)=\"emitChange()\"\n >\n </ngx-mat-entity-input>\n </div>\n </mat-tab>\n </mat-tab-group>\n\n <div *ngIf=\"referencesOnePropertyTabs && referencesOnePropertyTabs.length <= 1\">\n <div class=\"row\" *ngFor=\"let row of referencesOnePropertyTabs[0].rows\">\n <ngx-mat-entity-input *ngFor=\"let key of row.keys; let i = index; trackBy: trackByFn\"\n [entity]=\"referencesOneObject\"\n [propertyKey]=\"key\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n [hideOmitForCreate]=\"hideOmitForCreate\"\n [hideOmitForEdit]=\"hideOmitForEdit\"\n [validEmpty]=\"!metadata.required(entity)\"\n [isReadOnly]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(referencesOneObject, key, 'lg')}} col-md-{{EntityUtilities.getWidth(referencesOneObject, key, 'md')}} col-sm-{{EntityUtilities.getWidth(referencesOneObject, key, 'sm')}}\"\n (inputChangeEvent)=\"emitChange()\"\n >\n </ngx-mat-entity-input>\n </div>\n </div>\n </div>\n\n <!-------------------------------------------->\n <!-------------------Array-------------------->\n <!-------------------------------------------->\n <div class=\"entityArray\" *ngSwitchCase=\"DecoratorTypes.ARRAY\">\n <div class=\"mat-elevation-z8 elevation-container\">\n <div class=\"array-headline\">\n <b>{{metadataEntityArray.displayName}}</b>\n </div>\n <div *ngIf=\"metadataEntityArray.createInline && !internalIsReadOnly\">\n <mat-tab-group *ngIf=\"arrayItemInlineTabs.length > 1\" preserveContent>\n <mat-tab *ngFor=\"let tab of arrayItemInlineTabs\" [label]=\"tab.tabName\">\n <div class=\"row\" *ngFor=\"let row of tab.rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys; let i = index; trackBy: trackByFn\"\n [entity]=\"arrayItem\"\n [propertyKey]=\"key\"\n [hideOmitForCreate]=\"true\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n class=\"col-lg-{{EntityUtilities.getWidth(arrayItem, key, 'lg')}} col-md-{{EntityUtilities.getWidth(arrayItem, key, 'md')}} col-sm-{{EntityUtilities.getWidth(arrayItem, key, 'sm')}}\"\n (inputChangeEvent)=\"checkIsArrayItemValid()\"\n >\n </ngx-mat-entity-input>\n </div>\n </mat-tab>\n </mat-tab-group>\n\n <div *ngIf=\"arrayItemInlineTabs.length <= 1\">\n <div class=\"row\" *ngFor=\"let row of arrayItemInlineTabs[0].rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys; let i = index; trackBy: trackByFn\"\n [entity]=\"arrayItem\"\n [propertyKey]=\"key\"\n [hideOmitForCreate]=\"true\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n class=\"col-lg-{{EntityUtilities.getWidth(arrayItem, key, 'lg')}} col-md-{{EntityUtilities.getWidth(arrayItem, key, 'md')}} col-sm-{{EntityUtilities.getWidth(arrayItem, key, 'sm')}}\"\n (inputChangeEvent)=\"checkIsArrayItemValid()\"\n >\n </ngx-mat-entity-input>\n </div>\n </div>\n </div>\n\n <div class=\"buttons\" *ngIf=\"!internalIsReadOnly\">\n <button type=\"button\" mat-raised-button\n [disabled]=\"metadataEntityArray.createInline && !isArrayItemValid\"\n (click)=\"addEntity()\">\n {{metadataEntityArray.addButtonLabel}}\n </button>\n <button type=\"button\" mat-raised-button\n [disabled]=\"!entityArraySelection.selected.length\"\n (click)=\"removeFromEntityArray()\">\n {{metadataEntityArray.removeButtonLabel}}\n </button>\n </div>\n \n <mat-table [dataSource]=\"entityArrayDataSource\">\n <!-- select Column -->\n <ng-container matColumnDef=\"select\" *ngIf=\"!internalIsReadOnly\">\n <mat-header-cell *matHeaderCellDef>\n <mat-checkbox\n [disabled]=\"!entityArrayDataSource.data.length\" (change)=\"$event ? SelectionUtilities.masterToggle(entityArraySelection, entityArrayDataSource) : null\"\n [checked]=\"entityArraySelection.hasValue() && SelectionUtilities.isAllSelected(entityArraySelection, entityArrayDataSource)\"\n [indeterminate]=\"entityArraySelection.hasValue() && !SelectionUtilities.isAllSelected(entityArraySelection, entityArrayDataSource)\">\n </mat-checkbox>\n </mat-header-cell>\n <mat-cell *matCellDef=\"let entity\">\n <mat-checkbox (click)=\"$event.stopPropagation()\" (change)=\"$event ? entityArraySelection.toggle(entity) : null\" [checked]=\"entityArraySelection.isSelected(entity)\"></mat-checkbox>\n </mat-cell>\n </ng-container>\n \n <ng-container *ngFor=\"let dCol of metadataEntityArray.displayColumns\" [matColumnDef]=\"dCol.displayName\">\n <mat-header-cell *matHeaderCellDef>\n {{dCol.displayName}}\n </mat-header-cell>\n <mat-cell [class.enabled]=\"!dCol.disableClick\" (click)=\"editArrayItem(entity, dCol)\" *matCellDef=\"let entity\">\n <ng-container *ngIf=\"dCol.Component\">\n <display-column-value [entity]=\"entity\" [ComponentClass]=\"dCol.Component\"></display-column-value>\n </ng-container>\n <ng-container *ngIf=\"!dCol.Component\">\n {{getDisplayColumnValue(entity, dCol)}}\n </ng-container>\n </mat-cell>\n </ng-container>\n \n <mat-header-row *matHeaderRowDef=\"entityArrayDisplayedColumns\"></mat-header-row>\n <mat-row *matRowDef=\"let row; columns: entityArrayDisplayedColumns\"></mat-row>\n </mat-table>\n \n <div class=\"array-error\" *ngIf=\"metadataEntityArray.required(entity) && !entityArrayDataSource.data.length\">\n {{metadataEntityArray.missingErrorMessage}}\n </div>\n </div>\n </div>\n\n <!-------------------------------------------->\n <!------------------ has many ---------------->\n <!-------------------------------------------->\n <div class=\"hasMany\" *ngSwitchCase=\"DecoratorTypes.HAS_MANY\">\n <h2 class=\"title\">{{metadataHasMany.tableData.baseData.title}}</h2>\n\n <div class=\"row\">\n <mat-form-field class=\"col-lg-8 col-md-6 col-sm-12\">\n <mat-label>{{metadataHasMany.tableData.baseData.searchLabel}}</mat-label>\n <input matInput (keyup)=\"applyHasManyFilter($event)\">\n </mat-form-field>\n <div\n *ngIf=\"metadataHasMany.tableData.baseData.tableActions.length\"\n [class.col-lg-2]=\"hasManyAllowCreate\"\n [class.col-lg-4]=\"!hasManyAllowCreate\"\n [class.col-md-3]=\"hasManyAllowCreate\"\n [class.col-md-6]=\"!hasManyAllowCreate\"\n [class.col-sm-6]=\"hasManyAllowCreate\"\n [class.col-sm-12]=\"!hasManyAllowCreate\"\n >\n <button type=\"button\" class=\"actions-button\" [matMenuTriggerFor]=\"menu\" mat-raised-button>\n {{metadataHasMany.tableData.baseData.tableActionsLabel}}\n </button>\n </div>\n <mat-menu #menu=\"matMenu\">\n <button *ngIf=\"metadataHasMany.tableData.baseData.allowJsonImport\" type=\"button\" [disabled]=\"hasManyTableActionDisabled(hasManyImportAction)\" (click)=\"runHasManyTableAction(hasManyImportAction)\" mat-menu-item>\n {{hasManyImportAction.displayName}}\n </button>\n <button type=\"button\" *ngFor=\"let action of metadataHasMany.tableData.baseData.tableActions\" [disabled]=\"hasManyTableActionDisabled(action)\" (click)=\"runHasManyTableAction(action)\" mat-menu-item>\n {{action.displayName}}\n </button>\n </mat-menu>\n\n <div\n *ngIf=\"hasManyAllowCreate\"\n [class.col-lg-2]=\"metadataHasMany.tableData.baseData.tableActions.length\"\n [class.col-lg-4]=\"!metadataHasMany.tableData.baseData.tableActions.length\"\n [class.col-md-3]=\"metadataHasMany.tableData.baseData.tableActions.length\"\n [class.col-md-6]=\"!metadataHasMany.tableData.baseData.tableActions.length\"\n [class.col-sm-6]=\"metadataHasMany.tableData.baseData.tableActions.length\"\n [class.col-sm-12]=\"!metadataHasMany.tableData.baseData.tableActions.length\"\n >\n <button type=\"button\" class=\"create-button\" (click)=\"createHasManyEntity()\" mat-raised-button>\n {{metadataHasMany.tableData.baseData.createButtonLabel}}\n </button>\n </div>\n </div>\n\n <div class=\"mat-elevation-z8 elevation-container\">\n <mat-table [dataSource]=\"hasManyDataSource\" matSort>\n <!-- select Column -->\n <ng-container matColumnDef=\"select\">\n <mat-header-cell *matHeaderCellDef>\n <mat-checkbox (change)=\"$event ? SelectionUtilities.masterToggle(hasManySelection, hasManyDataSource) : null\"\n [checked]=\"hasManySelection.hasValue() && SelectionUtilities.isAllSelected(hasManySelection, hasManyDataSource)\"\n [indeterminate]=\"hasManySelection.hasValue() && !SelectionUtilities.isAllSelected(hasManySelection, hasManyDataSource)\">\n </mat-checkbox>\n </mat-header-cell>\n <mat-cell *matCellDef=\"let entity\" class=\"enabled\">\n <mat-checkbox (click)=\"$event.stopPropagation()\"\n (change)=\"$event ? hasManySelection.toggle(entity) : null\"\n [checked]=\"hasManySelection.isSelected(entity)\">\n </mat-checkbox>\n </mat-cell>\n </ng-container>\n\n <ng-container *ngFor=\"let dCol of metadataHasMany.tableData.baseData.displayColumns\" [matColumnDef]=\"dCol.displayName\">\n <mat-header-cell *matHeaderCellDef mat-sort-header>\n {{dCol.displayName}}\n </mat-header-cell>\n <mat-cell [class.enabled]=\"!dCol.disableClick && (hasManyAllowUpdate(entity) || hasManyAllowRead(entity))\"\n (click)=\"editHasManyEntity(entity, dCol)\"\n *matCellDef=\"let entity\"\n >\n <ng-container *ngIf=\"dCol.Component\">\n <display-column-value [entity]=\"entity\" [ComponentClass]=\"dCol.Component\"></display-column-value>\n </ng-container>\n <ng-container *ngIf=\"!dCol.Component\">\n {{getDisplayColumnValue(entity, dCol)}}\n </ng-container>\n </mat-cell>\n </ng-container>\n\n <mat-header-row *matHeaderRowDef=\"displayedHasManyColumns\"></mat-header-row>\n <mat-row *matRowDef=\"let row; columns: displayedHasManyColumns\"></mat-row>\n </mat-table>\n\n <mat-spinner *ngIf=\"hasManyIsLoading && metadataHasMany.tableData.baseData.displayLoadingSpinner\">\n </mat-spinner>\n\n <mat-paginator [length]=\"hasManyDataSource.filteredData.length\" [pageIndex]=\"0\" [pageSize]=\"10\" [pageSizeOptions]=\"[5, 10, 25, 50]\"></mat-paginator>\n </div>\n </div>\n\n <div *ngSwitchDefault>ERROR: The type {{type}} is not known.</div>\n</div>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<!--------------------------------------------------------->\n<!--------------------Add Array Item Dialog---------------->\n<!--------------------------------------------------------->\n<ng-template #addArrayItemDialog>\n <div class=\"mat-dialog-title\">\n <div>{{addArrayItemDialogData.title}}</div>\n </div>\n\n <form>\n <mat-dialog-content>\n <mat-tab-group *ngIf=\"arrayItemDialogTabs.length > 1\" preserveContent>\n <mat-tab *ngFor=\"let tab of arrayItemDialogTabs\" [label]=\"tab.tabName\">\n <div class=\"row\" *ngFor=\"let row of tab.rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"arrayItem\"\n [propertyKey]=\"key\"\n [hideOmitForCreate]=\"true\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n class=\"col-lg-{{EntityUtilities.getWidth(arrayItem, key, 'lg')}} col-md-{{EntityUtilities.getWidth(arrayItem, key, 'md')}} col-sm-{{EntityUtilities.getWidth(arrayItem, key, 'sm')}}\"\n (inputChangeEvent)=\"checkIsArrayItemValid()\"\n >\n </ngx-mat-entity-input>\n </div>\n </mat-tab>\n </mat-tab-group>\n \n <div *ngIf=\"arrayItemDialogTabs.length <= 1\">\n <div class=\"row\" *ngFor=\"let row of arrayItemDialogTabs[0].rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"arrayItem\"\n [propertyKey]=\"key\"\n [hideOmitForCreate]=\"true\"\n [getValidationErrorMessage]=\"internalGetValidationErrorMessage\"\n class=\"col-lg-{{EntityUtilities.getWidth(arrayItem, key, 'lg')}} col-md-{{EntityUtilities.getWidth(arrayItem, key, 'md')}} col-sm-{{EntityUtilities.getWidth(arrayItem, key, 'sm')}}\"\n (inputChangeEvent)=\"checkIsArrayItemValid()\"\n >\n </ngx-mat-entity-input>\n </div>\n </div>\n </mat-dialog-content>\n \n <mat-dialog-actions>\n <button type=\"submit\" mat-raised-button (click)=\"addArrayItem()\" [disabled]=\"!isArrayItemValid\">\n {{addArrayItemDialogData.createButtonLabel}}\n </button>\n <button type=\"button\" mat-raised-button (click)=\"closeAddArrayItemDialog()\" class=\"cancel-button\">\n {{addArrayItemDialogData.cancelButtonLabel}}\n </button>\n </mat-dialog-actions>\n </form>\n\n</ng-template>\n\n<!--------------------------------------------------------->\n<!--------------------Edit Array Item Dialog---------------->\n<!--------------------------------------------------------->\n<ng-template #editArrayItemDialog>\n <div class=\"mat-dialog-title\">\n <div>{{editArrayItemDialogData.title(arrayItemPriorChanges)}}</div>\n </div>\n \n <form>\n <mat-dialog-content>\n <mat-tab-group *ngIf=\"arrayItemDialogTabs.length > 1\" preserveContent>\n <mat-tab *ngFor=\"let tab of arrayItemDialogTabs\" [label]=\"tab.tabName\">\n <div class=\"row\" *ngFor=\"let row of tab.rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"arrayItem\"\n [propertyKey]=\"key\"\n [hideOmitForEdit]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(arrayItem, key, 'lg')}} col-md-{{EntityUtilities.getWidth(arrayItem, key, 'md')}} col-sm-{{EntityUtilities.getWidth(arrayItem, key, 'sm')}}\"\n (inputChangeEvent)=\"checkArrayItem()\"\n [isReadOnly]=\"isPropertyReadOnly(arrayItem, key)\"\n >\n </ngx-mat-entity-input>\n </div>\n </mat-tab>\n </mat-tab-group>\n \n <div *ngIf=\"arrayItemDialogTabs.length <= 1\">\n <div class=\"row\" *ngFor=\"let row of arrayItemDialogTabs[0].rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"arrayItem\"\n [propertyKey]=\"key\"\n [hideOmitForEdit]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(arrayItem, key, 'lg')}} col-md-{{EntityUtilities.getWidth(arrayItem, key, 'md')}} col-sm-{{EntityUtilities.getWidth(arrayItem, key, 'sm')}}\"\n (inputChangeEvent)=\"checkArrayItem()\"\n [isReadOnly]=\"isPropertyReadOnly(arrayItem, key)\"\n >\n </ngx-mat-entity-input>\n </div>\n </div>\n </mat-dialog-content>\n \n <mat-dialog-actions>\n <button type=\"submit\" (click)=\"saveArrayItem()\" mat-raised-button [disabled]=\"internalIsReadOnly || !isArrayItemValid || !isArrayItemDirty\">\n {{editArrayItemDialogData.confirmButtonLabel}}\n </button>\n <button type=\"button\" mat-raised-button (click)=\"closeEditArrayItemDialog()\" class=\"cancel-button\">\n {{editArrayItemDialogData.cancelButtonLabel}}\n </button>\n </mat-dialog-actions>\n </form>\n</ng-template>\n\n<!--------------------------------------------------------->\n<!--------------------Create Has Many Dialog---------------->\n<!--------------------------------------------------------->\n<ng-template #createHasManyDialog>\n <div class=\"mat-dialog-title\">\n <div>{{metadataHasMany.tableData.createDialogData.title}}</div>\n </div>\n \n <form>\n <mat-dialog-content>\n <mat-tab-group *ngIf=\"hasManyCreateTabs.length > 1\" preserveContent>\n <mat-tab *ngFor=\"let tab of hasManyCreateTabs\" [label]=\"tab.tabName\">\n <div class=\"row\" *ngFor=\"let row of tab.rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"hasManyEntity\"\n [propertyKey]=\"key\"\n [hideOmitForCreate]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(hasManyEntity, key, 'lg')}} col-md-{{EntityUtilities.getWidth(hasManyEntity, key, 'md')}} col-sm-{{EntityUtilities.getWidth(hasManyEntity, key, 'sm')}}\"\n (inputChangeEvent)=\"checkIsHasManyEntityValid('create')\"\n >\n </ngx-mat-entity-input>\n </div>\n </mat-tab>\n </mat-tab-group>\n \n <div *ngIf=\"hasManyCreateTabs.length <= 1\">\n <div class=\"row\" *ngFor=\"let row of hasManyCreateTabs[0].rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"hasManyEntity\"\n [propertyKey]=\"key\"\n [hideOmitForCreate]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(hasManyEntity, key, 'lg')}} col-md-{{EntityUtilities.getWidth(hasManyEntity, key, 'md')}} col-sm-{{EntityUtilities.getWidth(hasManyEntity, key, 'sm')}}\"\n (inputChangeEvent)=\"checkIsHasManyEntityValid('create')\"\n >\n </ngx-mat-entity-input>\n </div>\n </div>\n </mat-dialog-content>\n \n <mat-dialog-actions>\n <button type=\"submit\" (click)=\"dialogCreateHasMany()\" mat-raised-button [disabled]=\"!isHasManyEntityValid\">\n {{metadataHasMany.tableData.createDialogData.createButtonLabel}}\n </button>\n <button type=\"button\" mat-raised-button (click)=\"dialogCancelCreateHasMany()\" class=\"cancel-button\">\n {{metadataHasMany.tableData.createDialogData.cancelButtonLabel}}\n </button>\n </mat-dialog-actions>\n </form>\n \n</ng-template>\n\n<ng-template #editHasManyDialog>\n <div class=\"mat-dialog-title\">\n <div>{{metadataHasMany.tableData.editData.title(hasManyEntityPriorChanges)}}</div>\n\n <div class=\"actions-container\">\n <button *ngIf=\"metadataHasMany.tableData.editData.actions.length\" type=\"button\" [matMenuTriggerFor]=\"menu\" mat-raised-button>\n {{metadataHasMany.tableData.editData.actionsLabel}}\n </button>\n <mat-menu #menu=\"matMenu\">\n <button type=\"button\" *ngFor=\"let action of metadataHasMany.tableData.editData.actions\" [disabled]=\"hasManyEditActionDisabled(action)\" (click)=\"hasManyRunEditAction(action)\" mat-menu-item>\n {{action.displayName}}\n </button>\n </mat-menu>\n \n <button type=\"button\" *ngIf=\"hasManyAllowDelete(hasManyEntity)\" mat-raised-button (click)=\"deleteHasManyEntity()\" color=\"warn\" class=\"delete-button\" tabindex=\"-1\">\n {{metadataHasMany.tableData.editData.deleteButtonLabel}}\n </button>\n </div>\n </div>\n \n <form>\n <mat-dialog-content>\n <mat-tab-group *ngIf=\"hasManyUpdateTabs.length > 1\" preserveContent>\n <mat-tab *ngFor=\"let tab of hasManyUpdateTabs\" [label]=\"tab.tabName\">\n <div class=\"row\" *ngFor=\"let row of tab.rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"hasManyEntity\"\n [propertyKey]=\"key\"\n [hideOmitForEdit]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(hasManyEntity, key, 'lg')}} col-md-{{EntityUtilities.getWidth(hasManyEntity, key, 'md')}} col-sm-{{EntityUtilities.getWidth(hasManyEntity, key, 'sm')}}\"\n (inputChangeEvent)=\"checkHasManyEntity()\"\n [isReadOnly]=\"isPropertyReadOnly(hasManyEntity, key)\"\n >\n </ngx-mat-entity-input>\n </div>\n </mat-tab>\n </mat-tab-group>\n \n <div *ngIf=\"hasManyUpdateTabs.length <= 1\">\n <div class=\"row\" *ngFor=\"let row of hasManyUpdateTabs[0].rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"hasManyEntity\"\n [propertyKey]=\"key\"\n [hideOmitForEdit]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(hasManyEntity, key, 'lg')}} col-md-{{EntityUtilities.getWidth(hasManyEntity, key, 'md')}} col-sm-{{EntityUtilities.getWidth(hasManyEntity, key, 'sm')}}\"\n (inputChangeEvent)=\"checkHasManyEntity()\"\n [isReadOnly]=\"isPropertyReadOnly(hasManyEntity, key)\"\n >\n </ngx-mat-entity-input>\n </div>\n </div>\n </mat-dialog-content>\n \n <mat-dialog-actions>\n <button type=\"submit\" (click)=\"dialogEditHasMany()\" mat-raised-button [disabled]=\"internalIsReadOnly || !isHasManyEntityValid || !isHasManyEntityDirty\">\n {{metadataHasMany.tableData.editData.confirmButtonLabel}}\n </button>\n <button type=\"button\" mat-raised-button (click)=\"dialogCancelEditHasMany()\" class=\"cancel-button\">\n {{metadataHasMany.tableData.editData.cancelButtonLabel}}\n </button>\n </mat-dialog-actions>\n </form> \n</ng-template>", styles: [".mdc-data-table__row:last-child .mdc-data-table__cell{border-bottom:1px solid rgba(0,0,0,.12)}.mat-dialog-title{padding:12px 20px;display:flex;justify-content:space-between;align-items:center}.mat-dialog-title div{font-size:var(--mdc-dialog-subhead-size, 14px);font-weight:var(--mdc-dialog-subhead-weight, 500)}::ng-deep .mdc-dialog .mdc-dialog__content{padding:6px 20px!important}mat-dialog-actions{justify-content:space-between;align-items:center;padding-left:20px;padding-right:20px}mat-spinner{margin:10px auto}::ng-deep .mat-mdc-tab-body-wrapper{margin-left:-12px;margin-right:-12px}::ng-deep mat-tab-body{padding-top:10px;padding-left:12px;padding-right:12px}::ng-deep mat-tab-body .mat-mdc-tab-body-content{overflow:initial}::ng-deep .mat-mdc-form-field.mat-form-field-disabled label{color:#0009}::ng-deep .mat-mdc-form-field.mat-form-field-disabled input,::ng-deep .mat-mdc-form-field.mat-form-field-disabled textarea,::ng-deep .mat-mdc-form-field.mat-form-field-disabled .mat-mdc-select-disabled,::ng-deep .mat-mdc-form-field.mat-form-field-disabled .mat-mdc-select-value{color:#000!important}::ng-deep .mat-mdc-form-field.mat-form-field-disabled .mdc-checkbox .mdc-checkbox__native-control[disabled]:not(:checked):not(:indeterminate):not([data-indeterminate=true])~.mdc-checkbox__background{border-color:#707070}::ng-deep .mat-mdc-form-field.mat-form-field-disabled .mdc-checkbox .mdc-checkbox__native-control[disabled]:checked~.mdc-checkbox__background,::ng-deep .mat-mdc-form-field.mat-form-field-disabled .mdc-checkbox .mdc-checkbox__native-control[disabled]:indeterminate~.mdc-checkbox__background,::ng-deep .mat-mdc-form-field.mat-form-field-disabled .mdc-checkbox .mdc-checkbox__native-control[data-indeterminate=true][disabled]~.mdc-checkbox__background{background-color:#000}::ng-deep .mat-mdc-form-field.mat-form-field-disabled .mat-mdc-chip.mat-mdc-standard-chip.mat-mdc-chip-disabled{opacity:1}::ng-deep .mat-mdc-form-field.mat-form-field-disabled .mat-mdc-chip.mat-mdc-standard-chip.mat-mdc-chip-disabled button{opacity:.2}::ng-deep .mat-mdc-form-field.mat-form-field-disabled .mdc-switch.mdc-switch--selected:disabled .mdc-switch__icons,::ng-deep .mat-mdc-form-field.mat-form-field-disabled .mdc-switch.mdc-switch--unselected:disabled .mdc-switch__icons{opacity:1}::ng-deep .mat-mdc-form-field.mat-form-field-disabled .mdc-switch.mdc-switch--selected:disabled .mdc-switch__handle:after,::ng-deep .mat-mdc-form-field.mat-form-field-disabled .mdc-switch.mdc-switch--unselected:disabled .mdc-switch__handle:after{opacity:1;background:black}::ng-deep .mat-mdc-form-field.mat-form-field-disabled .mdc-switch.mdc-switch--selected:disabled .mdc-switch__track,::ng-deep .mat-mdc-form-field.mat-form-field-disabled .mdc-switch.mdc-switch--unselected:disabled .mdc-switch__track{opacity:.3}::ng-deep .mat-mdc-form-field.mat-form-field-disabled .mat-date-range-input-inner:disabled{color:#000}::ng-deep #slider.mat-mdc-slider.mdc-slider--disabled{opacity:1}::ng-deep #slider.mat-mdc-slider.mdc-slider--disabled .mdc-slider__input{cursor:default}::ng-deep #slider.mat-mdc-slider.mdc-slider--disabled mat-slider-visual-thumb .mat-mdc-slider,::ng-deep #slider.mat-mdc-slider.mdc-slider--disabled mat-slider-visual-thumb .mdc-slider__thumb:hover,::ng-deep #slider.mat-mdc-slider.mdc-slider--disabled mat-slider-visual-thumb .mdc-slider__thumb-knob{background-color:#000;border-color:#000}::ng-deep .mat-mdc-form-field .mat-mdc-slide-toggle{opacity:1}.entityArray .elevation-container{border-radius:5px;padding:15px;margin-bottom:15px;margin-top:15px}.entityArray .array-headline{padding-bottom:10px}.entityArray .buttons{display:flex;justify-content:space-between;margin-bottom:10px;margin-top:5px}.entityArray mat-table{border:1px solid #E0E0E0;border-radius:5px;padding-top:5px;padding-bottom:25px}.entityArray .mat-column-select{flex:0 0 75px}.entityArray .enabled:hover{cursor:pointer}.entityArray .array-error{display:flex;align-items:center;justify-content:center;margin-top:-25.8px;border:1px solid #E0E0E0;background-color:#f8d3d7;color:#721c24;height:25.8px;border-bottom-left-radius:5px;border-bottom-right-radius:5px}.hasMany button{width:100%;height:56px;line-height:24px;font-size:16px}.hasMany .title{text-align:center}.hasMany .elevation-container{border-radius:5px;padding:5px;margin-bottom:15px}.hasMany .mat-column-select{flex:0 0 75px}.hasMany .enabled:hover{cursor:pointer}\n"] }]
4624
4953
  }], ctorParameters: function () { return [{ type: i1.MatDialog }, { type: i0.EnvironmentInjector }, { type: i2$3.Router }, { type: undefined, decorators: [{
4625
4954
  type: Inject,
4626
4955
  args: [NGX_GET_VALIDATION_ERROR_MESSAGE]
@@ -4648,10 +4977,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.0", ngImpor
4648
4977
  args: ['editArrayItemDialog']
4649
4978
  }], hasManyPaginator: [{
4650
4979
  type: ViewChild,
4651
- args: [MatPaginator, { static: true }]
4980
+ args: [MatPaginator]
4652
4981
  }], hasManySort: [{
4653
4982
  type: ViewChild,
4654
- args: [MatSort, { static: true }]
4983
+ args: [MatSort]
4655
4984
  }], hasManyFilter: [{
4656
4985
  type: ViewChild,
4657
4986
  args: ['filter', { static: true }]
@@ -4709,7 +5038,10 @@ class NgxMatEntityInputModule {
4709
5038
  MatMenuModule,
4710
5039
  MatProgressSpinnerModule,
4711
5040
  MatPaginatorModule,
4712
- DisplayColumnValueComponent], exports: [NgxMatEntityInputComponent] });
5041
+ DisplayColumnValueComponent,
5042
+ MatSortModule,
5043
+ PasswordMatchValidatorDirective,
5044
+ NumberDirective], exports: [NgxMatEntityInputComponent] });
4713
5045
  static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "16.2.0", ngImport: i0, type: NgxMatEntityInputModule, imports: [CommonModule,
4714
5046
  MatInputModule,
4715
5047
  FormsModule,
@@ -4728,7 +5060,8 @@ class NgxMatEntityInputModule {
4728
5060
  MatTabsModule,
4729
5061
  MatMenuModule,
4730
5062
  MatProgressSpinnerModule,
4731
- MatPaginatorModule] });
5063
+ MatPaginatorModule,
5064
+ MatSortModule] });
4732
5065
  }
4733
5066
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.0", ngImport: i0, type: NgxMatEntityInputModule, decorators: [{
4734
5067
  type: NgModule,
@@ -4781,12 +5114,142 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.0", ngImpor
4781
5114
  MatMenuModule,
4782
5115
  MatProgressSpinnerModule,
4783
5116
  MatPaginatorModule,
4784
- DisplayColumnValueComponent
5117
+ DisplayColumnValueComponent,
5118
+ MatSortModule,
5119
+ PasswordMatchValidatorDirective,
5120
+ NumberDirective
4785
5121
  ],
4786
5122
  exports: [NgxMatEntityInputComponent]
4787
5123
  }]
4788
5124
  }] });
4789
5125
 
5126
+ /**
5127
+ * A directive that displays a tooltip on hover.
5128
+ */
5129
+ class TooltipDirective {
5130
+ el;
5131
+ renderer;
5132
+ /**
5133
+ * The content to display inside the tooltip.
5134
+ */
5135
+ tooltip;
5136
+ tooltipElement;
5137
+ isTooltipVisible = false;
5138
+ closeListeners = [];
5139
+ constructor(el, renderer) {
5140
+ this.el = el;
5141
+ this.renderer = renderer;
5142
+ }
5143
+ /**
5144
+ * Shows the tooltip.
5145
+ */
5146
+ onMouseEnter() {
5147
+ if (!this.isTooltipVisible) {
5148
+ this.showTooltip();
5149
+ this.registerCloseListeners();
5150
+ }
5151
+ else {
5152
+ this.hideTooltip();
5153
+ this.removeCloseListeners();
5154
+ }
5155
+ }
5156
+ /**
5157
+ * Hides the tooltip.
5158
+ */
5159
+ onMouseLeave() {
5160
+ this.hideTooltip();
5161
+ this.removeCloseListeners();
5162
+ }
5163
+ showTooltip() {
5164
+ if (!this.tooltipElement) {
5165
+ this.tooltipElement = this.renderer.createElement('div');
5166
+ this.tooltipElement.innerHTML = this.tooltip;
5167
+ const rect = this.el.nativeElement.getBoundingClientRect();
5168
+ this.renderer.setStyle(this.tooltipElement, 'z-index', '1000');
5169
+ this.renderer.setStyle(this.tooltipElement, 'position', 'absolute');
5170
+ this.renderer.setStyle(this.tooltipElement, 'padding', '4px 8px 4px 8px');
5171
+ this.renderer.setStyle(this.tooltipElement, 'border-radius', '5px');
5172
+ this.renderer.setStyle(this.tooltipElement, 'background-color', '#616161');
5173
+ this.renderer.setStyle(this.tooltipElement, 'color', 'white');
5174
+ this.renderer.setStyle(this.tooltipElement, 'max-height', '30vh');
5175
+ this.renderer.setStyle(this.tooltipElement, 'overflow', 'scroll');
5176
+ this.renderer.appendChild(document.body, this.tooltipElement);
5177
+ const left = window.scrollX + rect.left - (this.tooltipElement.clientWidth / 2) + 18;
5178
+ this.renderer.setStyle(this.tooltipElement, 'left', `${left}px`);
5179
+ const top = window.scrollY + rect.top - this.tooltipElement.clientHeight - 5;
5180
+ this.renderer.setStyle(this.tooltipElement, 'top', `${top}px`);
5181
+ this.isTooltipVisible = true;
5182
+ }
5183
+ }
5184
+ registerCloseListeners() {
5185
+ setTimeout(() => {
5186
+ this.closeListeners.push(this.getCloseListener('click'), this.getCloseListener('touchmove'), this.getCloseListener('resize'));
5187
+ }, 100);
5188
+ }
5189
+ getCloseListener(event) {
5190
+ return this.renderer.listen('document', event, () => {
5191
+ this.hideTooltip();
5192
+ this.removeCloseListeners();
5193
+ });
5194
+ }
5195
+ hideTooltip() {
5196
+ if (this.tooltipElement) {
5197
+ this.renderer.removeChild(this.el.nativeElement, this.tooltipElement);
5198
+ this.tooltipElement = undefined;
5199
+ this.isTooltipVisible = false;
5200
+ }
5201
+ }
5202
+ removeCloseListeners() {
5203
+ for (const listener of this.closeListeners) {
5204
+ listener();
5205
+ }
5206
+ this.closeListeners = [];
5207
+ }
5208
+ ngOnDestroy() {
5209
+ this.removeCloseListeners();
5210
+ }
5211
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.0", ngImport: i0, type: TooltipDirective, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Directive });
5212
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.0", type: TooltipDirective, isStandalone: true, selector: "[tooltip]", inputs: { tooltip: "tooltip" }, host: { listeners: { "mouseenter": "onMouseEnter()", "click": "onMouseEnter()", "mouseleave": "onMouseLeave()", "window:resize": "onMouseLeave()" } }, ngImport: i0 });
5213
+ }
5214
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.0", ngImport: i0, type: TooltipDirective, decorators: [{
5215
+ type: Directive,
5216
+ args: [{
5217
+ selector: '[tooltip]',
5218
+ standalone: true
5219
+ }]
5220
+ }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i0.Renderer2 }]; }, propDecorators: { tooltip: [{
5221
+ type: Input
5222
+ }], onMouseEnter: [{
5223
+ type: HostListener,
5224
+ args: ['mouseenter']
5225
+ }, {
5226
+ type: HostListener,
5227
+ args: ['click']
5228
+ }], onMouseLeave: [{
5229
+ type: HostListener,
5230
+ args: ['mouseleave']
5231
+ }, {
5232
+ type: HostListener,
5233
+ args: ['window:resize']
5234
+ }] } });
5235
+
5236
+ /**
5237
+ * A component that displays an info-symbol and a tooltip when it is hovered/clicked.
5238
+ */
5239
+ class TooltipComponent {
5240
+ tooltipContent;
5241
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.0", ngImport: i0, type: TooltipComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
5242
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.0", type: TooltipComponent, isStandalone: true, selector: "ngx-mat-entity-tooltip", inputs: { tooltipContent: "tooltipContent" }, ngImport: i0, template: "<div [tooltip]=\"tooltipContent\" class=\"info\">\n <i class=\"fas fa-info\"></i>\n</div>", styles: [".info{display:flex;align-items:center;justify-content:center;height:25px;min-height:25px;width:25px;min-width:25px;border:2px solid black;border-radius:50%}.info:hover{cursor:pointer}\n"], dependencies: [{ kind: "directive", type: TooltipDirective, selector: "[tooltip]", inputs: ["tooltip"] }] });
5243
+ }
5244
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.0", ngImport: i0, type: TooltipComponent, decorators: [{
5245
+ type: Component,
5246
+ args: [{ selector: 'ngx-mat-entity-tooltip', standalone: true, imports: [
5247
+ TooltipDirective
5248
+ ], template: "<div [tooltip]=\"tooltipContent\" class=\"info\">\n <i class=\"fas fa-info\"></i>\n</div>", styles: [".info{display:flex;align-items:center;justify-content:center;height:25px;min-height:25px;width:25px;min-width:25px;border:2px solid black;border-radius:50%}.info:hover{cursor:pointer}\n"] }]
5249
+ }], propDecorators: { tooltipContent: [{
5250
+ type: Input
5251
+ }] } });
5252
+
4790
5253
  // eslint-disable-next-line jsdoc/require-jsdoc
4791
5254
  class PageEditDataBuilder extends BaseBuilder {
4792
5255
  constructor(data) {
@@ -4844,13 +5307,17 @@ class NgxMatEntityEditPageComponent {
4844
5307
  EntityClass;
4845
5308
  inputData;
4846
5309
  http;
5310
+ el;
5311
+ renderer;
4847
5312
  EntityUtilities = EntityUtilities;
4848
5313
  entityTabs;
4849
5314
  entity;
4850
5315
  entityPriorChanges;
4851
5316
  data;
5317
+ validationErrors = [];
4852
5318
  isEntityValid = true;
4853
5319
  isEntityDirty = false;
5320
+ tooltipContent = '';
4854
5321
  isEntityReadOnly;
4855
5322
  allowDelete;
4856
5323
  inConfirmNavigation = false;
@@ -4858,7 +5325,7 @@ class NgxMatEntityEditPageComponent {
4858
5325
  get hasUnsavedChanges() {
4859
5326
  return this.isEntityDirty && this.data.editData.unsavedChangesRequireConfirmDialog;
4860
5327
  }
4861
- constructor(dialog, location, route, injector, entityService, EntityClass, inputData, http) {
5328
+ constructor(dialog, location, route, injector, entityService, EntityClass, inputData, http, el, renderer) {
4862
5329
  this.dialog = dialog;
4863
5330
  this.location = location;
4864
5331
  this.route = route;
@@ -4867,6 +5334,8 @@ class NgxMatEntityEditPageComponent {
4867
5334
  this.EntityClass = EntityClass;
4868
5335
  this.inputData = inputData;
4869
5336
  this.http = http;
5337
+ this.el = el;
5338
+ this.renderer = renderer;
4870
5339
  }
4871
5340
  /**
4872
5341
  * Checks if the input with the given key is readonly.
@@ -4875,7 +5344,7 @@ class NgxMatEntityEditPageComponent {
4875
5344
  * @returns Whether or not the input for the key is read only.
4876
5345
  */
4877
5346
  isReadOnly(key) {
4878
- return this.injector.runInContext(() => {
5347
+ return runInInjectionContext(this.injector, () => {
4879
5348
  const metadata = EntityUtilities.getPropertyMetadata(this.entity, key);
4880
5349
  return this.isEntityReadOnly || metadata.isReadOnly(this.entity);
4881
5350
  });
@@ -4894,11 +5363,34 @@ class NgxMatEntityEditPageComponent {
4894
5363
  }
4895
5364
  this.entity = new this.EntityClass(foundEntity);
4896
5365
  this.entityPriorChanges = LodashUtilities.cloneDeep(this.entity);
4897
- this.injector.runInContext(() => {
5366
+ runInInjectionContext(this.injector, () => {
4898
5367
  this.isEntityReadOnly = !this.data.allowUpdate(this.entityPriorChanges);
4899
5368
  this.allowDelete = this.data.allowDelete(this.entityPriorChanges);
4900
5369
  });
4901
5370
  this.entityTabs = EntityUtilities.getEntityTabs(this.entity, false, true);
5371
+ setTimeout(() => this.checkOffset(), 1);
5372
+ setTimeout(() => this.checkIsEntityValid(), 1);
5373
+ }
5374
+ /**
5375
+ * Checks if the bottom row should be displayed as fixed.
5376
+ */
5377
+ onScroll() {
5378
+ this.checkOffset();
5379
+ }
5380
+ checkOffset() {
5381
+ const scrollY = window.scrollY;
5382
+ const bottomRow = this.el.nativeElement.querySelector('.bottom-row');
5383
+ const bottomRowContainer = this.el.nativeElement.querySelector('.bottom-row-container');
5384
+ if (bottomRow && bottomRowContainer) {
5385
+ const bottomRowContainerOffset = bottomRowContainer.offsetTop;
5386
+ const windowHeight = window.innerHeight;
5387
+ if (scrollY + windowHeight >= bottomRowContainerOffset) {
5388
+ this.renderer.removeClass(bottomRow, 'fixed');
5389
+ }
5390
+ else {
5391
+ this.renderer.addClass(bottomRow, 'fixed');
5392
+ }
5393
+ }
4902
5394
  }
4903
5395
  /**
4904
5396
  * Whether the page can be left without confirmation (of unsaved changes).
@@ -4912,9 +5404,14 @@ class NgxMatEntityEditPageComponent {
4912
5404
  * Checks if the entity has become invalid or dirty.
4913
5405
  */
4914
5406
  async checkEntity() {
4915
- this.isEntityValid = EntityUtilities.isEntityValid(this.entity, 'update');
5407
+ this.checkIsEntityValid();
4916
5408
  this.isEntityDirty = await EntityUtilities.isDirty(this.entity, this.entityPriorChanges, this.http);
4917
5409
  }
5410
+ checkIsEntityValid() {
5411
+ this.validationErrors = ValidationUtilities.getEntityValidationErrors(this.entity, 'update');
5412
+ this.tooltipContent = runInInjectionContext(this.injector, () => getValidationErrorsTooltipContent(this.validationErrors));
5413
+ this.isEntityValid = this.validationErrors.length === 0;
5414
+ }
4918
5415
  /**
4919
5416
  * Tries to save the changes and close the dialog afterwards.
4920
5417
  * Also handles the confirmation if required.
@@ -5051,8 +5548,8 @@ class NgxMatEntityEditPageComponent {
5051
5548
  return !action.enabled(this.entityPriorChanges);
5052
5549
  });
5053
5550
  }
5054
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.0", ngImport: i0, type: NgxMatEntityEditPageComponent, deps: [{ token: i1.MatDialog }, { token: i1$1.Location }, { token: i2$3.ActivatedRoute }, { token: i0.EnvironmentInjector }, { token: NGX_EDIT_DATA_ENTITY_SERVICE }, { token: NGX_EDIT_DATA_ENTITY }, { token: NGX_EDIT_DATA }, { token: i2.HttpClient }], target: i0.ɵɵFactoryTarget.Component });
5055
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.0", type: NgxMatEntityEditPageComponent, isStandalone: true, selector: "ngx-mat-entity-edit-page", host: { listeners: { "window:beforeunload": "canDeactivate()" } }, ngImport: i0, template: "<div *ngIf=\"!entityTabs && data.displayLoadingSpinner\" class=\"container\">\n <br>\n <mat-spinner></mat-spinner>\n <br>\n</div>\n\n<div *ngIf=\"entityTabs\" class=\"container\">\n <br>\n\n <!------------>\n <!-- Header -->\n <!------------>\n <div class=\"header\">\n <div class=\"save-cancel-container\">\n <button type=\"button\" [class.unsavedChanges]=\"hasUnsavedChanges\" mat-raised-button (click)=\"navigateBack()\" class=\"back-button\" tabindex=\"-1\">\n <i class=\"fas fa-chevron-left\"></i>\n {{data.editData.cancelButtonLabel}}\n <i class=\"fas fa-warning\" *ngIf=\"hasUnsavedChanges\"></i>\n </button>\n <button type=\"button\" class=\"save-button\" (click)=\"edit()\" mat-raised-button [disabled]=\"isEntityReadOnly || !isEntityValid || !isEntityDirty\">\n {{data.editData.confirmButtonLabel}}\n </button>\n </div>\n <div class=\"actions-container\">\n <button *ngIf=\"data.editData.actions.length\" type=\"button\" [matMenuTriggerFor]=\"menu\" mat-raised-button>\n {{data.editData.actionsLabel}}\n </button>\n <mat-menu #menu=\"matMenu\">\n <button type=\"button\" *ngFor=\"let action of data.editData.actions\" [disabled]=\"editActionDisabled(action)\" (click)=\"runEditAction(action)\" mat-menu-item>\n {{action.displayName}}\n </button>\n </mat-menu>\n \n <button type=\"button\" *ngIf=\"allowDelete\" mat-raised-button (click)=\"delete()\" color=\"warn\" class=\"delete-button\" tabindex=\"-1\">\n {{data.editData.deleteButtonLabel}}\n </button>\n </div>\n </div>\n\n <h1>{{data.editData.title(entityPriorChanges)}}</h1>\n\n <!----------->\n <!-- Input -->\n <!----------->\n <form>\n <mat-tab-group *ngIf=\"entityTabs.length > 1\" preserveContent>\n <mat-tab *ngFor=\"let tab of entityTabs\" [label]=\"tab.tabName\">\n <div class=\"row\" *ngFor=\"let row of tab.rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"entity\"\n [propertyKey]=\"key\"\n [hideOmitForEdit]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(entity, key, 'lg')}} col-md-{{EntityUtilities.getWidth(entity, key, 'md')}} col-sm-{{EntityUtilities.getWidth(entity, key, 'sm')}}\"\n (inputChangeEvent)=\"checkEntity()\"\n [isReadOnly]=\"isReadOnly(key)\"\n >\n </ngx-mat-entity-input>\n </div>\n </mat-tab>\n </mat-tab-group>\n \n <div *ngIf=\"entityTabs.length <= 1\">\n <span class=\"no-entity-tabs\" *ngIf=\"!entityTabs.length\">\n ERROR: No Inputs. Did you correctly assign all values in the model constructor?\n </span>\n <div class=\"row\" *ngFor=\"let row of entityTabs[0]?.rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"entity\"\n [propertyKey]=\"key\"\n [hideOmitForEdit]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(entity, key, 'lg')}} col-md-{{EntityUtilities.getWidth(entity, key, 'md')}} col-sm-{{EntityUtilities.getWidth(entity, key, 'sm')}}\"\n (inputChangeEvent)=\"checkEntity()\"\n [isReadOnly]=\"isReadOnly(key)\"\n >\n </ngx-mat-entity-input>\n </div>\n </div>\n\n <button type=\"submit\" (click)=\"edit()\" mat-raised-button [disabled]=\"isEntityReadOnly || !isEntityValid || !isEntityDirty\">\n {{data.editData.confirmButtonLabel}}\n </button>\n </form>\n\n <br>\n</div>", styles: ["h1{text-align:center}mat-spinner{margin:10px auto}.fa-warning{color:orange}.no-entity-tabs{padding:10px;background-color:red;color:#f5f5f5}.header{display:flex;margin-bottom:5px;gap:10px;flex-wrap:wrap}.header button{min-width:150px}.header .save-cancel-container{display:flex;justify-content:flex-start;column-gap:10px;width:calc(50% - 10px)}.header .actions-container{display:flex;justify-content:flex-end;gap:10px;width:calc(50% - 10px)}.unsavedChanges{background-color:#ffe48d}@media (max-width: 800px){.header{margin-bottom:10px;gap:15px}.header button{min-width:0px;width:50%}.header .save-cancel-container,.header .actions-container{width:100%;gap:15px}}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i5.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", inputs: ["disabled", "disableRipple", "color"], exportAs: ["matButton"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "component", type: i6$2.MatTab, selector: "mat-tab", inputs: ["disabled"], exportAs: ["matTab"] }, { kind: "component", type: i6$2.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "disableRipple", "fitInkBarToContent", "mat-stretch-tabs"], exportAs: ["matTabGroup"] }, { kind: "ngmodule", type: NgxMatEntityInputModule }, { kind: "component", type: NgxMatEntityInputComponent, selector: "ngx-mat-entity-input", inputs: ["entity", "propertyKey", "getValidationErrorMessage", "hideOmitForCreate", "hideOmitForEdit", "validEmpty", "isReadOnly"], outputs: ["inputChangeEvent"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i15.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i9.MatMenu, selector: "mat-menu", exportAs: ["matMenu"] }, { kind: "component", type: i9.MatMenuItem, selector: "[mat-menu-item]", inputs: ["disabled", "disableRipple", "role"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i9.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", exportAs: ["matMenuTrigger"] }] });
5551
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.0", ngImport: i0, type: NgxMatEntityEditPageComponent, deps: [{ token: i1.MatDialog }, { token: i1$1.Location }, { token: i2$3.ActivatedRoute }, { token: i0.EnvironmentInjector }, { token: NGX_EDIT_DATA_ENTITY_SERVICE }, { token: NGX_EDIT_DATA_ENTITY }, { token: NGX_EDIT_DATA }, { token: i2.HttpClient }, { token: i0.ElementRef }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Component });
5552
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.0", type: NgxMatEntityEditPageComponent, isStandalone: true, selector: "ngx-mat-entity-edit-page", host: { listeners: { "window:scroll": "onScroll()", "window:beforeunload": "canDeactivate()" } }, ngImport: i0, template: "<div *ngIf=\"!entityTabs && data.displayLoadingSpinner\" class=\"container\">\n <br>\n <mat-spinner></mat-spinner>\n <br>\n</div>\n\n<div *ngIf=\"entityTabs\" class=\"container\">\n <br>\n\n <!------------>\n <!-- Header -->\n <!------------>\n <div class=\"header\">\n <div class=\"save-cancel-container\">\n <button type=\"button\" [class.unsavedChanges]=\"hasUnsavedChanges\" mat-raised-button (click)=\"navigateBack()\" class=\"back-button\" tabindex=\"-1\">\n <i class=\"fas fa-chevron-left\"></i>\n {{data.editData.cancelButtonLabel}}\n <i class=\"fas fa-warning\" *ngIf=\"hasUnsavedChanges\"></i>\n </button>\n </div>\n <div class=\"actions-container\">\n <button *ngIf=\"data.editData.actions.length\" type=\"button\" [matMenuTriggerFor]=\"menu\" mat-raised-button>\n {{data.editData.actionsLabel}}\n </button>\n <mat-menu #menu=\"matMenu\">\n <button type=\"button\" *ngFor=\"let action of data.editData.actions\" [disabled]=\"editActionDisabled(action)\" (click)=\"runEditAction(action)\" mat-menu-item>\n {{action.displayName}}\n </button>\n </mat-menu>\n \n <button type=\"button\" *ngIf=\"allowDelete\" mat-raised-button (click)=\"delete()\" color=\"warn\" class=\"delete-button\" tabindex=\"-1\">\n {{data.editData.deleteButtonLabel}}\n </button>\n </div>\n </div>\n\n <h1>{{data.editData.title(entityPriorChanges)}}</h1>\n\n <!----------->\n <!-- Input -->\n <!----------->\n <form>\n <mat-tab-group *ngIf=\"entityTabs.length > 1\" preserveContent>\n <mat-tab *ngFor=\"let tab of entityTabs\" [label]=\"tab.tabName\">\n <div class=\"row\" *ngFor=\"let row of tab.rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"entity\"\n [propertyKey]=\"key\"\n [hideOmitForEdit]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(entity, key, 'lg')}} col-md-{{EntityUtilities.getWidth(entity, key, 'md')}} col-sm-{{EntityUtilities.getWidth(entity, key, 'sm')}}\"\n (inputChangeEvent)=\"checkEntity()\"\n [isReadOnly]=\"isReadOnly(key)\"\n >\n </ngx-mat-entity-input>\n </div>\n </mat-tab>\n </mat-tab-group>\n \n <div *ngIf=\"entityTabs.length <= 1\">\n <span class=\"no-entity-tabs\" *ngIf=\"!entityTabs.length\">\n ERROR: No Inputs. Did you correctly assign all values in the model constructor?\n </span>\n <div class=\"row\" *ngFor=\"let row of entityTabs[0]?.rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"entity\"\n [propertyKey]=\"key\"\n [hideOmitForEdit]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(entity, key, 'lg')}} col-md-{{EntityUtilities.getWidth(entity, key, 'md')}} col-sm-{{EntityUtilities.getWidth(entity, key, 'sm')}}\"\n (inputChangeEvent)=\"checkEntity()\"\n [isReadOnly]=\"isReadOnly(key)\"\n >\n </ngx-mat-entity-input>\n </div>\n </div>\n\n <div class=\"bottom-row-container\">\n <div class=\"bottom-row container\" style=\"margin-top: 10px;\">\n <button type=\"submit\" (click)=\"edit()\" mat-raised-button\n [disabled]=\"isEntityReadOnly || !isEntityValid || !isEntityDirty\"\n [matBadge]=\"validationErrors.length\"\n [matBadgeHidden]=\"!validationErrors.length\"\n matBadgeColor=\"warn\"\n >\n {{data.editData.confirmButtonLabel}}\n </button>\n <ngx-mat-entity-tooltip *ngIf=\"validationErrors.length\" [tooltipContent]=\"tooltipContent\"></ngx-mat-entity-tooltip>\n </div>\n </div>\n </form>\n\n <br>\n</div>", styles: ["h1{text-align:center}mat-spinner{margin:10px auto}.fa-warning{color:orange}.bottom-row{display:flex;align-items:center;column-gap:10px}.fixed{position:fixed;bottom:0;left:0;right:0;width:100%;z-index:1000;padding:8px 20px;background-color:#fff}.no-entity-tabs{padding:10px;background-color:red;color:#f5f5f5}.header{display:flex;margin-bottom:5px;gap:10px;flex-wrap:wrap}.header button{min-width:150px}.header .save-cancel-container{display:flex;justify-content:flex-start;align-items:center;column-gap:10px;width:calc(50% - 10px)}.header .actions-container{display:flex;justify-content:flex-end;gap:10px;width:calc(50% - 10px)}.unsavedChanges{background-color:#ffe48d}@media (max-width: 800px){.header{margin-bottom:10px;gap:15px}.header button{min-width:0px;width:50%}.header .save-cancel-container,.header .actions-container{width:100%;gap:15px}}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i5.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", inputs: ["disabled", "disableRipple", "color"], exportAs: ["matButton"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "component", type: i6$2.MatTab, selector: "mat-tab", inputs: ["disabled"], exportAs: ["matTab"] }, { kind: "component", type: i6$2.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "disableRipple", "fitInkBarToContent", "mat-stretch-tabs"], exportAs: ["matTabGroup"] }, { kind: "ngmodule", type: NgxMatEntityInputModule }, { kind: "component", type: NgxMatEntityInputComponent, selector: "ngx-mat-entity-input", inputs: ["entity", "propertyKey", "getValidationErrorMessage", "hideOmitForCreate", "hideOmitForEdit", "validEmpty", "isReadOnly"], outputs: ["inputChangeEvent"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i15.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i14.MatMenu, selector: "mat-menu", exportAs: ["matMenu"] }, { kind: "component", type: i14.MatMenuItem, selector: "[mat-menu-item]", inputs: ["disabled", "disableRipple", "role"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i14.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatBadgeModule }, { kind: "directive", type: i10.MatBadge, selector: "[matBadge]", inputs: ["matBadgeDisabled", "matBadgeColor", "matBadgeOverlap", "matBadgePosition", "matBadge", "matBadgeDescription", "matBadgeSize", "matBadgeHidden"] }, { kind: "component", type: TooltipComponent, selector: "ngx-mat-entity-tooltip", inputs: ["tooltipContent"] }] });
5056
5553
  }
5057
5554
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.0", ngImport: i0, type: NgxMatEntityEditPageComponent, decorators: [{
5058
5555
  type: Component,
@@ -5063,8 +5560,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.0", ngImpor
5063
5560
  MatTabsModule,
5064
5561
  NgxMatEntityInputModule,
5065
5562
  MatProgressSpinnerModule,
5066
- MatMenuModule
5067
- ], template: "<div *ngIf=\"!entityTabs && data.displayLoadingSpinner\" class=\"container\">\n <br>\n <mat-spinner></mat-spinner>\n <br>\n</div>\n\n<div *ngIf=\"entityTabs\" class=\"container\">\n <br>\n\n <!------------>\n <!-- Header -->\n <!------------>\n <div class=\"header\">\n <div class=\"save-cancel-container\">\n <button type=\"button\" [class.unsavedChanges]=\"hasUnsavedChanges\" mat-raised-button (click)=\"navigateBack()\" class=\"back-button\" tabindex=\"-1\">\n <i class=\"fas fa-chevron-left\"></i>\n {{data.editData.cancelButtonLabel}}\n <i class=\"fas fa-warning\" *ngIf=\"hasUnsavedChanges\"></i>\n </button>\n <button type=\"button\" class=\"save-button\" (click)=\"edit()\" mat-raised-button [disabled]=\"isEntityReadOnly || !isEntityValid || !isEntityDirty\">\n {{data.editData.confirmButtonLabel}}\n </button>\n </div>\n <div class=\"actions-container\">\n <button *ngIf=\"data.editData.actions.length\" type=\"button\" [matMenuTriggerFor]=\"menu\" mat-raised-button>\n {{data.editData.actionsLabel}}\n </button>\n <mat-menu #menu=\"matMenu\">\n <button type=\"button\" *ngFor=\"let action of data.editData.actions\" [disabled]=\"editActionDisabled(action)\" (click)=\"runEditAction(action)\" mat-menu-item>\n {{action.displayName}}\n </button>\n </mat-menu>\n \n <button type=\"button\" *ngIf=\"allowDelete\" mat-raised-button (click)=\"delete()\" color=\"warn\" class=\"delete-button\" tabindex=\"-1\">\n {{data.editData.deleteButtonLabel}}\n </button>\n </div>\n </div>\n\n <h1>{{data.editData.title(entityPriorChanges)}}</h1>\n\n <!----------->\n <!-- Input -->\n <!----------->\n <form>\n <mat-tab-group *ngIf=\"entityTabs.length > 1\" preserveContent>\n <mat-tab *ngFor=\"let tab of entityTabs\" [label]=\"tab.tabName\">\n <div class=\"row\" *ngFor=\"let row of tab.rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"entity\"\n [propertyKey]=\"key\"\n [hideOmitForEdit]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(entity, key, 'lg')}} col-md-{{EntityUtilities.getWidth(entity, key, 'md')}} col-sm-{{EntityUtilities.getWidth(entity, key, 'sm')}}\"\n (inputChangeEvent)=\"checkEntity()\"\n [isReadOnly]=\"isReadOnly(key)\"\n >\n </ngx-mat-entity-input>\n </div>\n </mat-tab>\n </mat-tab-group>\n \n <div *ngIf=\"entityTabs.length <= 1\">\n <span class=\"no-entity-tabs\" *ngIf=\"!entityTabs.length\">\n ERROR: No Inputs. Did you correctly assign all values in the model constructor?\n </span>\n <div class=\"row\" *ngFor=\"let row of entityTabs[0]?.rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"entity\"\n [propertyKey]=\"key\"\n [hideOmitForEdit]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(entity, key, 'lg')}} col-md-{{EntityUtilities.getWidth(entity, key, 'md')}} col-sm-{{EntityUtilities.getWidth(entity, key, 'sm')}}\"\n (inputChangeEvent)=\"checkEntity()\"\n [isReadOnly]=\"isReadOnly(key)\"\n >\n </ngx-mat-entity-input>\n </div>\n </div>\n\n <button type=\"submit\" (click)=\"edit()\" mat-raised-button [disabled]=\"isEntityReadOnly || !isEntityValid || !isEntityDirty\">\n {{data.editData.confirmButtonLabel}}\n </button>\n </form>\n\n <br>\n</div>", styles: ["h1{text-align:center}mat-spinner{margin:10px auto}.fa-warning{color:orange}.no-entity-tabs{padding:10px;background-color:red;color:#f5f5f5}.header{display:flex;margin-bottom:5px;gap:10px;flex-wrap:wrap}.header button{min-width:150px}.header .save-cancel-container{display:flex;justify-content:flex-start;column-gap:10px;width:calc(50% - 10px)}.header .actions-container{display:flex;justify-content:flex-end;gap:10px;width:calc(50% - 10px)}.unsavedChanges{background-color:#ffe48d}@media (max-width: 800px){.header{margin-bottom:10px;gap:15px}.header button{min-width:0px;width:50%}.header .save-cancel-container,.header .actions-container{width:100%;gap:15px}}\n"] }]
5563
+ MatMenuModule,
5564
+ MatBadgeModule,
5565
+ TooltipComponent
5566
+ ], template: "<div *ngIf=\"!entityTabs && data.displayLoadingSpinner\" class=\"container\">\n <br>\n <mat-spinner></mat-spinner>\n <br>\n</div>\n\n<div *ngIf=\"entityTabs\" class=\"container\">\n <br>\n\n <!------------>\n <!-- Header -->\n <!------------>\n <div class=\"header\">\n <div class=\"save-cancel-container\">\n <button type=\"button\" [class.unsavedChanges]=\"hasUnsavedChanges\" mat-raised-button (click)=\"navigateBack()\" class=\"back-button\" tabindex=\"-1\">\n <i class=\"fas fa-chevron-left\"></i>\n {{data.editData.cancelButtonLabel}}\n <i class=\"fas fa-warning\" *ngIf=\"hasUnsavedChanges\"></i>\n </button>\n </div>\n <div class=\"actions-container\">\n <button *ngIf=\"data.editData.actions.length\" type=\"button\" [matMenuTriggerFor]=\"menu\" mat-raised-button>\n {{data.editData.actionsLabel}}\n </button>\n <mat-menu #menu=\"matMenu\">\n <button type=\"button\" *ngFor=\"let action of data.editData.actions\" [disabled]=\"editActionDisabled(action)\" (click)=\"runEditAction(action)\" mat-menu-item>\n {{action.displayName}}\n </button>\n </mat-menu>\n \n <button type=\"button\" *ngIf=\"allowDelete\" mat-raised-button (click)=\"delete()\" color=\"warn\" class=\"delete-button\" tabindex=\"-1\">\n {{data.editData.deleteButtonLabel}}\n </button>\n </div>\n </div>\n\n <h1>{{data.editData.title(entityPriorChanges)}}</h1>\n\n <!----------->\n <!-- Input -->\n <!----------->\n <form>\n <mat-tab-group *ngIf=\"entityTabs.length > 1\" preserveContent>\n <mat-tab *ngFor=\"let tab of entityTabs\" [label]=\"tab.tabName\">\n <div class=\"row\" *ngFor=\"let row of tab.rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"entity\"\n [propertyKey]=\"key\"\n [hideOmitForEdit]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(entity, key, 'lg')}} col-md-{{EntityUtilities.getWidth(entity, key, 'md')}} col-sm-{{EntityUtilities.getWidth(entity, key, 'sm')}}\"\n (inputChangeEvent)=\"checkEntity()\"\n [isReadOnly]=\"isReadOnly(key)\"\n >\n </ngx-mat-entity-input>\n </div>\n </mat-tab>\n </mat-tab-group>\n \n <div *ngIf=\"entityTabs.length <= 1\">\n <span class=\"no-entity-tabs\" *ngIf=\"!entityTabs.length\">\n ERROR: No Inputs. Did you correctly assign all values in the model constructor?\n </span>\n <div class=\"row\" *ngFor=\"let row of entityTabs[0]?.rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"entity\"\n [propertyKey]=\"key\"\n [hideOmitForEdit]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(entity, key, 'lg')}} col-md-{{EntityUtilities.getWidth(entity, key, 'md')}} col-sm-{{EntityUtilities.getWidth(entity, key, 'sm')}}\"\n (inputChangeEvent)=\"checkEntity()\"\n [isReadOnly]=\"isReadOnly(key)\"\n >\n </ngx-mat-entity-input>\n </div>\n </div>\n\n <div class=\"bottom-row-container\">\n <div class=\"bottom-row container\" style=\"margin-top: 10px;\">\n <button type=\"submit\" (click)=\"edit()\" mat-raised-button\n [disabled]=\"isEntityReadOnly || !isEntityValid || !isEntityDirty\"\n [matBadge]=\"validationErrors.length\"\n [matBadgeHidden]=\"!validationErrors.length\"\n matBadgeColor=\"warn\"\n >\n {{data.editData.confirmButtonLabel}}\n </button>\n <ngx-mat-entity-tooltip *ngIf=\"validationErrors.length\" [tooltipContent]=\"tooltipContent\"></ngx-mat-entity-tooltip>\n </div>\n </div>\n </form>\n\n <br>\n</div>", styles: ["h1{text-align:center}mat-spinner{margin:10px auto}.fa-warning{color:orange}.bottom-row{display:flex;align-items:center;column-gap:10px}.fixed{position:fixed;bottom:0;left:0;right:0;width:100%;z-index:1000;padding:8px 20px;background-color:#fff}.no-entity-tabs{padding:10px;background-color:red;color:#f5f5f5}.header{display:flex;margin-bottom:5px;gap:10px;flex-wrap:wrap}.header button{min-width:150px}.header .save-cancel-container{display:flex;justify-content:flex-start;align-items:center;column-gap:10px;width:calc(50% - 10px)}.header .actions-container{display:flex;justify-content:flex-end;gap:10px;width:calc(50% - 10px)}.unsavedChanges{background-color:#ffe48d}@media (max-width: 800px){.header{margin-bottom:10px;gap:15px}.header button{min-width:0px;width:50%}.header .save-cancel-container,.header .actions-container{width:100%;gap:15px}}\n"] }]
5068
5567
  }], ctorParameters: function () { return [{ type: i1.MatDialog }, { type: i1$1.Location }, { type: i2$3.ActivatedRoute }, { type: i0.EnvironmentInjector }, { type: EntityService, decorators: [{
5069
5568
  type: Inject,
5070
5569
  args: [NGX_EDIT_DATA_ENTITY_SERVICE]
@@ -5074,7 +5573,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.0", ngImpor
5074
5573
  }] }, { type: undefined, decorators: [{
5075
5574
  type: Inject,
5076
5575
  args: [NGX_EDIT_DATA]
5077
- }] }, { type: i2.HttpClient }]; }, propDecorators: { canDeactivate: [{
5576
+ }] }, { type: i2.HttpClient }, { type: i0.ElementRef }, { type: i0.Renderer2 }]; }, propDecorators: { onScroll: [{
5577
+ type: HostListener,
5578
+ args: ['window:scroll']
5579
+ }], canDeactivate: [{
5078
5580
  type: HostListener,
5079
5581
  args: ['window:beforeunload']
5080
5582
  }] } });
@@ -5133,6 +5635,8 @@ class NgxMatEntityCreateDialogComponent {
5133
5635
  entityService;
5134
5636
  data;
5135
5637
  isEntityValid = false;
5638
+ validationErrors = [];
5639
+ tooltipContent = '';
5136
5640
  constructor(inputData, dialogRef, injector, dialog) {
5137
5641
  this.inputData = inputData;
5138
5642
  this.dialogRef = dialogRef;
@@ -5144,12 +5648,15 @@ class NgxMatEntityCreateDialogComponent {
5144
5648
  this.dialogRef.disableClose = true;
5145
5649
  this.entityTabs = EntityUtilities.getEntityTabs(this.data.entity, true);
5146
5650
  this.entityService = this.injector.get(this.data.EntityServiceClass);
5651
+ setTimeout(() => this.checkIsEntityValid(), 1);
5147
5652
  }
5148
5653
  /**
5149
5654
  * Checks if the entity is valid.
5150
5655
  */
5151
5656
  checkIsEntityValid() {
5152
- this.isEntityValid = EntityUtilities.isEntityValid(this.data.entity, 'create');
5657
+ this.validationErrors = ValidationUtilities.getEntityValidationErrors(this.data.entity, 'create');
5658
+ this.tooltipContent = runInInjectionContext(this.injector, () => getValidationErrorsTooltipContent(this.validationErrors));
5659
+ this.isEntityValid = this.validationErrors.length === 0;
5153
5660
  }
5154
5661
  /**
5155
5662
  * Tries add the new entity and close the dialog afterwards.
@@ -5189,7 +5696,7 @@ class NgxMatEntityCreateDialogComponent {
5189
5696
  this.dialogRef.close();
5190
5697
  }
5191
5698
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.0", ngImport: i0, type: NgxMatEntityCreateDialogComponent, deps: [{ token: MAT_DIALOG_DATA }, { token: i1.MatDialogRef }, { token: i0.Injector }, { token: i1.MatDialog }], target: i0.ɵɵFactoryTarget.Component });
5192
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.0", type: NgxMatEntityCreateDialogComponent, isStandalone: true, selector: "ngx-mat-entity-create-dialog", ngImport: i0, template: "<div class=\"mat-dialog-title\">\n <div>{{data.createDialogData.title}}</div>\n</div>\n\n<form>\n <mat-dialog-content>\n <mat-tab-group *ngIf=\"entityTabs.length > 1\" preserveContent>\n <mat-tab *ngFor=\"let tab of entityTabs\" [label]=\"tab.tabName\">\n <div class=\"row\" *ngFor=\"let row of tab.rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"data.entity\"\n [propertyKey]=\"key\"\n [hideOmitForCreate]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(data.entity, key, 'lg')}} col-md-{{EntityUtilities.getWidth(data.entity, key, 'md')}} col-sm-{{EntityUtilities.getWidth(data.entity, key, 'sm')}}\"\n (inputChangeEvent)=\"checkIsEntityValid()\"\n >\n </ngx-mat-entity-input>\n </div>\n </mat-tab>\n </mat-tab-group>\n \n <div *ngIf=\"entityTabs.length <= 1\">\n <span class=\"no-entity-tabs\" *ngIf=\"!entityTabs.length\">\n ERROR: No Inputs. Did you correctly assign all values in the model constructor?\n </span>\n <div class=\"row\" *ngFor=\"let row of entityTabs[0]?.rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"data.entity\"\n [propertyKey]=\"key\"\n [hideOmitForCreate]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(data.entity, key, 'lg')}} col-md-{{EntityUtilities.getWidth(data.entity, key, 'md')}} col-sm-{{EntityUtilities.getWidth(data.entity, key, 'sm')}}\"\n (inputChangeEvent)=\"checkIsEntityValid()\"\n >\n </ngx-mat-entity-input>\n </div>\n </div>\n </mat-dialog-content>\n \n <mat-dialog-actions>\n <button type=\"submit\" (click)=\"create()\" mat-raised-button [disabled]=\"!isEntityValid\">\n {{data.createDialogData.createButtonLabel}}\n </button>\n <button type=\"button\" mat-raised-button (click)=\"cancel()\" class=\"cancel-button\">\n {{data.createDialogData.cancelButtonLabel}}\n </button>\n </mat-dialog-actions>\n</form>\n", styles: [".mat-dialog-title{padding:20px;display:flex;justify-content:space-between;align-items:center}.mat-dialog-title div{font-size:var(--mdc-dialog-subhead-size, 14px);font-weight:var(--mdc-dialog-subhead-weight, 500)}.no-entity-tabs{padding:10px;background-color:red;color:#f5f5f5}::ng-deep .mdc-dialog .mdc-dialog__content{padding:6px 20px!important}mat-dialog-actions{justify-content:space-between;align-items:center;padding-left:20px;padding-right:20px}\n"], dependencies: [{ kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: NgxMatEntityInputModule }, { kind: "component", type: NgxMatEntityInputComponent, selector: "ngx-mat-entity-input", inputs: ["entity", "propertyKey", "getValidationErrorMessage", "hideOmitForCreate", "hideOmitForEdit", "validEmpty", "isReadOnly"], outputs: ["inputChangeEvent"] }, { kind: "ngmodule", type: MatDialogModule }, { kind: "directive", type: i1.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "directive", type: i1.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i3.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i5.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", inputs: ["disabled", "disableRipple", "color"], exportAs: ["matButton"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "component", type: i6$2.MatTab, selector: "mat-tab", inputs: ["disabled"], exportAs: ["matTab"] }, { kind: "component", type: i6$2.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "disableRipple", "fitInkBarToContent", "mat-stretch-tabs"], exportAs: ["matTabGroup"] }] });
5699
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.0", type: NgxMatEntityCreateDialogComponent, isStandalone: true, selector: "ngx-mat-entity-create-dialog", ngImport: i0, template: "<div class=\"mat-dialog-title\">\n <div>{{data.createDialogData.title}}</div>\n</div>\n\n<form>\n <mat-dialog-content>\n <mat-tab-group *ngIf=\"entityTabs.length > 1\" preserveContent>\n <mat-tab *ngFor=\"let tab of entityTabs\" [label]=\"tab.tabName\">\n <div class=\"row\" *ngFor=\"let row of tab.rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"data.entity\"\n [propertyKey]=\"key\"\n [hideOmitForCreate]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(data.entity, key, 'lg')}} col-md-{{EntityUtilities.getWidth(data.entity, key, 'md')}} col-sm-{{EntityUtilities.getWidth(data.entity, key, 'sm')}}\"\n (inputChangeEvent)=\"checkIsEntityValid()\"\n >\n </ngx-mat-entity-input>\n </div>\n </mat-tab>\n </mat-tab-group>\n \n <div *ngIf=\"entityTabs.length <= 1\">\n <span class=\"no-entity-tabs\" *ngIf=\"!entityTabs.length\">\n ERROR: No Inputs. Did you correctly assign all values in the model constructor?\n </span>\n <div class=\"row\" *ngFor=\"let row of entityTabs[0]?.rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"data.entity\"\n [propertyKey]=\"key\"\n [hideOmitForCreate]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(data.entity, key, 'lg')}} col-md-{{EntityUtilities.getWidth(data.entity, key, 'md')}} col-sm-{{EntityUtilities.getWidth(data.entity, key, 'sm')}}\"\n (inputChangeEvent)=\"checkIsEntityValid()\"\n >\n </ngx-mat-entity-input>\n </div>\n </div>\n </mat-dialog-content>\n \n <mat-dialog-actions>\n\n <div class=\"d-flex align-items-center gap-3\" style=\"margin-top: 10px;\">\n <button type=\"submit\" (click)=\"create()\" mat-raised-button\n [disabled]=\"!isEntityValid\"\n [matBadge]=\"validationErrors.length\"\n [matBadgeHidden]=\"!validationErrors.length\"\n matBadgeColor=\"warn\"\n >\n {{data.createDialogData.createButtonLabel}}\n </button>\n <ngx-mat-entity-tooltip *ngIf=\"validationErrors.length\" [tooltipContent]=\"tooltipContent\"></ngx-mat-entity-tooltip>\n </div>\n\n <button type=\"button\" mat-raised-button (click)=\"cancel()\" class=\"cancel-button\">\n {{data.createDialogData.cancelButtonLabel}}\n </button>\n </mat-dialog-actions>\n</form>\n", styles: [".mat-dialog-title{padding:20px;display:flex;justify-content:space-between;align-items:center}.mat-dialog-title div{font-size:var(--mdc-dialog-subhead-size, 14px);font-weight:var(--mdc-dialog-subhead-weight, 500)}.no-entity-tabs{padding:10px;background-color:red;color:#f5f5f5}::ng-deep .mdc-dialog .mdc-dialog__content{padding:6px 20px!important}mat-dialog-actions{justify-content:space-between;align-items:center;padding-left:20px;padding-right:20px}\n"], dependencies: [{ kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: NgxMatEntityInputModule }, { kind: "component", type: NgxMatEntityInputComponent, selector: "ngx-mat-entity-input", inputs: ["entity", "propertyKey", "getValidationErrorMessage", "hideOmitForCreate", "hideOmitForEdit", "validEmpty", "isReadOnly"], outputs: ["inputChangeEvent"] }, { kind: "ngmodule", type: MatDialogModule }, { kind: "directive", type: i1.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "directive", type: i1.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i3.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i5.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", inputs: ["disabled", "disableRipple", "color"], exportAs: ["matButton"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "component", type: i6$2.MatTab, selector: "mat-tab", inputs: ["disabled"], exportAs: ["matTab"] }, { kind: "component", type: i6$2.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "disableRipple", "fitInkBarToContent", "mat-stretch-tabs"], exportAs: ["matTabGroup"] }, { kind: "ngmodule", type: MatBadgeModule }, { kind: "directive", type: i10.MatBadge, selector: "[matBadge]", inputs: ["matBadgeDisabled", "matBadgeColor", "matBadgeOverlap", "matBadgePosition", "matBadge", "matBadgeDescription", "matBadgeSize", "matBadgeHidden"] }, { kind: "component", type: TooltipComponent, selector: "ngx-mat-entity-tooltip", inputs: ["tooltipContent"] }] });
5193
5700
  }
5194
5701
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.0", ngImport: i0, type: NgxMatEntityCreateDialogComponent, decorators: [{
5195
5702
  type: Component,
@@ -5200,8 +5707,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.0", ngImpor
5200
5707
  MatDialogModule,
5201
5708
  FormsModule,
5202
5709
  MatButtonModule,
5203
- MatTabsModule
5204
- ], template: "<div class=\"mat-dialog-title\">\n <div>{{data.createDialogData.title}}</div>\n</div>\n\n<form>\n <mat-dialog-content>\n <mat-tab-group *ngIf=\"entityTabs.length > 1\" preserveContent>\n <mat-tab *ngFor=\"let tab of entityTabs\" [label]=\"tab.tabName\">\n <div class=\"row\" *ngFor=\"let row of tab.rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"data.entity\"\n [propertyKey]=\"key\"\n [hideOmitForCreate]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(data.entity, key, 'lg')}} col-md-{{EntityUtilities.getWidth(data.entity, key, 'md')}} col-sm-{{EntityUtilities.getWidth(data.entity, key, 'sm')}}\"\n (inputChangeEvent)=\"checkIsEntityValid()\"\n >\n </ngx-mat-entity-input>\n </div>\n </mat-tab>\n </mat-tab-group>\n \n <div *ngIf=\"entityTabs.length <= 1\">\n <span class=\"no-entity-tabs\" *ngIf=\"!entityTabs.length\">\n ERROR: No Inputs. Did you correctly assign all values in the model constructor?\n </span>\n <div class=\"row\" *ngFor=\"let row of entityTabs[0]?.rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"data.entity\"\n [propertyKey]=\"key\"\n [hideOmitForCreate]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(data.entity, key, 'lg')}} col-md-{{EntityUtilities.getWidth(data.entity, key, 'md')}} col-sm-{{EntityUtilities.getWidth(data.entity, key, 'sm')}}\"\n (inputChangeEvent)=\"checkIsEntityValid()\"\n >\n </ngx-mat-entity-input>\n </div>\n </div>\n </mat-dialog-content>\n \n <mat-dialog-actions>\n <button type=\"submit\" (click)=\"create()\" mat-raised-button [disabled]=\"!isEntityValid\">\n {{data.createDialogData.createButtonLabel}}\n </button>\n <button type=\"button\" mat-raised-button (click)=\"cancel()\" class=\"cancel-button\">\n {{data.createDialogData.cancelButtonLabel}}\n </button>\n </mat-dialog-actions>\n</form>\n", styles: [".mat-dialog-title{padding:20px;display:flex;justify-content:space-between;align-items:center}.mat-dialog-title div{font-size:var(--mdc-dialog-subhead-size, 14px);font-weight:var(--mdc-dialog-subhead-weight, 500)}.no-entity-tabs{padding:10px;background-color:red;color:#f5f5f5}::ng-deep .mdc-dialog .mdc-dialog__content{padding:6px 20px!important}mat-dialog-actions{justify-content:space-between;align-items:center;padding-left:20px;padding-right:20px}\n"] }]
5710
+ MatTabsModule,
5711
+ MatBadgeModule,
5712
+ TooltipComponent
5713
+ ], template: "<div class=\"mat-dialog-title\">\n <div>{{data.createDialogData.title}}</div>\n</div>\n\n<form>\n <mat-dialog-content>\n <mat-tab-group *ngIf=\"entityTabs.length > 1\" preserveContent>\n <mat-tab *ngFor=\"let tab of entityTabs\" [label]=\"tab.tabName\">\n <div class=\"row\" *ngFor=\"let row of tab.rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"data.entity\"\n [propertyKey]=\"key\"\n [hideOmitForCreate]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(data.entity, key, 'lg')}} col-md-{{EntityUtilities.getWidth(data.entity, key, 'md')}} col-sm-{{EntityUtilities.getWidth(data.entity, key, 'sm')}}\"\n (inputChangeEvent)=\"checkIsEntityValid()\"\n >\n </ngx-mat-entity-input>\n </div>\n </mat-tab>\n </mat-tab-group>\n \n <div *ngIf=\"entityTabs.length <= 1\">\n <span class=\"no-entity-tabs\" *ngIf=\"!entityTabs.length\">\n ERROR: No Inputs. Did you correctly assign all values in the model constructor?\n </span>\n <div class=\"row\" *ngFor=\"let row of entityTabs[0]?.rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"data.entity\"\n [propertyKey]=\"key\"\n [hideOmitForCreate]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(data.entity, key, 'lg')}} col-md-{{EntityUtilities.getWidth(data.entity, key, 'md')}} col-sm-{{EntityUtilities.getWidth(data.entity, key, 'sm')}}\"\n (inputChangeEvent)=\"checkIsEntityValid()\"\n >\n </ngx-mat-entity-input>\n </div>\n </div>\n </mat-dialog-content>\n \n <mat-dialog-actions>\n\n <div class=\"d-flex align-items-center gap-3\" style=\"margin-top: 10px;\">\n <button type=\"submit\" (click)=\"create()\" mat-raised-button\n [disabled]=\"!isEntityValid\"\n [matBadge]=\"validationErrors.length\"\n [matBadgeHidden]=\"!validationErrors.length\"\n matBadgeColor=\"warn\"\n >\n {{data.createDialogData.createButtonLabel}}\n </button>\n <ngx-mat-entity-tooltip *ngIf=\"validationErrors.length\" [tooltipContent]=\"tooltipContent\"></ngx-mat-entity-tooltip>\n </div>\n\n <button type=\"button\" mat-raised-button (click)=\"cancel()\" class=\"cancel-button\">\n {{data.createDialogData.cancelButtonLabel}}\n </button>\n </mat-dialog-actions>\n</form>\n", styles: [".mat-dialog-title{padding:20px;display:flex;justify-content:space-between;align-items:center}.mat-dialog-title div{font-size:var(--mdc-dialog-subhead-size, 14px);font-weight:var(--mdc-dialog-subhead-weight, 500)}.no-entity-tabs{padding:10px;background-color:red;color:#f5f5f5}::ng-deep .mdc-dialog .mdc-dialog__content{padding:6px 20px!important}mat-dialog-actions{justify-content:space-between;align-items:center;padding-left:20px;padding-right:20px}\n"] }]
5205
5714
  }], ctorParameters: function () { return [{ type: undefined, decorators: [{
5206
5715
  type: Inject,
5207
5716
  args: [MAT_DIALOG_DATA]
@@ -5386,6 +5895,8 @@ class NgxMatEntityEditDialogComponent {
5386
5895
  data;
5387
5896
  isEntityValid = true;
5388
5897
  isEntityDirty = false;
5898
+ validationErrors = [];
5899
+ tooltipContent = '';
5389
5900
  isEntityReadOnly;
5390
5901
  allowDelete;
5391
5902
  constructor(inputData, dialogRef, injector, dialog, http) {
@@ -5398,13 +5909,14 @@ class NgxMatEntityEditDialogComponent {
5398
5909
  ngOnInit() {
5399
5910
  this.data = new EditEntityDataBuilder(this.inputData).getResult();
5400
5911
  this.entityPriorChanges = LodashUtilities.cloneDeep(this.data.entity);
5401
- this.injector.runInContext(() => {
5912
+ runInInjectionContext(this.injector, () => {
5402
5913
  this.isEntityReadOnly = !this.data.allowUpdate(this.entityPriorChanges);
5403
5914
  this.allowDelete = this.data.allowDelete(this.entityPriorChanges);
5404
5915
  });
5405
5916
  this.dialogRef.disableClose = true;
5406
5917
  this.entityTabs = EntityUtilities.getEntityTabs(this.data.entity, false, true);
5407
5918
  this.entityService = this.injector.get(this.data.EntityServiceClass);
5919
+ setTimeout(() => this.checkIsEntityValid(), 1);
5408
5920
  }
5409
5921
  /**
5410
5922
  * Checks if the input with the given key is readonly.
@@ -5413,7 +5925,7 @@ class NgxMatEntityEditDialogComponent {
5413
5925
  * @returns Whether or not the input for the key is read only.
5414
5926
  */
5415
5927
  isReadOnly(key) {
5416
- return this.injector.runInContext(() => {
5928
+ return runInInjectionContext(this.injector, () => {
5417
5929
  const metadata = EntityUtilities.getPropertyMetadata(this.data.entity, key);
5418
5930
  return this.isEntityReadOnly || metadata.isReadOnly(this.data.entity);
5419
5931
  });
@@ -5422,9 +5934,14 @@ class NgxMatEntityEditDialogComponent {
5422
5934
  * Checks if the entity has become invalid or dirty.
5423
5935
  */
5424
5936
  async checkEntity() {
5425
- this.isEntityValid = EntityUtilities.isEntityValid(this.data.entity, 'update');
5937
+ this.checkIsEntityValid();
5426
5938
  this.isEntityDirty = await EntityUtilities.isDirty(this.data.entity, this.entityPriorChanges, this.http);
5427
5939
  }
5940
+ checkIsEntityValid() {
5941
+ this.validationErrors = ValidationUtilities.getEntityValidationErrors(this.data.entity, 'update');
5942
+ this.tooltipContent = runInInjectionContext(this.injector, () => getValidationErrorsTooltipContent(this.validationErrors));
5943
+ this.isEntityValid = this.validationErrors.length === 0;
5944
+ }
5428
5945
  /**
5429
5946
  * Tries to save the changes and close the dialog afterwards.
5430
5947
  * Also handles the confirmation if required.
@@ -5498,7 +6015,7 @@ class NgxMatEntityEditDialogComponent {
5498
6015
  * @param action - The action to run.
5499
6016
  */
5500
6017
  runEditAction(action) {
5501
- const requireConfirmDialog = this.injector.runInContext(() => {
6018
+ const requireConfirmDialog = runInInjectionContext(this.injector, () => {
5502
6019
  return action.requireConfirmDialog(this.entityPriorChanges);
5503
6020
  });
5504
6021
  if (!requireConfirmDialog) {
@@ -5517,7 +6034,7 @@ class NgxMatEntityEditDialogComponent {
5517
6034
  });
5518
6035
  }
5519
6036
  confirmRunEditAction(action) {
5520
- void this.injector.runInContext(async () => {
6037
+ void runInInjectionContext(this.injector, async () => {
5521
6038
  await action.action(this.data.entity, this.entityPriorChanges);
5522
6039
  await this.checkEntity();
5523
6040
  });
@@ -5529,12 +6046,10 @@ class NgxMatEntityEditDialogComponent {
5529
6046
  * @returns Whether or not the Action can be used.
5530
6047
  */
5531
6048
  editActionDisabled(action) {
5532
- return this.injector.runInContext(() => {
5533
- return !action.enabled(this.entityPriorChanges);
5534
- });
6049
+ return runInInjectionContext(this.injector, () => !action.enabled(this.entityPriorChanges));
5535
6050
  }
5536
6051
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.0", ngImport: i0, type: NgxMatEntityEditDialogComponent, deps: [{ token: MAT_DIALOG_DATA }, { token: i1.MatDialogRef }, { token: i0.EnvironmentInjector }, { token: i1.MatDialog }, { token: i2.HttpClient }], target: i0.ɵɵFactoryTarget.Component });
5537
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.0", type: NgxMatEntityEditDialogComponent, isStandalone: true, selector: "ngx-mat-entity-edit-dialog", ngImport: i0, template: "<div class=\"mat-dialog-title\">\n <div>{{data.editData.title(entityPriorChanges)}}</div>\n\n <div class=\"actions-container\">\n <button *ngIf=\"data.editData.actions.length\" type=\"button\" [matMenuTriggerFor]=\"menu\" mat-raised-button>\n {{data.editData.actionsLabel}}\n </button>\n <mat-menu #menu=\"matMenu\">\n <button type=\"button\" *ngFor=\"let action of data.editData.actions\" [disabled]=\"editActionDisabled(action)\" (click)=\"runEditAction(action)\" mat-menu-item>\n {{action.displayName}}\n </button>\n </mat-menu>\n \n <button type=\"button\" *ngIf=\"allowDelete\" mat-raised-button (click)=\"delete()\" color=\"warn\" class=\"delete-button\" tabindex=\"-1\">\n {{data.editData.deleteButtonLabel}}\n </button>\n </div>\n</div>\n\n<form>\n <mat-dialog-content>\n <mat-tab-group *ngIf=\"entityTabs.length > 1\" preserveContent>\n <mat-tab *ngFor=\"let tab of entityTabs\" [label]=\"tab.tabName\">\n <div class=\"row\" *ngFor=\"let row of tab.rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"data.entity\"\n [propertyKey]=\"key\"\n [hideOmitForEdit]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(data.entity, key, 'lg')}} col-md-{{EntityUtilities.getWidth(data.entity, key, 'md')}} col-sm-{{EntityUtilities.getWidth(data.entity, key, 'sm')}}\"\n (inputChangeEvent)=\"checkEntity()\"\n [isReadOnly]=\"isReadOnly(key)\"\n >\n </ngx-mat-entity-input>\n </div>\n </mat-tab>\n </mat-tab-group>\n \n <div *ngIf=\"entityTabs.length <= 1\">\n <span class=\"no-entity-tabs\" *ngIf=\"!entityTabs.length\">\n ERROR: No Inputs. Did you correctly assign all values in the model constructor?\n </span>\n <div class=\"row\" *ngFor=\"let row of entityTabs[0].rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"data.entity\"\n [propertyKey]=\"key\"\n [hideOmitForEdit]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(data.entity, key, 'lg')}} col-md-{{EntityUtilities.getWidth(data.entity, key, 'md')}} col-sm-{{EntityUtilities.getWidth(data.entity, key, 'sm')}}\"\n (inputChangeEvent)=\"checkEntity()\"\n [isReadOnly]=\"isReadOnly(key)\"\n >\n </ngx-mat-entity-input>\n </div>\n </div>\n </mat-dialog-content>\n \n <mat-dialog-actions>\n <button type=\"submit\" (click)=\"edit()\" mat-raised-button [disabled]=\"isEntityReadOnly || !isEntityValid || !isEntityDirty\">\n {{data.editData.confirmButtonLabel}}\n </button>\n <button type=\"button\" mat-raised-button (click)=\"cancel()\" class=\"cancel-button\">\n {{data.editData.cancelButtonLabel}}\n </button>\n </mat-dialog-actions>\n</form>\n", styles: [".mat-dialog-title{padding:12px 20px;display:flex;justify-content:space-between;align-items:center}.mat-dialog-title div{font-size:var(--mdc-dialog-subhead-size, 14px);font-weight:var(--mdc-dialog-subhead-weight, 500)}.actions-container{display:flex;gap:10px}.no-entity-tabs{padding:10px;background-color:red;color:#f5f5f5}::ng-deep .mdc-dialog .mdc-dialog__content{padding:6px 20px!important}mat-dialog-actions{justify-content:space-between;align-items:center;padding-left:20px;padding-right:20px}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "ngmodule", type: NgxMatEntityInputModule }, { kind: "component", type: NgxMatEntityInputComponent, selector: "ngx-mat-entity-input", inputs: ["entity", "propertyKey", "getValidationErrorMessage", "hideOmitForCreate", "hideOmitForEdit", "validEmpty", "isReadOnly"], outputs: ["inputChangeEvent"] }, { kind: "ngmodule", type: MatDialogModule }, { kind: "directive", type: i1.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "directive", type: i1.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i3.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i5.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", inputs: ["disabled", "disableRipple", "color"], exportAs: ["matButton"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "component", type: i6$2.MatTab, selector: "mat-tab", inputs: ["disabled"], exportAs: ["matTab"] }, { kind: "component", type: i6$2.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "disableRipple", "fitInkBarToContent", "mat-stretch-tabs"], exportAs: ["matTabGroup"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i9.MatMenu, selector: "mat-menu", exportAs: ["matMenu"] }, { kind: "component", type: i9.MatMenuItem, selector: "[mat-menu-item]", inputs: ["disabled", "disableRipple", "role"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i9.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", exportAs: ["matMenuTrigger"] }] });
6052
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.0", type: NgxMatEntityEditDialogComponent, isStandalone: true, selector: "ngx-mat-entity-edit-dialog", ngImport: i0, template: "<div class=\"mat-dialog-title\">\n <div>{{data.editData.title(entityPriorChanges)}}</div>\n\n <div class=\"actions-container\">\n <button *ngIf=\"data.editData.actions.length\" type=\"button\" [matMenuTriggerFor]=\"menu\" mat-raised-button>\n {{data.editData.actionsLabel}}\n </button>\n <mat-menu #menu=\"matMenu\">\n <button type=\"button\" *ngFor=\"let action of data.editData.actions\" [disabled]=\"editActionDisabled(action)\" (click)=\"runEditAction(action)\" mat-menu-item>\n {{action.displayName}}\n </button>\n </mat-menu>\n \n <button type=\"button\" *ngIf=\"allowDelete\" mat-raised-button (click)=\"delete()\" color=\"warn\" class=\"delete-button\" tabindex=\"-1\">\n {{data.editData.deleteButtonLabel}}\n </button>\n </div>\n</div>\n\n<form>\n <mat-dialog-content>\n <mat-tab-group *ngIf=\"entityTabs.length > 1\" preserveContent>\n <mat-tab *ngFor=\"let tab of entityTabs\" [label]=\"tab.tabName\">\n <div class=\"row\" *ngFor=\"let row of tab.rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"data.entity\"\n [propertyKey]=\"key\"\n [hideOmitForEdit]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(data.entity, key, 'lg')}} col-md-{{EntityUtilities.getWidth(data.entity, key, 'md')}} col-sm-{{EntityUtilities.getWidth(data.entity, key, 'sm')}}\"\n (inputChangeEvent)=\"checkEntity()\"\n [isReadOnly]=\"isReadOnly(key)\"\n >\n </ngx-mat-entity-input>\n </div>\n </mat-tab>\n </mat-tab-group>\n \n <div *ngIf=\"entityTabs.length <= 1\">\n <span class=\"no-entity-tabs\" *ngIf=\"!entityTabs.length\">\n ERROR: No Inputs. Did you correctly assign all values in the model constructor?\n </span>\n <div class=\"row\" *ngFor=\"let row of entityTabs[0].rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"data.entity\"\n [propertyKey]=\"key\"\n [hideOmitForEdit]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(data.entity, key, 'lg')}} col-md-{{EntityUtilities.getWidth(data.entity, key, 'md')}} col-sm-{{EntityUtilities.getWidth(data.entity, key, 'sm')}}\"\n (inputChangeEvent)=\"checkEntity()\"\n [isReadOnly]=\"isReadOnly(key)\"\n >\n </ngx-mat-entity-input>\n </div>\n </div>\n </mat-dialog-content>\n \n <mat-dialog-actions>\n <div class=\"d-flex align-items-center gap-3\" style=\"margin-top: 10px;\">\n <button type=\"submit\" (click)=\"edit()\" mat-raised-button\n [disabled]=\"isEntityReadOnly || !isEntityValid || !isEntityDirty\"\n [matBadge]=\"validationErrors.length\"\n [matBadgeHidden]=\"!validationErrors.length\"\n matBadgeColor=\"warn\"\n >\n {{data.editData.confirmButtonLabel}}\n </button>\n <ngx-mat-entity-tooltip *ngIf=\"validationErrors.length\" [tooltipContent]=\"tooltipContent\"></ngx-mat-entity-tooltip>\n </div>\n\n\n <button type=\"button\" mat-raised-button (click)=\"cancel()\" class=\"cancel-button\">\n {{data.editData.cancelButtonLabel}}\n </button>\n </mat-dialog-actions>\n</form>\n", styles: [".mat-dialog-title{padding:12px 20px;display:flex;justify-content:space-between;align-items:center}.mat-dialog-title div{font-size:var(--mdc-dialog-subhead-size, 14px);font-weight:var(--mdc-dialog-subhead-weight, 500)}.actions-container{display:flex;gap:10px}.no-entity-tabs{padding:10px;background-color:red;color:#f5f5f5}::ng-deep .mdc-dialog .mdc-dialog__content{padding:6px 20px!important}mat-dialog-actions{justify-content:space-between;align-items:center;padding-left:20px;padding-right:20px}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "ngmodule", type: NgxMatEntityInputModule }, { kind: "component", type: NgxMatEntityInputComponent, selector: "ngx-mat-entity-input", inputs: ["entity", "propertyKey", "getValidationErrorMessage", "hideOmitForCreate", "hideOmitForEdit", "validEmpty", "isReadOnly"], outputs: ["inputChangeEvent"] }, { kind: "ngmodule", type: MatDialogModule }, { kind: "directive", type: i1.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "directive", type: i1.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i3.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i5.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", inputs: ["disabled", "disableRipple", "color"], exportAs: ["matButton"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "component", type: i6$2.MatTab, selector: "mat-tab", inputs: ["disabled"], exportAs: ["matTab"] }, { kind: "component", type: i6$2.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "disableRipple", "fitInkBarToContent", "mat-stretch-tabs"], exportAs: ["matTabGroup"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i14.MatMenu, selector: "mat-menu", exportAs: ["matMenu"] }, { kind: "component", type: i14.MatMenuItem, selector: "[mat-menu-item]", inputs: ["disabled", "disableRipple", "role"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i14.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatBadgeModule }, { kind: "directive", type: i10.MatBadge, selector: "[matBadge]", inputs: ["matBadgeDisabled", "matBadgeColor", "matBadgeOverlap", "matBadgePosition", "matBadge", "matBadgeDescription", "matBadgeSize", "matBadgeHidden"] }, { kind: "component", type: TooltipComponent, selector: "ngx-mat-entity-tooltip", inputs: ["tooltipContent"] }] });
5538
6053
  }
5539
6054
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.0", ngImport: i0, type: NgxMatEntityEditDialogComponent, decorators: [{
5540
6055
  type: Component,
@@ -5547,8 +6062,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.0", ngImpor
5547
6062
  MatButtonModule,
5548
6063
  MatTabsModule,
5549
6064
  NgxMatEntityConfirmDialogComponent,
5550
- MatMenuModule
5551
- ], template: "<div class=\"mat-dialog-title\">\n <div>{{data.editData.title(entityPriorChanges)}}</div>\n\n <div class=\"actions-container\">\n <button *ngIf=\"data.editData.actions.length\" type=\"button\" [matMenuTriggerFor]=\"menu\" mat-raised-button>\n {{data.editData.actionsLabel}}\n </button>\n <mat-menu #menu=\"matMenu\">\n <button type=\"button\" *ngFor=\"let action of data.editData.actions\" [disabled]=\"editActionDisabled(action)\" (click)=\"runEditAction(action)\" mat-menu-item>\n {{action.displayName}}\n </button>\n </mat-menu>\n \n <button type=\"button\" *ngIf=\"allowDelete\" mat-raised-button (click)=\"delete()\" color=\"warn\" class=\"delete-button\" tabindex=\"-1\">\n {{data.editData.deleteButtonLabel}}\n </button>\n </div>\n</div>\n\n<form>\n <mat-dialog-content>\n <mat-tab-group *ngIf=\"entityTabs.length > 1\" preserveContent>\n <mat-tab *ngFor=\"let tab of entityTabs\" [label]=\"tab.tabName\">\n <div class=\"row\" *ngFor=\"let row of tab.rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"data.entity\"\n [propertyKey]=\"key\"\n [hideOmitForEdit]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(data.entity, key, 'lg')}} col-md-{{EntityUtilities.getWidth(data.entity, key, 'md')}} col-sm-{{EntityUtilities.getWidth(data.entity, key, 'sm')}}\"\n (inputChangeEvent)=\"checkEntity()\"\n [isReadOnly]=\"isReadOnly(key)\"\n >\n </ngx-mat-entity-input>\n </div>\n </mat-tab>\n </mat-tab-group>\n \n <div *ngIf=\"entityTabs.length <= 1\">\n <span class=\"no-entity-tabs\" *ngIf=\"!entityTabs.length\">\n ERROR: No Inputs. Did you correctly assign all values in the model constructor?\n </span>\n <div class=\"row\" *ngFor=\"let row of entityTabs[0].rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"data.entity\"\n [propertyKey]=\"key\"\n [hideOmitForEdit]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(data.entity, key, 'lg')}} col-md-{{EntityUtilities.getWidth(data.entity, key, 'md')}} col-sm-{{EntityUtilities.getWidth(data.entity, key, 'sm')}}\"\n (inputChangeEvent)=\"checkEntity()\"\n [isReadOnly]=\"isReadOnly(key)\"\n >\n </ngx-mat-entity-input>\n </div>\n </div>\n </mat-dialog-content>\n \n <mat-dialog-actions>\n <button type=\"submit\" (click)=\"edit()\" mat-raised-button [disabled]=\"isEntityReadOnly || !isEntityValid || !isEntityDirty\">\n {{data.editData.confirmButtonLabel}}\n </button>\n <button type=\"button\" mat-raised-button (click)=\"cancel()\" class=\"cancel-button\">\n {{data.editData.cancelButtonLabel}}\n </button>\n </mat-dialog-actions>\n</form>\n", styles: [".mat-dialog-title{padding:12px 20px;display:flex;justify-content:space-between;align-items:center}.mat-dialog-title div{font-size:var(--mdc-dialog-subhead-size, 14px);font-weight:var(--mdc-dialog-subhead-weight, 500)}.actions-container{display:flex;gap:10px}.no-entity-tabs{padding:10px;background-color:red;color:#f5f5f5}::ng-deep .mdc-dialog .mdc-dialog__content{padding:6px 20px!important}mat-dialog-actions{justify-content:space-between;align-items:center;padding-left:20px;padding-right:20px}\n"] }]
6065
+ MatMenuModule,
6066
+ MatBadgeModule,
6067
+ TooltipComponent
6068
+ ], template: "<div class=\"mat-dialog-title\">\n <div>{{data.editData.title(entityPriorChanges)}}</div>\n\n <div class=\"actions-container\">\n <button *ngIf=\"data.editData.actions.length\" type=\"button\" [matMenuTriggerFor]=\"menu\" mat-raised-button>\n {{data.editData.actionsLabel}}\n </button>\n <mat-menu #menu=\"matMenu\">\n <button type=\"button\" *ngFor=\"let action of data.editData.actions\" [disabled]=\"editActionDisabled(action)\" (click)=\"runEditAction(action)\" mat-menu-item>\n {{action.displayName}}\n </button>\n </mat-menu>\n \n <button type=\"button\" *ngIf=\"allowDelete\" mat-raised-button (click)=\"delete()\" color=\"warn\" class=\"delete-button\" tabindex=\"-1\">\n {{data.editData.deleteButtonLabel}}\n </button>\n </div>\n</div>\n\n<form>\n <mat-dialog-content>\n <mat-tab-group *ngIf=\"entityTabs.length > 1\" preserveContent>\n <mat-tab *ngFor=\"let tab of entityTabs\" [label]=\"tab.tabName\">\n <div class=\"row\" *ngFor=\"let row of tab.rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"data.entity\"\n [propertyKey]=\"key\"\n [hideOmitForEdit]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(data.entity, key, 'lg')}} col-md-{{EntityUtilities.getWidth(data.entity, key, 'md')}} col-sm-{{EntityUtilities.getWidth(data.entity, key, 'sm')}}\"\n (inputChangeEvent)=\"checkEntity()\"\n [isReadOnly]=\"isReadOnly(key)\"\n >\n </ngx-mat-entity-input>\n </div>\n </mat-tab>\n </mat-tab-group>\n \n <div *ngIf=\"entityTabs.length <= 1\">\n <span class=\"no-entity-tabs\" *ngIf=\"!entityTabs.length\">\n ERROR: No Inputs. Did you correctly assign all values in the model constructor?\n </span>\n <div class=\"row\" *ngFor=\"let row of entityTabs[0].rows\">\n <ngx-mat-entity-input\n *ngFor=\"let key of row.keys\"\n [entity]=\"data.entity\"\n [propertyKey]=\"key\"\n [hideOmitForEdit]=\"true\"\n class=\"col-lg-{{EntityUtilities.getWidth(data.entity, key, 'lg')}} col-md-{{EntityUtilities.getWidth(data.entity, key, 'md')}} col-sm-{{EntityUtilities.getWidth(data.entity, key, 'sm')}}\"\n (inputChangeEvent)=\"checkEntity()\"\n [isReadOnly]=\"isReadOnly(key)\"\n >\n </ngx-mat-entity-input>\n </div>\n </div>\n </mat-dialog-content>\n \n <mat-dialog-actions>\n <div class=\"d-flex align-items-center gap-3\" style=\"margin-top: 10px;\">\n <button type=\"submit\" (click)=\"edit()\" mat-raised-button\n [disabled]=\"isEntityReadOnly || !isEntityValid || !isEntityDirty\"\n [matBadge]=\"validationErrors.length\"\n [matBadgeHidden]=\"!validationErrors.length\"\n matBadgeColor=\"warn\"\n >\n {{data.editData.confirmButtonLabel}}\n </button>\n <ngx-mat-entity-tooltip *ngIf=\"validationErrors.length\" [tooltipContent]=\"tooltipContent\"></ngx-mat-entity-tooltip>\n </div>\n\n\n <button type=\"button\" mat-raised-button (click)=\"cancel()\" class=\"cancel-button\">\n {{data.editData.cancelButtonLabel}}\n </button>\n </mat-dialog-actions>\n</form>\n", styles: [".mat-dialog-title{padding:12px 20px;display:flex;justify-content:space-between;align-items:center}.mat-dialog-title div{font-size:var(--mdc-dialog-subhead-size, 14px);font-weight:var(--mdc-dialog-subhead-weight, 500)}.actions-container{display:flex;gap:10px}.no-entity-tabs{padding:10px;background-color:red;color:#f5f5f5}::ng-deep .mdc-dialog .mdc-dialog__content{padding:6px 20px!important}mat-dialog-actions{justify-content:space-between;align-items:center;padding-left:20px;padding-right:20px}\n"] }]
5552
6069
  }], ctorParameters: function () { return [{ type: undefined, decorators: [{
5553
6070
  type: Inject,
5554
6071
  args: [MAT_DIALOG_DATA]
@@ -5733,7 +6250,7 @@ class NgxMatEntityTableComponent {
5733
6250
  const dialogData = new EditEntityDataBuilder(inputDialogData).getResult();
5734
6251
  const res = await firstValueFrom(this.dialog.open(NgxMatEntityEditDialogComponent, {
5735
6252
  data: dialogData,
5736
- minWidth: '60%',
6253
+ // minWidth: '60%',
5737
6254
  autoFocus: false,
5738
6255
  restoreFocus: false
5739
6256
  }).afterClosed());
@@ -5755,11 +6272,13 @@ class NgxMatEntityTableComponent {
5755
6272
  if (!this.data.baseData.EntityClass) {
5756
6273
  throw new Error('No "EntityClass" specified for this table');
5757
6274
  }
6275
+ const entity = new this.data.baseData.EntityClass();
6276
+ EntityUtilities.setDefaultValues(entity);
5758
6277
  if (this.data.baseData.create) {
5759
- this.data.baseData.create(new this.data.baseData.EntityClass());
6278
+ this.data.baseData.create(entity);
5760
6279
  }
5761
6280
  else {
5762
- this.createDefault(new this.data.baseData.EntityClass());
6281
+ this.createDefault(entity);
5763
6282
  }
5764
6283
  }
5765
6284
  });
@@ -5832,7 +6351,7 @@ class NgxMatEntityTableComponent {
5832
6351
  this.dataSource.filter = filterValue.trim().toLowerCase();
5833
6352
  }
5834
6353
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.0", ngImport: i0, type: NgxMatEntityTableComponent, deps: [{ token: i1.MatDialog }, { token: i0.EnvironmentInjector }, { token: i2$3.Router }], target: i0.ɵɵFactoryTarget.Component });
5835
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.0", type: NgxMatEntityTableComponent, isStandalone: true, selector: "ngx-mat-entity-table", inputs: { tableData: "tableData" }, viewQueries: [{ propertyName: "paginator", first: true, predicate: MatPaginator, descendants: true, static: true }, { propertyName: "sort", first: true, predicate: MatSort, descendants: true, static: true }, { propertyName: "filter", first: true, predicate: ["filter"], descendants: true, static: true }], ngImport: i0, template: "<h1 class=\"title\">{{data.baseData.title}}</h1>\n\n<div class=\"row\">\n <mat-form-field class=\"col-lg-8 col-md-6 col-sm-12\">\n <mat-label>{{data.baseData.searchLabel}}</mat-label>\n <input matInput (keyup)=\"applyFilter($event)\">\n </mat-form-field>\n <div\n *ngIf=\"data.baseData.tableActions.length\"\n [class.col-lg-2]=\"allowCreate\"\n [class.col-lg-4]=\"!allowCreate\"\n [class.col-md-3]=\"allowCreate\"\n [class.col-md-6]=\"!allowCreate\"\n [class.col-sm-6]=\"allowCreate\"\n [class.col-sm-12]=\"!allowCreate\"\n >\n <button type=\"button\" class=\"actions-button\" [matMenuTriggerFor]=\"menu\" mat-raised-button>\n {{data.baseData.tableActionsLabel}}\n </button>\n </div>\n <mat-menu #menu=\"matMenu\">\n <button *ngIf=\"data.baseData.allowJsonImport\" type=\"button\" [disabled]=\"tableActionDisabled(importAction)\" (click)=\"runTableAction(importAction)\" mat-menu-item>\n {{importAction.displayName}}\n </button>\n <button type=\"button\" *ngFor=\"let action of data.baseData.tableActions\" [disabled]=\"tableActionDisabled(action)\" (click)=\"runTableAction(action)\" mat-menu-item>\n {{action.displayName}}\n </button>\n </mat-menu>\n\n <div\n *ngIf=\"allowCreate\"\n [class.col-lg-2]=\"data.baseData.tableActions.length\"\n [class.col-lg-4]=\"!data.baseData.tableActions.length\"\n [class.col-md-3]=\"data.baseData.tableActions.length\"\n [class.col-md-6]=\"!data.baseData.tableActions.length\"\n [class.col-sm-6]=\"data.baseData.tableActions.length\"\n [class.col-sm-12]=\"!data.baseData.tableActions.length\"\n >\n <button type=\"button\" class=\"create-button\" (click)=\"createEntity()\" mat-raised-button>\n {{data.baseData.createButtonLabel}}\n </button>\n </div>\n</div>\n\n<div class=\"mat-elevation-z8\">\n <mat-table [dataSource]=\"dataSource\" matSort>\n <!-- select Column -->\n <ng-container matColumnDef=\"select\">\n <mat-header-cell *matHeaderCellDef>\n <mat-checkbox (change)=\"$event ? SelectionUtilities.masterToggle(selection, dataSource) : null\"\n [checked]=\"selection.hasValue() && SelectionUtilities.isAllSelected(selection, dataSource)\"\n [indeterminate]=\"selection.hasValue() && !SelectionUtilities.isAllSelected(selection, dataSource)\">\n </mat-checkbox>\n </mat-header-cell>\n <mat-cell class=\"entity\" *matCellDef=\"let entity\">\n <mat-checkbox (click)=\"$event.stopPropagation()\"\n (change)=\"$event ? selection.toggle(entity) : null\"\n [checked]=\"selection.isSelected(entity)\">\n </mat-checkbox>\n </mat-cell>\n </ng-container>\n\n <ng-container *ngFor=\"let dCol of data.baseData.displayColumns\" [matColumnDef]=\"dCol.displayName\">\n <mat-header-cell *matHeaderCellDef mat-sort-header>\n {{dCol.displayName}}\n </mat-header-cell>\n <mat-cell class=\"entity\" [class.enabled]=\"!dCol.disableClick && (allowUpdate(entity) || allowRead(entity))\"\n (click)=\"editEntity(entity, dCol)\"\n *matCellDef=\"let entity\"\n >\n <ng-container *ngIf=\"dCol.Component\">\n <display-column-value [entity]=\"entity\" [ComponentClass]=\"dCol.Component\"></display-column-value>\n </ng-container>\n <ng-container *ngIf=\"!dCol.Component\">\n {{getDisplayColumnValue(entity, dCol)}}\n </ng-container>\n </mat-cell>\n </ng-container>\n\n <mat-header-row *matHeaderRowDef=\"displayedColumns\"></mat-header-row>\n <mat-row *matRowDef=\"let row; columns: displayedColumns\"></mat-row>\n </mat-table>\n\n <mat-spinner *ngIf=\"isLoading && data.baseData.displayLoadingSpinner\">\n </mat-spinner>\n\n <mat-paginator [length]=\"dataSource.filteredData.length\" [pageIndex]=\"0\" [pageSize]=\"10\" [pageSizeOptions]=\"[5, 10, 25, 50]\"></mat-paginator>\n</div>", styles: [".mdc-data-table__row:last-child .mdc-data-table__cell{border-bottom:1px solid rgba(0,0,0,.12)}button{width:100%}mat-spinner{margin:10px auto}.title{text-align:center}.actions-button,.create-button{height:56px;line-height:24px;font-size:16px}.mat-column-select{flex:0 0 75px}.enabled:hover{cursor:pointer}@media (max-width: 767px){.actions-button,.create-button{margin-bottom:15px}}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i2$1.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "component", type: i2$2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$2.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i4.MatCheckbox, selector: "mat-checkbox", inputs: ["disableRipple", "color", "tabIndex"], exportAs: ["matCheckbox"] }, { kind: "ngmodule", type: MatTableModule }, { kind: "component", type: i6.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i6.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i6.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i6.MatColumnDef, selector: "[matColumnDef]", inputs: ["sticky", "matColumnDef"] }, { kind: "directive", type: i6.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i6.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i6.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i6.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i6.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i6.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "ngmodule", type: MatPaginatorModule }, { kind: "component", type: i16.MatPaginator, selector: "mat-paginator", inputs: ["disabled"], exportAs: ["matPaginator"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i5.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", inputs: ["disabled", "disableRipple", "color"], exportAs: ["matButton"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i9.MatMenu, selector: "mat-menu", exportAs: ["matMenu"] }, { kind: "component", type: i9.MatMenuItem, selector: "[mat-menu-item]", inputs: ["disabled", "disableRipple", "role"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i9.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatDialogModule }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i15.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "component", type: DisplayColumnValueComponent, selector: "display-column-value", inputs: ["entity", "ComponentClass"] }] });
6354
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.0", type: NgxMatEntityTableComponent, isStandalone: true, selector: "ngx-mat-entity-table", inputs: { tableData: "tableData" }, viewQueries: [{ propertyName: "paginator", first: true, predicate: MatPaginator, descendants: true, static: true }, { propertyName: "sort", first: true, predicate: MatSort, descendants: true, static: true }, { propertyName: "filter", first: true, predicate: ["filter"], descendants: true, static: true }], ngImport: i0, template: "<h1 class=\"title\">{{data.baseData.title}}</h1>\n\n<div class=\"row\">\n <mat-form-field class=\"col-lg-8 col-md-6 col-sm-12\">\n <mat-label>{{data.baseData.searchLabel}}</mat-label>\n <input matInput (keyup)=\"applyFilter($event)\">\n </mat-form-field>\n <div\n *ngIf=\"data.baseData.tableActions.length\"\n [class.col-lg-2]=\"allowCreate\"\n [class.col-lg-4]=\"!allowCreate\"\n [class.col-md-3]=\"allowCreate\"\n [class.col-md-6]=\"!allowCreate\"\n [class.col-sm-6]=\"allowCreate\"\n [class.col-sm-12]=\"!allowCreate\"\n >\n <button type=\"button\" class=\"actions-button\" [matMenuTriggerFor]=\"menu\" mat-raised-button>\n {{data.baseData.tableActionsLabel}}\n </button>\n </div>\n <mat-menu #menu=\"matMenu\">\n <button *ngIf=\"data.baseData.allowJsonImport\" type=\"button\" [disabled]=\"tableActionDisabled(importAction)\" (click)=\"runTableAction(importAction)\" mat-menu-item>\n {{importAction.displayName}}\n </button>\n <button type=\"button\" *ngFor=\"let action of data.baseData.tableActions\" [disabled]=\"tableActionDisabled(action)\" (click)=\"runTableAction(action)\" mat-menu-item>\n {{action.displayName}}\n </button>\n </mat-menu>\n\n <div\n *ngIf=\"allowCreate\"\n [class.col-lg-2]=\"data.baseData.tableActions.length\"\n [class.col-lg-4]=\"!data.baseData.tableActions.length\"\n [class.col-md-3]=\"data.baseData.tableActions.length\"\n [class.col-md-6]=\"!data.baseData.tableActions.length\"\n [class.col-sm-6]=\"data.baseData.tableActions.length\"\n [class.col-sm-12]=\"!data.baseData.tableActions.length\"\n >\n <button type=\"button\" class=\"create-button\" (click)=\"createEntity()\" mat-raised-button>\n {{data.baseData.createButtonLabel}}\n </button>\n </div>\n</div>\n\n<div class=\"mat-elevation-z8\">\n <mat-table [dataSource]=\"dataSource\" matSort>\n <!-- select Column -->\n <ng-container matColumnDef=\"select\">\n <mat-header-cell *matHeaderCellDef>\n <mat-checkbox (change)=\"$event ? SelectionUtilities.masterToggle(selection, dataSource) : null\"\n [checked]=\"selection.hasValue() && SelectionUtilities.isAllSelected(selection, dataSource)\"\n [indeterminate]=\"selection.hasValue() && !SelectionUtilities.isAllSelected(selection, dataSource)\">\n </mat-checkbox>\n </mat-header-cell>\n <mat-cell class=\"entity\" *matCellDef=\"let entity\">\n <mat-checkbox (click)=\"$event.stopPropagation()\"\n (change)=\"$event ? selection.toggle(entity) : null\"\n [checked]=\"selection.isSelected(entity)\">\n </mat-checkbox>\n </mat-cell>\n </ng-container>\n\n <ng-container *ngFor=\"let dCol of data.baseData.displayColumns\" [matColumnDef]=\"dCol.displayName\">\n <mat-header-cell *matHeaderCellDef mat-sort-header>\n {{dCol.displayName}}\n </mat-header-cell>\n <mat-cell class=\"entity\" [class.enabled]=\"!dCol.disableClick && (allowUpdate(entity) || allowRead(entity))\"\n (click)=\"editEntity(entity, dCol)\"\n *matCellDef=\"let entity\"\n >\n <ng-container *ngIf=\"dCol.Component\">\n <display-column-value [entity]=\"entity\" [ComponentClass]=\"dCol.Component\"></display-column-value>\n </ng-container>\n <ng-container *ngIf=\"!dCol.Component\">\n {{getDisplayColumnValue(entity, dCol)}}\n </ng-container>\n </mat-cell>\n </ng-container>\n\n <mat-header-row *matHeaderRowDef=\"displayedColumns\"></mat-header-row>\n <mat-row *matRowDef=\"let row; columns: displayedColumns\"></mat-row>\n </mat-table>\n\n <mat-spinner *ngIf=\"isLoading && data.baseData.displayLoadingSpinner\">\n </mat-spinner>\n\n <mat-paginator [length]=\"dataSource.filteredData.length\" [pageIndex]=\"0\" [pageSize]=\"10\" [pageSizeOptions]=\"[5, 10, 25, 50]\"></mat-paginator>\n</div>", styles: [".mdc-data-table__row:last-child .mdc-data-table__cell{border-bottom:1px solid rgba(0,0,0,.12)}button{width:100%}mat-spinner{margin:10px auto}.title{text-align:center}.actions-button,.create-button{height:56px;line-height:24px;font-size:16px}.mat-column-select{flex:0 0 75px}.enabled:hover{cursor:pointer}@media (max-width: 767px){.actions-button,.create-button{margin-bottom:15px}}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i2$1.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "component", type: i2$2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2$2.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i4.MatCheckbox, selector: "mat-checkbox", inputs: ["disableRipple", "color", "tabIndex"], exportAs: ["matCheckbox"] }, { kind: "ngmodule", type: MatTableModule }, { kind: "component", type: i6.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i6.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i6.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i6.MatColumnDef, selector: "[matColumnDef]", inputs: ["sticky", "matColumnDef"] }, { kind: "directive", type: i6.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i6.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i6.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i6.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i6.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i6.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "ngmodule", type: MatPaginatorModule }, { kind: "component", type: i16.MatPaginator, selector: "mat-paginator", inputs: ["disabled"], exportAs: ["matPaginator"] }, { kind: "ngmodule", type: MatSortModule }, { kind: "directive", type: i18.MatSort, selector: "[matSort]", inputs: ["matSortDisabled", "matSortActive", "matSortStart", "matSortDirection", "matSortDisableClear"], outputs: ["matSortChange"], exportAs: ["matSort"] }, { kind: "component", type: i18.MatSortHeader, selector: "[mat-sort-header]", inputs: ["disabled", "mat-sort-header", "arrowPosition", "start", "sortActionDescription", "disableClear"], exportAs: ["matSortHeader"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i5.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", inputs: ["disabled", "disableRipple", "color"], exportAs: ["matButton"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i14.MatMenu, selector: "mat-menu", exportAs: ["matMenu"] }, { kind: "component", type: i14.MatMenuItem, selector: "[mat-menu-item]", inputs: ["disabled", "disableRipple", "role"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i14.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatDialogModule }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i15.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "component", type: DisplayColumnValueComponent, selector: "display-column-value", inputs: ["entity", "ComponentClass"] }] });
5836
6355
  }
5837
6356
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.0", ngImport: i0, type: NgxMatEntityTableComponent, decorators: [{
5838
6357
  type: Component,
@@ -5845,6 +6364,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.0", ngImpor
5845
6364
  MatCheckboxModule,
5846
6365
  MatTableModule,
5847
6366
  MatPaginatorModule,
6367
+ MatSortModule,
5848
6368
  MatButtonModule,
5849
6369
  MatMenuModule,
5850
6370
  MatDialogModule,
@@ -5920,6 +6440,15 @@ class PropertyDecoratorConfig {
5920
6440
  */
5921
6441
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
5922
6442
  isReadOnly;
6443
+ /**
6444
+ * The value that the property should be prefilled with. Only active in create mode.
6445
+ */
6446
+ default;
6447
+ /**
6448
+ * A function that runs just before the inputChangeEvent every time the property is changed.
6449
+ */
6450
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
6451
+ change;
5923
6452
  }
5924
6453
 
5925
6454
  /**
@@ -6461,6 +6990,7 @@ class DateTimeDateDecoratorConfigInternal extends PropertyDecoratorConfigInterna
6461
6990
  this.minTime = data.minTime;
6462
6991
  this.maxTime = data.maxTime;
6463
6992
  this.filterTime = data.filterTime;
6993
+ this.defaultWidths = [6, 12, 12];
6464
6994
  }
6465
6995
  }
6466
6996
 
@@ -7015,5 +7545,5 @@ class StringDecoratorConfig extends PropertyDecoratorConfig {
7015
7545
  * Generated bundle index. Do not edit.
7016
7546
  */
7017
7547
 
7018
- export { ArrayDecoratorConfig, DateUtilities, DecoratorTypes, Entity, EntityService, EntityUtilities, FileUtilities, NGX_EDIT_DATA, NGX_EDIT_DATA_ENTITY, NGX_EDIT_DATA_ENTITY_SERVICE, NGX_GET_VALIDATION_ERROR_MESSAGE, NgxMatEntityBaseDisplayColumnValueComponent, NgxMatEntityBaseInputComponent, NgxMatEntityConfirmDialogComponent, NgxMatEntityCreateDialogComponent, NgxMatEntityEditDialogComponent, NgxMatEntityEditPageComponent, NgxMatEntityInputComponent, NgxMatEntityInputModule, NgxMatEntityTableComponent, UnsavedChangesGuard, array, boolean, custom, date, defaultEditDataRoute, exportAsCsvMultiAction, exportAsJsonMultiAction, exportAsXmlMultiAction, file, hasMany, importFromJsonMultiAction, number, object, referencesMany, referencesOne, string };
7548
+ export { ArrayDecoratorConfig, DateUtilities, DecoratorTypes, Entity, EntityService, EntityUtilities, FileUtilities, NGX_EDIT_DATA, NGX_EDIT_DATA_ENTITY, NGX_EDIT_DATA_ENTITY_SERVICE, NGX_GET_VALIDATION_ERROR_MESSAGE, NGX_VALIDATION_ERRORS_TOOLTIP_TITLE, NgxMatEntityBaseDisplayColumnValueComponent, NgxMatEntityBaseInputComponent, NgxMatEntityConfirmDialogComponent, NgxMatEntityCreateDialogComponent, NgxMatEntityEditDialogComponent, NgxMatEntityEditPageComponent, NgxMatEntityInputComponent, NgxMatEntityInputModule, NgxMatEntityTableComponent, TooltipComponent, UnsavedChangesGuard, ValidationUtilities, array, boolean, custom, date, defaultEditDataRoute, exportAsCsvMultiAction, exportAsJsonMultiAction, exportAsXmlMultiAction, file, getValidationErrorsTooltipContent, hasMany, importFromJsonMultiAction, number, object, referencesMany, referencesOne, string };
7019
7549
  //# sourceMappingURL=ngx-material-entity.mjs.map