@ldquocc/dynamic-form 0.0.8 → 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ng-package.json +15 -0
- package/package.json +20 -31
- package/src/lib/dynamic-form.component.html +121 -0
- package/src/lib/dynamic-form.component.scss +7 -0
- package/src/lib/dynamic-form.component.spec.ts +21 -0
- package/src/lib/dynamic-form.component.ts +237 -0
- package/src/lib/dynamic-form.service.spec.ts +16 -0
- package/src/lib/dynamic-form.service.ts +9 -0
- package/src/lib/shared/base/base-select.component.ts +183 -0
- package/src/lib/shared/base/base-value-accessor.ts +192 -0
- package/{lib/shared/components/component.d.ts → src/lib/shared/components/component.ts} +34 -14
- package/src/lib/shared/components/d-form-testing/d-form-testing.component.html +197 -0
- package/src/lib/shared/components/d-form-testing/d-form-testing.component.less +12 -0
- package/src/lib/shared/components/d-form-testing/d-form-testing.component.spec.ts +22 -0
- package/src/lib/shared/components/d-form-testing/d-form-testing.component.ts +197 -0
- package/src/lib/shared/components/dynamic-field/dynamic-field.component.html +218 -0
- package/src/lib/shared/components/dynamic-field/dynamic-field.component.scss +28 -0
- package/src/lib/shared/components/dynamic-field/dynamic-field.component.spec.ts +21 -0
- package/src/lib/shared/components/dynamic-field/dynamic-field.component.ts +67 -0
- package/src/lib/shared/components/field-user-config/field-user-config.component.html +38 -0
- package/src/lib/shared/components/field-user-config/field-user-config.component.scss +29 -0
- package/src/lib/shared/components/field-user-config/field-user-config.component.ts +33 -0
- package/src/lib/shared/components/fields/components/suffix/suffix.component.html +42 -0
- package/src/lib/shared/components/fields/components/suffix/suffix.component.scss +3 -0
- package/src/lib/shared/components/fields/components/suffix/suffix.component.spec.ts +22 -0
- package/src/lib/shared/components/fields/components/suffix/suffix.component.ts +32 -0
- package/src/lib/shared/components/fields/date/date-picker/date-picker.component.html +20 -0
- package/src/lib/shared/components/fields/date/date-picker/date-picker.component.scss +5 -0
- package/src/lib/shared/components/fields/date/date-picker/date-picker.component.spec.ts +21 -0
- package/src/lib/shared/components/fields/date/date-picker/date-picker.component.ts +148 -0
- package/src/lib/shared/components/fields/date/date-range/date-range.component.html +33 -0
- package/src/lib/shared/components/fields/date/date-range/date-range.component.scss +22 -0
- package/src/lib/shared/components/fields/date/date-range/date-range.component.spec.ts +21 -0
- package/src/lib/shared/components/fields/date/date-range/date-range.component.ts +133 -0
- package/src/lib/shared/components/fields/input/input-formatted/input-formatted.component.html +31 -0
- package/src/lib/shared/components/fields/input/input-formatted/input-formatted.component.scss +3 -0
- package/src/lib/shared/components/fields/input/input-formatted/input-formatted.component.spec.ts +21 -0
- package/src/lib/shared/components/fields/input/input-formatted/input-formatted.component.ts +148 -0
- package/src/lib/shared/components/fields/input/input-password/input-password.component.html +18 -0
- package/src/lib/shared/components/fields/input/input-password/input-password.component.scss +0 -0
- package/src/lib/shared/components/fields/input/input-password/input-password.component.spec.ts +21 -0
- package/src/lib/shared/components/fields/input/input-password/input-password.component.ts +56 -0
- package/src/lib/shared/components/fields/input/input-text/input-text.component.html +73 -0
- package/src/lib/shared/components/fields/input/input-text/input-text.component.scss +0 -0
- package/src/lib/shared/components/fields/input/input-text/input-text.component.spec.ts +21 -0
- package/src/lib/shared/components/fields/input/input-text/input-text.component.ts +98 -0
- package/src/lib/shared/components/fields/select/multi-select/multi-select.component.html +73 -0
- package/src/lib/shared/components/fields/select/multi-select/multi-select.component.scss +21 -0
- package/src/lib/shared/components/fields/select/multi-select/multi-select.component.spec.ts +21 -0
- package/src/lib/shared/components/fields/select/multi-select/multi-select.component.ts +212 -0
- package/src/lib/shared/components/fields/select/single-select/single-select.component.html +55 -0
- package/src/lib/shared/components/fields/select/single-select/single-select.component.scss +1 -0
- package/src/lib/shared/components/fields/select/single-select/single-select.component.spec.ts +21 -0
- package/src/lib/shared/components/fields/select/single-select/single-select.component.ts +217 -0
- package/src/lib/shared/components/fields/select/single-select/sl.component.old.txt +337 -0
- package/src/lib/shared/components/fields/select/single-select/sl.component.old2.txt +340 -0
- package/src/lib/shared/components/fields/select/tree-select/tree-select.component.html +38 -0
- package/src/lib/shared/components/fields/select/tree-select/tree-select.component.scss +1 -0
- package/src/lib/shared/components/fields/select/tree-select/tree-select.component.spec.ts +21 -0
- package/src/lib/shared/components/fields/select/tree-select/tree-select.component.ts +401 -0
- package/src/lib/shared/components/fields/textarea/textarea.component.html +29 -0
- package/src/lib/shared/components/fields/textarea/textarea.component.scss +17 -0
- package/src/lib/shared/components/fields/textarea/textarea.component.spec.ts +21 -0
- package/src/lib/shared/components/fields/textarea/textarea.component.ts +76 -0
- package/src/lib/shared/components/fields/upload/avatar-upload/avatar-upload.component.html +30 -0
- package/src/lib/shared/components/fields/upload/avatar-upload/avatar-upload.component.scss +22 -0
- package/src/lib/shared/components/fields/upload/avatar-upload/avatar-upload.component.spec.ts +22 -0
- package/src/lib/shared/components/fields/upload/avatar-upload/avatar-upload.component.ts +217 -0
- package/src/lib/shared/components/fields/upload/multi-file-upload/multi-file-upload.component.html +101 -0
- package/src/lib/shared/components/fields/upload/multi-file-upload/multi-file-upload.component.scss +117 -0
- package/src/lib/shared/components/fields/upload/multi-file-upload/multi-file-upload.component.spec.ts +22 -0
- package/src/lib/shared/components/fields/upload/multi-file-upload/multi-file-upload.component.ts +480 -0
- package/src/lib/shared/components/fields/upload/multi-image-upload/multi-image-upload.component.html +82 -0
- package/src/lib/shared/components/fields/upload/multi-image-upload/multi-image-upload.component.less +80 -0
- package/src/lib/shared/components/fields/upload/multi-image-upload/multi-image-upload.component.spec.ts +22 -0
- package/src/lib/shared/components/fields/upload/multi-image-upload/multi-image-upload.component.ts +288 -0
- package/src/lib/shared/components/fields/upload/single-file-upload/single-file-upload.component.html +104 -0
- package/src/lib/shared/components/fields/upload/single-file-upload/single-file-upload.component.scss +37 -0
- package/src/lib/shared/components/fields/upload/single-file-upload/single-file-upload.component.spec.ts +21 -0
- package/src/lib/shared/components/fields/upload/single-file-upload/single-file-upload.component.ts +317 -0
- package/src/lib/shared/config/dynamic-form.config.ts +11 -0
- package/src/lib/shared/consts/common/common.const.ts +5 -0
- package/src/lib/shared/consts/common/field-dependency.const.ts +1 -0
- package/src/lib/shared/consts/common/file.const.ts +130 -0
- package/{lib/shared/consts/common/index.d.ts → src/lib/shared/consts/common/index.ts} +4 -4
- package/src/lib/shared/consts/common/sample-test.const.ts +1648 -0
- package/src/lib/shared/consts/common/standalone-import.const.ts +51 -0
- package/src/lib/shared/consts/controls/control.const.ts +11 -0
- package/{lib/shared/consts/controls/index.d.ts → src/lib/shared/consts/controls/index.ts} +1 -1
- package/src/lib/shared/consts/fields/date-field.const.ts +12 -0
- package/src/lib/shared/consts/fields/field.const.ts +23 -0
- package/{lib/shared/consts/fields/index.d.ts → src/lib/shared/consts/fields/index.ts} +5 -5
- package/src/lib/shared/consts/fields/input-field.const.ts +4 -0
- package/src/lib/shared/consts/fields/input-formatted-field.const.ts +35 -0
- package/src/lib/shared/consts/fields/radio-field.const.ts +31 -0
- package/src/lib/shared/consts/fields/select-field.const.ts +7 -0
- package/src/lib/shared/consts/fields/upload-field.const.ts +1 -0
- package/src/lib/shared/consts/validators/index.ts +0 -0
- package/src/lib/shared/consts/validators/pattern.const.ts +117 -0
- package/src/lib/shared/enums/common/component-ui.enum.ts +8 -0
- package/src/lib/shared/enums/common/field-dependency.enum.ts +10 -0
- package/src/lib/shared/enums/common/file-size-type.enum.ts +6 -0
- package/{lib/shared/enums/common/index.d.ts → src/lib/shared/enums/common/index.ts} +4 -4
- package/src/lib/shared/enums/common/output-value-type.enum.ts +22 -0
- package/src/lib/shared/enums/common/view-mode.enum.ts +4 -0
- package/src/lib/shared/enums/controls/control.enum.ts +12 -0
- package/{lib/shared/enums/controls/index.d.ts → src/lib/shared/enums/controls/index.ts} +1 -1
- package/src/lib/shared/enums/fields/date-field.enum.ts +4 -0
- package/src/lib/shared/enums/fields/field.enum.ts +21 -0
- package/{lib/shared/enums/fields/index.d.ts → src/lib/shared/enums/fields/index.ts} +6 -6
- package/src/lib/shared/enums/fields/input-field.enum.ts +6 -0
- package/src/lib/shared/enums/fields/input-formatted-field.enum.ts +6 -0
- package/src/lib/shared/enums/fields/select-field.enum.ts +4 -0
- package/src/lib/shared/enums/fields/upload-field.enum.ts +5 -0
- package/src/lib/shared/enums/form/dynamic-form.enum.ts +6 -0
- package/{lib/shared/enums/form/index.d.ts → src/lib/shared/enums/form/index.ts} +1 -1
- package/{lib/shared/enums/validators/index.d.ts → src/lib/shared/enums/validators/index.ts} +2 -2
- package/src/lib/shared/enums/validators/regex.enum.ts +47 -0
- package/src/lib/shared/enums/validators/validators.enum.ts +17 -0
- package/src/lib/shared/factories/auto-numeric-options.factory.ts +14 -0
- package/src/lib/shared/models/common/api-config.model.ts +7 -0
- package/src/lib/shared/models/common/api-response.model.ts +20 -0
- package/src/lib/shared/models/common/common.model.ts +39 -0
- package/src/lib/shared/models/common/field-dependency.model.ts +27 -0
- package/src/lib/shared/models/common/file-config.model.ts +22 -0
- package/{lib/shared/models/common/index.d.ts → src/lib/shared/models/common/index.ts} +7 -7
- package/src/lib/shared/models/common/obj-field.model.ts +68 -0
- package/src/lib/shared/models/common/object-storage.model.ts +15 -0
- package/{lib/shared/models/common/output-value-type.model.d.ts → src/lib/shared/models/common/output-value-type.model.ts} +4 -4
- package/src/lib/shared/models/common/table-models.ts +16 -0
- package/{lib/shared/models/common/view-mode.model.d.ts → src/lib/shared/models/common/view-mode.model.ts} +1 -1
- package/{lib/shared/models/controls/control.model.d.ts → src/lib/shared/models/controls/control.model.ts} +3 -2
- package/{lib/shared/models/controls/index.d.ts → src/lib/shared/models/controls/index.ts} +1 -1
- package/src/lib/shared/models/fields/date/base-date-field.model.ts +52 -0
- package/src/lib/shared/models/fields/date/date-picker-field.model.ts +43 -0
- package/src/lib/shared/models/fields/date/date-range-field.model.ts +52 -0
- package/{lib/shared/models/fields/date/index.d.ts → src/lib/shared/models/fields/date/index.ts} +3 -3
- package/src/lib/shared/models/fields/field-grid.model.ts +10 -0
- package/src/lib/shared/models/fields/field-group.model.ts +50 -0
- package/src/lib/shared/models/fields/field-user-config.model.ts +8 -0
- package/src/lib/shared/models/fields/field.model.ts +184 -0
- package/{lib/shared/models/fields/index.d.ts → src/lib/shared/models/fields/index.ts} +2 -2
- package/{lib/shared/models/fields/input/index.d.ts → src/lib/shared/models/fields/input/index.ts} +4 -4
- package/src/lib/shared/models/fields/input/input-field.model.ts +111 -0
- package/src/lib/shared/models/fields/input/input-formatted-field.model.ts +38 -0
- package/src/lib/shared/models/fields/input/input-password-field.model.ts +38 -0
- package/src/lib/shared/models/fields/input/input-text-field.model.ts +26 -0
- package/src/lib/shared/models/fields/radio/index.ts +0 -0
- package/src/lib/shared/models/fields/radio/radio-base-field.model.ts +84 -0
- package/src/lib/shared/models/fields/select/index.ts +0 -0
- package/src/lib/shared/models/fields/select/multi-select-field.model.ts +46 -0
- package/src/lib/shared/models/fields/select/select-base-field.model.ts +125 -0
- package/src/lib/shared/models/fields/select/single-select-field.model.ts +43 -0
- package/src/lib/shared/models/fields/select/tree-select-field.model.ts +44 -0
- package/src/lib/shared/models/fields/textarea/index.ts +0 -0
- package/src/lib/shared/models/fields/textarea/textarea-field.model.ts +97 -0
- package/src/lib/shared/models/fields/upload/avatar-upload-field.model.ts +33 -0
- package/src/lib/shared/models/fields/upload/base-upload-field.model.ts +105 -0
- package/{lib/shared/models/fields/upload/index.d.ts → src/lib/shared/models/fields/upload/index.ts} +5 -5
- package/src/lib/shared/models/fields/upload/multi-file-upload-field.model.ts +34 -0
- package/src/lib/shared/models/fields/upload/multi-image-upload-field.model.ts +42 -0
- package/src/lib/shared/models/fields/upload/single-file-upload-field.model.ts +33 -0
- package/{lib/shared/models/form/dynamic-form.model.d.ts → src/lib/shared/models/form/dynamic-form.model.ts} +1 -1
- package/{lib/shared/models/form/index.d.ts → src/lib/shared/models/form/index.ts} +1 -1
- package/src/lib/shared/models/validations/index.ts +1 -0
- package/src/lib/shared/models/validations/validations.model.ts +14 -0
- package/src/lib/shared/services/dynamic-form.service.spec.ts +16 -0
- package/src/lib/shared/services/dynamic-form.service.ts +251 -0
- package/src/lib/shared/services/field.service.spec.ts +16 -0
- package/src/lib/shared/services/field.service.ts +152 -0
- package/src/lib/shared/services/file.service.spec.ts +16 -0
- package/src/lib/shared/services/file.service.ts +53 -0
- package/src/lib/shared/services/form-config.service.ts +53 -0
- package/src/lib/shared/services/form-data.service.spec.ts +16 -0
- package/src/lib/shared/services/form-data.service.ts +114 -0
- package/src/lib/shared/services/form-utils.ts +52 -0
- package/src/lib/shared/services/suffix.service.spec.ts +16 -0
- package/src/lib/shared/services/suffix.service.ts +16 -0
- package/src/lib/shared/services/validation.service.spec.ts +16 -0
- package/src/lib/shared/services/validation.service.ts +38 -0
- package/src/lib/shared/utils/common/common.utils.ts +52 -0
- package/{lib/shared/utils/common/index.d.ts → src/lib/shared/utils/common/index.ts} +1 -1
- package/src/lib/shared/utils/date/date.utils.ts +129 -0
- package/{lib/shared/utils/date/index.d.ts → src/lib/shared/utils/date/index.ts} +1 -1
- package/src/lib/shared/utils/dynamic-field/dynamic-field.ts +0 -0
- package/src/lib/shared/utils/dynamic-field/index.ts +0 -0
- package/src/lib/shared/utils/dynamic-form/dynamic-form.ts +114 -0
- package/src/lib/shared/utils/dynamic-form/index.ts +0 -0
- package/src/lib/shared/utils/fields/fields.ts +78 -0
- package/{lib/shared/utils/fields/index.d.ts → src/lib/shared/utils/fields/index.ts} +1 -1
- package/src/lib/shared/utils/file/file.utils.ts +37 -0
- package/{lib/shared/utils/file/index.d.ts → src/lib/shared/utils/file/index.ts} +1 -1
- package/src/lib/shared/utils/validators/custom-validators.ts +158 -0
- package/src/lib/shared/utils/validators/default-error-message.ts +42 -0
- package/{lib/shared/utils/validators/index.d.ts → src/lib/shared/utils/validators/index.ts} +3 -3
- package/src/lib/shared/utils/validators/pattern.ts +26 -0
- package/{public-api.d.ts → src/public-api.ts} +68 -28
- package/tsconfig.lib.json +15 -0
- package/tsconfig.lib.prod.json +11 -0
- package/tsconfig.spec.json +15 -0
- package/fesm2022/ldquocc-dynamic-form.mjs +0 -4938
- package/fesm2022/ldquocc-dynamic-form.mjs.map +0 -1
- package/index.d.ts +0 -5
- package/lib/dynamic-form.component.d.ts +0 -67
- package/lib/shared/base/base-select.component.d.ts +0 -19
- package/lib/shared/base/base-value-accessor.d.ts +0 -69
- package/lib/shared/components/dynamic-field/dynamic-field.component.d.ts +0 -23
- package/lib/shared/components/field-user-config/field-user-config.component.d.ts +0 -10
- package/lib/shared/components/fields/components/suffix/suffix.component.d.ts +0 -12
- package/lib/shared/components/fields/date/date-picker/date-picker.component.d.ts +0 -17
- package/lib/shared/components/fields/date/date-range/date-range.component.d.ts +0 -17
- package/lib/shared/components/fields/input/input-formatted/input-formatted.component.d.ts +0 -20
- package/lib/shared/components/fields/input/input-password/input-password.component.d.ts +0 -12
- package/lib/shared/components/fields/input/input-text/input-text.component.d.ts +0 -14
- package/lib/shared/components/fields/select/multi-select/multi-select.component.d.ts +0 -21
- package/lib/shared/components/fields/select/single-select/single-select.component.d.ts +0 -21
- package/lib/shared/components/fields/select/tree-select/tree-select.component.d.ts +0 -28
- package/lib/shared/components/fields/textarea/textarea.component.d.ts +0 -14
- package/lib/shared/components/fields/upload/avatar-upload/avatar-upload.component.d.ts +0 -25
- package/lib/shared/components/fields/upload/multi-file-upload/multi-file-upload.component.d.ts +0 -76
- package/lib/shared/components/fields/upload/multi-image-upload/multi-image-upload.component.d.ts +0 -27
- package/lib/shared/components/fields/upload/single-file-upload/single-file-upload.component.d.ts +0 -29
- package/lib/shared/config/dynamic-form.config.d.ts +0 -9
- package/lib/shared/consts/common/common.const.d.ts +0 -5
- package/lib/shared/consts/common/field-dependency.const.d.ts +0 -1
- package/lib/shared/consts/common/file.const.d.ts +0 -148
- package/lib/shared/consts/common/standalone-import.const.d.ts +0 -7
- package/lib/shared/consts/controls/control.const.d.ts +0 -1
- package/lib/shared/consts/fields/date-field.const.d.ts +0 -7
- package/lib/shared/consts/fields/field.const.d.ts +0 -4
- package/lib/shared/consts/fields/input-field.const.d.ts +0 -4
- package/lib/shared/consts/fields/input-formatted-field.const.d.ts +0 -4
- package/lib/shared/consts/fields/radio-field.const.d.ts +0 -10
- package/lib/shared/consts/fields/select-field.const.d.ts +0 -6
- package/lib/shared/consts/fields/upload-field.const.d.ts +0 -1
- package/lib/shared/consts/validators/pattern.const.d.ts +0 -2
- package/lib/shared/enums/common/field-dependency.enum.d.ts +0 -9
- package/lib/shared/enums/common/file-size-type.enum.d.ts +0 -6
- package/lib/shared/enums/common/output-value-type.enum.d.ts +0 -19
- package/lib/shared/enums/common/view-mode.enum.d.ts +0 -4
- package/lib/shared/enums/controls/control.enum.d.ts +0 -11
- package/lib/shared/enums/fields/date-field.enum.d.ts +0 -4
- package/lib/shared/enums/fields/field.enum.d.ts +0 -20
- package/lib/shared/enums/fields/input-field.enum.d.ts +0 -6
- package/lib/shared/enums/fields/input-formatted-field.enum.d.ts +0 -6
- package/lib/shared/enums/fields/select-field.enum.d.ts +0 -4
- package/lib/shared/enums/fields/upload-field.enum.d.ts +0 -5
- package/lib/shared/enums/form/dynamic-form.enum.d.ts +0 -6
- package/lib/shared/enums/validators/regex.enum.d.ts +0 -38
- package/lib/shared/enums/validators/validators.enum.d.ts +0 -14
- package/lib/shared/factories/auto-numeric-options.factory.d.ts +0 -5
- package/lib/shared/models/common/api-config.model.d.ts +0 -4
- package/lib/shared/models/common/api-response.model.d.ts +0 -19
- package/lib/shared/models/common/common.model.d.ts +0 -29
- package/lib/shared/models/common/field-dependency.model.d.ts +0 -16
- package/lib/shared/models/common/file-config.model.d.ts +0 -11
- package/lib/shared/models/common/obj-field.model.d.ts +0 -59
- package/lib/shared/models/common/object-storage.model.d.ts +0 -15
- package/lib/shared/models/common/table-models.d.ts +0 -15
- package/lib/shared/models/fields/date/base-date-field.model.d.ts +0 -19
- package/lib/shared/models/fields/date/date-picker-field.model.d.ts +0 -12
- package/lib/shared/models/fields/date/date-range-field.model.d.ts +0 -12
- package/lib/shared/models/fields/field-grid.model.d.ts +0 -9
- package/lib/shared/models/fields/field-group.model.d.ts +0 -21
- package/lib/shared/models/fields/field-user-config.model.d.ts +0 -3
- package/lib/shared/models/fields/field.model.d.ts +0 -57
- package/lib/shared/models/fields/input/input-field.model.d.ts +0 -31
- package/lib/shared/models/fields/input/input-formatted-field.model.d.ts +0 -13
- package/lib/shared/models/fields/input/input-password-field.model.d.ts +0 -11
- package/lib/shared/models/fields/input/input-text-field.model.d.ts +0 -9
- package/lib/shared/models/fields/radio/radio-base-field.model.d.ts +0 -26
- package/lib/shared/models/fields/select/multi-select-field.model.d.ts +0 -11
- package/lib/shared/models/fields/select/select-base-field.model.d.ts +0 -40
- package/lib/shared/models/fields/select/single-select-field.model.d.ts +0 -9
- package/lib/shared/models/fields/select/tree-select-field.model.d.ts +0 -10
- package/lib/shared/models/fields/textarea/textarea-field.model.d.ts +0 -20
- package/lib/shared/models/fields/upload/avatar-upload-field.model.d.ts +0 -9
- package/lib/shared/models/fields/upload/base-upload-field.model.d.ts +0 -25
- package/lib/shared/models/fields/upload/multi-file-upload-field.model.d.ts +0 -10
- package/lib/shared/models/fields/upload/multi-image-upload-field.model.d.ts +0 -12
- package/lib/shared/models/fields/upload/single-file-upload-field.model.d.ts +0 -11
- package/lib/shared/services/dynamic-form.service.d.ts +0 -48
- package/lib/shared/services/field.service.d.ts +0 -41
- package/lib/shared/services/file.service.d.ts +0 -20
- package/lib/shared/services/form-config.service.d.ts +0 -10
- package/lib/shared/services/form-data.service.d.ts +0 -53
- package/lib/shared/services/form-utils.d.ts +0 -5
- package/lib/shared/services/suffix.service.d.ts +0 -12
- package/lib/shared/services/validation.service.d.ts +0 -10
- package/lib/shared/utils/common/common.utils.d.ts +0 -7
- package/lib/shared/utils/date/date.utils.d.ts +0 -6
- package/lib/shared/utils/dynamic-form/dynamic-form.d.ts +0 -11
- package/lib/shared/utils/fields/fields.d.ts +0 -20
- package/lib/shared/utils/file/file.utils.d.ts +0 -4
- package/lib/shared/utils/validators/custom-validators.d.ts +0 -11
- package/lib/shared/utils/validators/default-error-message.d.ts +0 -6
- package/lib/shared/utils/validators/pattern.d.ts +0 -4
- /package/{styles → src/lib/shared/styles}/_common-form.scss +0 -0
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { FORMAT_DATE_PATTERNS } from '../../consts/fields';
|
|
2
|
+
|
|
3
|
+
// Chuyển đổi đối tượng Date thành string có format DDMMYYYY.
|
|
4
|
+
export const formatDateToDDMMYYYY = (date: Date, separator: '-' | '/' = '-'): string => {
|
|
5
|
+
const dd = String(date.getDate()).padStart(2, '0');
|
|
6
|
+
const mm = String(date.getMonth() + 1).padStart(2, '0');
|
|
7
|
+
const yyyy = date.getFullYear();
|
|
8
|
+
|
|
9
|
+
return `${dd}${separator}${mm}${separator}${yyyy}`;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// Chuyển đổi đối tượng Date thành string có format DDMMYYYY.
|
|
13
|
+
export const formatDateToYYYYMMDD = (date: Date, separator: '-' | '/' = '-'): string => {
|
|
14
|
+
const dd = String(date.getDate()).padStart(2, '0');
|
|
15
|
+
const mm = String(date.getMonth() + 1).padStart(2, '0');
|
|
16
|
+
const yyyy = date.getFullYear();
|
|
17
|
+
|
|
18
|
+
return `${yyyy}${separator}${mm}${separator}${dd}`;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// Chuyển đổi string ở các dạng: YYYY-MM-DD, DD-MM-YYYY, MM-DD-YYYY đến Date, nếu sẵn là Date thì mặc định không đổi.
|
|
22
|
+
export const normalizeToDate = (input: string | Date | undefined): Date | null => {
|
|
23
|
+
if (!input) return null;
|
|
24
|
+
|
|
25
|
+
if (input instanceof Date && !isNaN(input.getTime())) return input;
|
|
26
|
+
if (typeof input !== 'string') return null;
|
|
27
|
+
|
|
28
|
+
const inputTrimed = input.trim();
|
|
29
|
+
|
|
30
|
+
let match: RegExpMatchArray | null;
|
|
31
|
+
let year: number, month: number, day: number;
|
|
32
|
+
let hour = 0,
|
|
33
|
+
minute = 0,
|
|
34
|
+
second = 0;
|
|
35
|
+
|
|
36
|
+
if ((match = inputTrimed.match(FORMAT_DATE_PATTERNS.YYYY_MM_DD))) {
|
|
37
|
+
year = +match[1];
|
|
38
|
+
month = +match[3] - 1;
|
|
39
|
+
day = +match[4];
|
|
40
|
+
} else if ((match = inputTrimed.match(FORMAT_DATE_PATTERNS.DD_MM_YYYY))) {
|
|
41
|
+
day = +match[1];
|
|
42
|
+
month = +match[3] - 1;
|
|
43
|
+
year = +match[4];
|
|
44
|
+
} else if ((match = inputTrimed.match(FORMAT_DATE_PATTERNS.MM_DD_YYYY))) {
|
|
45
|
+
month = +match[1] - 1;
|
|
46
|
+
day = +match[3];
|
|
47
|
+
year = +match[4];
|
|
48
|
+
} else if ((match = inputTrimed.match(FORMAT_DATE_PATTERNS.MM_DD_YYYY_HMS))) {
|
|
49
|
+
month = +match[1] - 1;
|
|
50
|
+
day = +match[3];
|
|
51
|
+
year = +match[4];
|
|
52
|
+
hour = +match[5];
|
|
53
|
+
minute = +match[6];
|
|
54
|
+
second = +match[7];
|
|
55
|
+
} else {
|
|
56
|
+
console.warn('Sai định dạng chuỗi date');
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const date = new Date(year, month, day, hour, minute, second);
|
|
61
|
+
|
|
62
|
+
if (
|
|
63
|
+
date.getFullYear() === year &&
|
|
64
|
+
date.getMonth() === month &&
|
|
65
|
+
date.getDate() === day &&
|
|
66
|
+
date.getHours() === hour &&
|
|
67
|
+
date.getMinutes() === minute &&
|
|
68
|
+
date.getSeconds() === second
|
|
69
|
+
) {
|
|
70
|
+
return date;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
console.warn('Ngày hoặc giờ không hợp lệ');
|
|
74
|
+
return null;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export const normalizeToDate1 = (input: string | Date | undefined): Date | null => {
|
|
78
|
+
if (input) {
|
|
79
|
+
if (input instanceof Date && !isNaN(input.getTime())) return input;
|
|
80
|
+
if (typeof input !== 'string') return null;
|
|
81
|
+
const inputTrimed = String(input).trim();
|
|
82
|
+
let match: RegExpMatchArray | null;
|
|
83
|
+
let year: number, month: number, day: number;
|
|
84
|
+
if ((match = inputTrimed.match(FORMAT_DATE_PATTERNS.YYYY_MM_DD))) {
|
|
85
|
+
year = +match[1];
|
|
86
|
+
month = +match[3] - 1;
|
|
87
|
+
day = +match[4];
|
|
88
|
+
} else if ((match = inputTrimed.match(FORMAT_DATE_PATTERNS.DD_MM_YYYY))) {
|
|
89
|
+
day = +match[1];
|
|
90
|
+
month = +match[3] - 1;
|
|
91
|
+
year = +match[4];
|
|
92
|
+
} else if ((match = inputTrimed.match(FORMAT_DATE_PATTERNS.MM_DD_YYYY))) {
|
|
93
|
+
month = +match[1] - 1;
|
|
94
|
+
day = +match[3];
|
|
95
|
+
year = +match[4];
|
|
96
|
+
} else {
|
|
97
|
+
console.warn('Sai định dạng chuỗi date');
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
const date = new Date(year, month, day); // Validate: kiểm tra lại giá trị date (28/29/30/31 hợp lệ)
|
|
101
|
+
if (date.getFullYear() === year && date.getMonth() === month && date.getDate() === day) {
|
|
102
|
+
return date;
|
|
103
|
+
} else {
|
|
104
|
+
console.warn('Ngày đưa vào không hợp lệ');
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return null;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
// Tạo danh sách năm chạy ngược từ hiện tại đến năm chỉ định.
|
|
112
|
+
export const generateYearsDesc = (from: number): number[] => {
|
|
113
|
+
const currentYear = new Date().getFullYear();
|
|
114
|
+
const years: number[] = [];
|
|
115
|
+
for (let year = currentYear; year >= from; year--) {
|
|
116
|
+
years.push(year);
|
|
117
|
+
}
|
|
118
|
+
return years;
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
// Tạo danh sách năm chạy từ năm chỉ định đến năm hiện tại.
|
|
122
|
+
export const generateYearsAsc = (from: number): number[] => {
|
|
123
|
+
const currentYear = new Date().getFullYear();
|
|
124
|
+
const years: number[] = [];
|
|
125
|
+
for (let year = from; year <= currentYear; year++) {
|
|
126
|
+
years.push(year);
|
|
127
|
+
}
|
|
128
|
+
return years;
|
|
129
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from './date.utils';
|
|
1
|
+
export * from './date.utils';
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { AbstractControl, FormGroup } from "@angular/forms";
|
|
2
|
+
import { BaseField } from "../../models/fields/field.model";
|
|
3
|
+
import { FormMode } from "../../models/form";
|
|
4
|
+
import { FormModeEnum } from "../../enums/form";
|
|
5
|
+
import { SelectBaseField } from "../../models/fields/select/select-base-field.model";
|
|
6
|
+
import { InputPasswordField } from "../../models/fields/input";
|
|
7
|
+
import { FieldTypeEnum } from "../../enums/fields";
|
|
8
|
+
|
|
9
|
+
export const buildForm = (
|
|
10
|
+
fields: BaseField<any>[],
|
|
11
|
+
mode: FormMode,
|
|
12
|
+
model?: Record<string, any>,
|
|
13
|
+
): FormGroup => {
|
|
14
|
+
const groupObj: Record<string, AbstractControl<any, any>> = {};
|
|
15
|
+
// console.log("model", model);
|
|
16
|
+
|
|
17
|
+
for (const field of fields) {
|
|
18
|
+
// Loại bỏ nếu quyết định ẩn field đi.
|
|
19
|
+
if (field.isHidden) continue;
|
|
20
|
+
// Nếu là dạng InputPasswordField nhưng ở mode edit và view thì không được dùng.
|
|
21
|
+
if (
|
|
22
|
+
field instanceof InputPasswordField &&
|
|
23
|
+
(mode === FormModeEnum.Edit || mode === FormModeEnum.View)
|
|
24
|
+
)
|
|
25
|
+
continue;
|
|
26
|
+
|
|
27
|
+
const fieldKey = field?.key;
|
|
28
|
+
// field.value = field.allowSetValue ? (model?.[fieldKey] || field.value) : null;
|
|
29
|
+
|
|
30
|
+
if (field.allowSetValue) {
|
|
31
|
+
field.value = field.getDefaultValue();
|
|
32
|
+
field.modelValue = null;
|
|
33
|
+
if (model?.[fieldKey]) {
|
|
34
|
+
field.value = field.toFormValue(model[fieldKey]);
|
|
35
|
+
field.modelValue = model[fieldKey];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
field.initialValue = field.value;
|
|
39
|
+
}
|
|
40
|
+
// console.log("e", field.value );
|
|
41
|
+
// field.value = field.allowSetValue ? field.toFormValue(model?.[fieldKey] | field.getDefaultValue()) : field.getDefaultValue();
|
|
42
|
+
const control = field.toFormControl();
|
|
43
|
+
control.reset(field.value);
|
|
44
|
+
if (field.isDisabled) {
|
|
45
|
+
//console.log(field.key, 'dfdfdfdf');
|
|
46
|
+
|
|
47
|
+
control?.disable();
|
|
48
|
+
}
|
|
49
|
+
groupObj[fieldKey] = control;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return new FormGroup(groupObj);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// Tạo tự động placeholder cho các dạng control nhập với label hoặc key từ field
|
|
56
|
+
export const createInputPlaceholder = (field: BaseField<any>): string => {
|
|
57
|
+
const label = field?.label;
|
|
58
|
+
if (label) return `Nhập ${label?.toLowerCase()}`;
|
|
59
|
+
|
|
60
|
+
return `Nhập ${field.key?.toLowerCase()}`;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// Tạo tự động placeholder cho các dạng single select với label hoặc key từ field
|
|
64
|
+
export const createSingleSelectPlaceholder = (
|
|
65
|
+
field: BaseField<any>,
|
|
66
|
+
): string => {
|
|
67
|
+
const label = field?.label;
|
|
68
|
+
if (label) return `Chọn ${label?.toLowerCase()}`;
|
|
69
|
+
|
|
70
|
+
return `Chọn ${field.key?.toLowerCase()}`;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// Tạo tự động placeholder cho các dạng multi select với label hoặc key từ field
|
|
74
|
+
export const createMultiSelectPlaceholder = (field: BaseField<any>): string => {
|
|
75
|
+
const label = field?.label;
|
|
76
|
+
if (label) return `Chọn các ${label?.toLowerCase()}`;
|
|
77
|
+
|
|
78
|
+
return `Chọn các ${field.key?.toLowerCase()}`;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
// Tạo tự động placeholder cho các dạng date-picker với label hoặc key từ field
|
|
82
|
+
export const createDatePickerPlaceholder = (field: BaseField<any>): string => {
|
|
83
|
+
const label = field?.label;
|
|
84
|
+
if (label) return `Chọn ${label?.toLowerCase()}`;
|
|
85
|
+
|
|
86
|
+
return `Chọn ${field.key?.toLowerCase()}`;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// Tạo placeholder mặc định cho các dạng Input, Textarea.
|
|
90
|
+
export const createPlaceholder = (field: BaseField<any>): string => {
|
|
91
|
+
const placeholder = field?.placeholder;
|
|
92
|
+
if (placeholder) return placeholder;
|
|
93
|
+
|
|
94
|
+
const label = field?.label;
|
|
95
|
+
if (label) return `Nhập ${label?.toLowerCase()}`;
|
|
96
|
+
|
|
97
|
+
return `Nhập ${field.key?.toLowerCase()}`;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
// Tạo placeholder mặc định cho các dạng Select.
|
|
101
|
+
export const createSelectPlaceholder = <K>(
|
|
102
|
+
field: SelectBaseField<K>,
|
|
103
|
+
): string => {
|
|
104
|
+
const placeholder = field?.placeholder;
|
|
105
|
+
if (placeholder) return placeholder;
|
|
106
|
+
|
|
107
|
+
const label = field?.label;
|
|
108
|
+
if (label) {
|
|
109
|
+
const isSingleSelect = field?.type === FieldTypeEnum.SingleSelect;
|
|
110
|
+
return `Chọn ${isSingleSelect ? "" : "các"} ${label?.toLowerCase()}`;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return `Nhập ${field.key?.toLowerCase()}`;
|
|
114
|
+
};
|
|
File without changes
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { ControlTypeEnum } from '../../enums/controls';
|
|
2
|
+
import { FieldTypeEnum, InputFieldTypeEnum } from '../../enums/fields';
|
|
3
|
+
import { DateFieldTypeEnum } from '../../enums/fields/date-field.enum';
|
|
4
|
+
import { SelectFieldTypeEnum } from '../../enums/fields/select-field.enum';
|
|
5
|
+
import { UploadFieldTypeEnum } from '../../enums/fields/upload-field.enum';
|
|
6
|
+
import { DatePickerField } from '../../models/fields/date/date-picker-field.model';
|
|
7
|
+
import { DateRangeField } from '../../models/fields/date/date-range-field.model';
|
|
8
|
+
import { FieldGroup, GroupedFields } from '../../models/fields/field-group.model';
|
|
9
|
+
import { BaseField } from '../../models/fields/field.model';
|
|
10
|
+
import { InputPasswordField, InputTextField } from '../../models/fields/input';
|
|
11
|
+
import { MultiSelectField } from '../../models/fields/select/multi-select-field.model';
|
|
12
|
+
import { SingleSelectField } from '../../models/fields/select/single-select-field.model';
|
|
13
|
+
import { AvatarUploadField } from '../../models/fields/upload/avatar-upload-field.model';
|
|
14
|
+
import { MultiFileUploadField } from '../../models/fields/upload/multi-file-upload-field.model';
|
|
15
|
+
import { SingleFileUploadField } from '../../models/fields/upload/single-file-upload-field.model';
|
|
16
|
+
|
|
17
|
+
export const isInputTextField = (field: BaseField<any>): field is InputTextField => {
|
|
18
|
+
return field.controlType === ControlTypeEnum.Input && field.type === FieldTypeEnum.InputText;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const isInputPasswordField = (field: BaseField<any>): field is InputPasswordField => {
|
|
22
|
+
return field.controlType === ControlTypeEnum.Input && field.type === FieldTypeEnum.InputPassword;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const isSingleSelectField = (field: BaseField<any>): field is SingleSelectField => {
|
|
26
|
+
return field.controlType === ControlTypeEnum.Select && field.type === FieldTypeEnum.SingleSelect;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const isMultiSelectField = (field: BaseField<any>): field is MultiSelectField => {
|
|
30
|
+
return field.controlType === ControlTypeEnum.Select && field.type === FieldTypeEnum.MultiSelect;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const isDatePickerField = (field: BaseField<any>): field is DatePickerField => {
|
|
34
|
+
return field.controlType === ControlTypeEnum.Date && field.type === FieldTypeEnum.DatePicker;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const isDateRangeField = (field: BaseField<any>): field is DateRangeField => {
|
|
38
|
+
return field.controlType === ControlTypeEnum.Date && field.type === FieldTypeEnum.DateRange;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const isSingleFileUploadField = (field: BaseField<any>): field is SingleFileUploadField => {
|
|
42
|
+
return (
|
|
43
|
+
field.controlType === ControlTypeEnum.Upload && field.type === FieldTypeEnum.SingleFileUpload
|
|
44
|
+
);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export const isMultiFileUploadField = (field: BaseField<any>): field is MultiFileUploadField => {
|
|
48
|
+
return (
|
|
49
|
+
field.controlType === ControlTypeEnum.Upload && field.type === FieldTypeEnum.MultiFileUpload
|
|
50
|
+
);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export const isAvatarUploadField = (field: BaseField<any>): field is AvatarUploadField => {
|
|
54
|
+
return field.controlType === ControlTypeEnum.Upload && field.type === FieldTypeEnum.AvatarUpload;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// Tạo FieldGroup render UI từ thông tin fields và groups truyền vào cho dynamic-form.
|
|
58
|
+
export const buildGroupedFields = (
|
|
59
|
+
fields: BaseField<any>[],
|
|
60
|
+
groups: FieldGroup[] = [],
|
|
61
|
+
): GroupedFields => {
|
|
62
|
+
const noGroup = fields.filter(
|
|
63
|
+
field => !field?.groupCode || field?.groupCode.toLowerCase() === 'none',
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
const sortedGroups = [...groups].sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
|
|
67
|
+
const grouped = sortedGroups
|
|
68
|
+
.map(group => ({
|
|
69
|
+
group,
|
|
70
|
+
fields: fields.filter(field => field.groupCode?.toLowerCase() === group.code.toLowerCase()),
|
|
71
|
+
}))
|
|
72
|
+
.filter(groupedFields => groupedFields.fields.length > 0);
|
|
73
|
+
|
|
74
|
+
return new GroupedFields({
|
|
75
|
+
noGroup,
|
|
76
|
+
grouped,
|
|
77
|
+
});
|
|
78
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from './fields';
|
|
1
|
+
export * from './fields';
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { MIME_TYPES_TO_EXTENSION } from '../../consts/common';
|
|
2
|
+
import { FileSizeTypeEnum } from '../../enums/common/file-size-type.enum';
|
|
3
|
+
|
|
4
|
+
// Format file size, đầu vào là Byte, và loại muốn format sang.
|
|
5
|
+
export const formatFileSize = (size: number, type: FileSizeTypeEnum): number => {
|
|
6
|
+
let returnVal = 0;
|
|
7
|
+
switch (type) {
|
|
8
|
+
case 'KB':
|
|
9
|
+
returnVal = size / 1024;
|
|
10
|
+
break;
|
|
11
|
+
case 'MB':
|
|
12
|
+
returnVal = size / (1024 * 1024);
|
|
13
|
+
break;
|
|
14
|
+
case 'GB':
|
|
15
|
+
returnVal = size / (1024 * 1024 * 1024);
|
|
16
|
+
break;
|
|
17
|
+
case 'TB':
|
|
18
|
+
returnVal = size / (1024 * 1024 * 1024 * 1024);
|
|
19
|
+
break;
|
|
20
|
+
default:
|
|
21
|
+
returnVal = size;
|
|
22
|
+
break;
|
|
23
|
+
}
|
|
24
|
+
return returnVal;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// Lấy extension. dựa vào các cấu hình mimeTypes sẵn.
|
|
28
|
+
export const getExtensions = (acptMimeTypes: string[]): string[] => {
|
|
29
|
+
return acptMimeTypes.map(mime => `.${MIME_TYPES_TO_EXTENSION[mime] || mime}`);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// Tạo một file giả từ thông tin name, type và size.
|
|
33
|
+
export const createFakeFile = (name: string, type: string, size: number): File => {
|
|
34
|
+
const buffer = new Uint8Array(size); // tạo buffer theo size, không crash nếu file có dung lượng lớn.
|
|
35
|
+
const blob = new Blob([buffer], { type });
|
|
36
|
+
return new File([blob], name, { type, lastModified: Date.now() });
|
|
37
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from './file.utils';
|
|
1
|
+
export * from './file.utils';
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AbstractControl,
|
|
3
|
+
AsyncValidatorFn,
|
|
4
|
+
ValidationErrors,
|
|
5
|
+
ValidatorFn,
|
|
6
|
+
} from "@angular/forms";
|
|
7
|
+
import { delay, Observable, of } from "rxjs";
|
|
8
|
+
import { formatFileSize } from "../file";
|
|
9
|
+
import { FileSizeTypeEnum } from "../../enums/common/file-size-type.enum";
|
|
10
|
+
import { isValidJSON } from "../common";
|
|
11
|
+
|
|
12
|
+
// Không cho phép nhập khoảng trắng ở đầu chuỗi, nếu không nhập gì vẫn tính đúng.
|
|
13
|
+
export const noLeadingSpaceValidator = (): ValidatorFn => {
|
|
14
|
+
return (control: AbstractControl): ValidationErrors | null => {
|
|
15
|
+
const value = (control.value ?? "").toString();
|
|
16
|
+
if (!value) return null; // Không required thì để trống là hợp lệ
|
|
17
|
+
return value[0] === " " ? { leadingSpace: true } : null;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// Không cho phép nhập toàn khoảng trắng.
|
|
22
|
+
export const noWhitespaceValidator = (): ValidatorFn => {
|
|
23
|
+
return (control: AbstractControl): ValidationErrors | null => {
|
|
24
|
+
const isWhitespace = (control.value || "").toString().trim().length === 0;
|
|
25
|
+
return isWhitespace ? { whitespace: true } : null;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// Bắt đầu với một kí tự được chỉ định.
|
|
30
|
+
export const startsWithValidator = (prefix: string): ValidatorFn => {
|
|
31
|
+
return (control: AbstractControl): ValidationErrors | null => {
|
|
32
|
+
const value = control.value ?? "";
|
|
33
|
+
return value.startsWith(prefix)
|
|
34
|
+
? null
|
|
35
|
+
: { startsWith: { requiredPrefix: prefix } };
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// So sánh giá trị field A không được lớn hơn field B.
|
|
40
|
+
export const minMaxFieldValidator = (
|
|
41
|
+
minKey: string,
|
|
42
|
+
maxKey: string,
|
|
43
|
+
): ValidatorFn => {
|
|
44
|
+
// group: AbstractControl sẽ đại diện cho FormGroup tức là cha bao hàm các field.
|
|
45
|
+
return (group: AbstractControl): ValidationErrors | null => {
|
|
46
|
+
const min = group.get(minKey)?.value;
|
|
47
|
+
const max = group.get(maxKey)?.value;
|
|
48
|
+
if (min != null && max != null && min > max) {
|
|
49
|
+
return { minMaxInvalid: true };
|
|
50
|
+
}
|
|
51
|
+
return null;
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// Match giữa hai field (ví dụ: password/confirmPassword,...).
|
|
56
|
+
export const matchFieldsValidator = (fieldToMatch: string): ValidatorFn => {
|
|
57
|
+
return (control: AbstractControl): ValidationErrors | null => {
|
|
58
|
+
const parent = control.parent;
|
|
59
|
+
if (!parent) return null;
|
|
60
|
+
|
|
61
|
+
const target = parent.get(fieldToMatch);
|
|
62
|
+
if (!target?.value) return null;
|
|
63
|
+
|
|
64
|
+
// Nếu đang validate chính nó (ví dụ: confirmPassword)
|
|
65
|
+
const isMismatch = control.value !== target.value;
|
|
66
|
+
|
|
67
|
+
// Nếu trước đó password (target) đang bị lỗi matchField nhưng giờ đã khớp
|
|
68
|
+
if (!isMismatch && target.hasError("matchField")) {
|
|
69
|
+
const errors = { ...target.errors };
|
|
70
|
+
delete errors["matchField"];
|
|
71
|
+
|
|
72
|
+
// Nếu không còn lỗi nào khác, set null
|
|
73
|
+
target.setErrors(Object.keys(errors).length ? errors : null);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return isMismatch ? { matchField: true } : null;
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// Async: Check if username is taken (simulate API).
|
|
81
|
+
export const asyncUsernameValidator = (
|
|
82
|
+
takenUsernames: string[],
|
|
83
|
+
): AsyncValidatorFn => {
|
|
84
|
+
return (control: AbstractControl): Observable<ValidationErrors | null> => {
|
|
85
|
+
const value = control.value ?? "";
|
|
86
|
+
const isTaken = takenUsernames.includes(value);
|
|
87
|
+
return of(isTaken ? { usernameTaken: true } : null).pipe(delay(500));
|
|
88
|
+
};
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
// Không có ký tự đặc biệt.
|
|
92
|
+
export const noSpecialCharValidator = (): ValidatorFn => {
|
|
93
|
+
return (control: AbstractControl) => {
|
|
94
|
+
const valid = /^[a-zA-Z0-9]*$/.test(control.value);
|
|
95
|
+
return valid ? null : { noSpecialChar: true };
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
// Username không được nằm trong blacklist.
|
|
100
|
+
export const usernameBlacklistValidator = (
|
|
101
|
+
blacklist: string[],
|
|
102
|
+
): ValidatorFn => {
|
|
103
|
+
return (control: AbstractControl) => {
|
|
104
|
+
return blacklist.includes(control.value) ? { blacklisted: true } : null;
|
|
105
|
+
};
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
// Có thuộc mimetypes từ mảng mimetypes được chỉ định hay không?
|
|
109
|
+
export const acceptedMimeTypesValidator = (
|
|
110
|
+
acceptedMimeTypes: string[],
|
|
111
|
+
): ValidatorFn => {
|
|
112
|
+
return (control: AbstractControl): ValidationErrors | null => {
|
|
113
|
+
const value = control?.value;
|
|
114
|
+
|
|
115
|
+
if (value) {
|
|
116
|
+
let fileType = "";
|
|
117
|
+
if (value instanceof File) {
|
|
118
|
+
fileType = (value as File).type;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (isValidJSON(value as string)) {
|
|
122
|
+
const { contentType } = JSON.parse(value)[0] || {};
|
|
123
|
+
if (contentType) fileType = contentType;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (fileType) {
|
|
127
|
+
if (!acceptedMimeTypes.includes(fileType)) {
|
|
128
|
+
return {
|
|
129
|
+
acceptedMimeTypes: {
|
|
130
|
+
actualValue: fileType,
|
|
131
|
+
requiredValue: `Nằm trong ${acceptedMimeTypes}`,
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return null;
|
|
138
|
+
};
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
export const maxFileSizeInMBValidator = (maxSizeInMB: number): ValidatorFn => {
|
|
142
|
+
return (control: AbstractControl): ValidationErrors | null => {
|
|
143
|
+
const file = control?.value as File;
|
|
144
|
+
if (file) {
|
|
145
|
+
const fileSizeInMB = formatFileSize(file.size, FileSizeTypeEnum.MB);
|
|
146
|
+
|
|
147
|
+
if (fileSizeInMB > maxSizeInMB) {
|
|
148
|
+
return {
|
|
149
|
+
maxSizeInMB: {
|
|
150
|
+
actualValue: fileSizeInMB,
|
|
151
|
+
requiredValue: `Nhỏ hơn ${maxSizeInMB}`,
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return null;
|
|
157
|
+
};
|
|
158
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { PATTERN_ERROR_MESSAGES } from '../../consts/validators/pattern.const';
|
|
2
|
+
import { resolvePattern } from './pattern';
|
|
3
|
+
|
|
4
|
+
export const getDefaultPatternErrorMessage = (label: string, pattern: string | RegExp): string => {
|
|
5
|
+
let finalErrorMessage = '';
|
|
6
|
+
const resolvePatternResult = resolvePattern(pattern);
|
|
7
|
+
|
|
8
|
+
// if (resolvePatternResult?.type === 'unknown') {
|
|
9
|
+
// return finalErrorMessage; // Không phải pattern hợp lệ
|
|
10
|
+
// }
|
|
11
|
+
|
|
12
|
+
if (resolvePatternResult.type === 'regexObject') {
|
|
13
|
+
finalErrorMessage = `${label || 'Trường này'} không hợp lệ!`;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (resolvePatternResult.type === 'regexKey') {
|
|
17
|
+
const patternMess = PATTERN_ERROR_MESSAGES?.[pattern as string] ?? '';
|
|
18
|
+
finalErrorMessage = `${label || 'Trường này'} không hợp lệ! ${patternMess.replace(/%s/g, () => label)}`;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return finalErrorMessage;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export const getDefaultMinLengthErrorMessage = (label: string, errorValue: any): string => {
|
|
25
|
+
return `${label || 'Trường này'} cần tối thiểu ${errorValue.requiredLength} ký tự`;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export const getDefaultMaxLengthErrorMessage = (label: string, errorValue: any): string => {
|
|
29
|
+
return `${label || 'Trường này'} cần đât thiểu ${errorValue.requiredLength} ký tự`;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const getDefaultAcptMimeTypesErrorMessage = (typeString: string): string => {
|
|
33
|
+
return `Tập tin tải lên không được hỗ trợ. Vui lòng tải các tập tin có định dạng ${typeString}`;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const getDefaultMaxSizeInMBErrorMessage = (maxSizeInMB: number): string => {
|
|
37
|
+
return `Kích thước tập tin tải lên không được phép vượt quá ${maxSizeInMB} MB`;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export const getLeadingSpaceErrorMessage = (label: string): string => {
|
|
41
|
+
return `${label || 'Trường này'} không được bắt đầu bằng khoảng trắng`;
|
|
42
|
+
};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export * from './custom-validators';
|
|
2
|
-
export * from './pattern';
|
|
3
|
-
export * from './default-error-message';
|
|
1
|
+
export * from './custom-validators';
|
|
2
|
+
export * from './pattern';
|
|
3
|
+
export * from './default-error-message';
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { PATTERN_PRESETS } from '../../consts/validators/pattern.const';
|
|
2
|
+
|
|
3
|
+
export const resolvePattern = (
|
|
4
|
+
pattern: string | RegExp | undefined,
|
|
5
|
+
): {
|
|
6
|
+
type: 'regexKey' | 'regexString' | 'regexObject' | 'unknown';
|
|
7
|
+
value: string | RegExp;
|
|
8
|
+
} => {
|
|
9
|
+
if (!pattern) return { type: 'unknown', value: '' };
|
|
10
|
+
|
|
11
|
+
// Trường hợp là RegExp thực sự
|
|
12
|
+
if (pattern instanceof RegExp) return { type: 'regexObject', value: pattern };
|
|
13
|
+
|
|
14
|
+
// Trường hợp là tên preset (ưu tiên nếu trùng)
|
|
15
|
+
if (pattern in PATTERN_PRESETS) {
|
|
16
|
+
return { type: 'regexKey', value: PATTERN_PRESETS[pattern] };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
// Trường hợp là chuỗi regex dạng "^abc$"
|
|
21
|
+
return { type: 'regexString', value: new RegExp(pattern) };
|
|
22
|
+
} catch (err) {
|
|
23
|
+
console.warn(`Không thể parse pattern "${pattern}"`, err);
|
|
24
|
+
return { type: 'unknown', value: '' };
|
|
25
|
+
}
|
|
26
|
+
};
|