@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.
Files changed (298) hide show
  1. package/ng-package.json +15 -0
  2. package/package.json +20 -31
  3. package/src/lib/dynamic-form.component.html +121 -0
  4. package/src/lib/dynamic-form.component.scss +7 -0
  5. package/src/lib/dynamic-form.component.spec.ts +21 -0
  6. package/src/lib/dynamic-form.component.ts +237 -0
  7. package/src/lib/dynamic-form.service.spec.ts +16 -0
  8. package/src/lib/dynamic-form.service.ts +9 -0
  9. package/src/lib/shared/base/base-select.component.ts +183 -0
  10. package/src/lib/shared/base/base-value-accessor.ts +192 -0
  11. package/{lib/shared/components/component.d.ts → src/lib/shared/components/component.ts} +34 -14
  12. package/src/lib/shared/components/d-form-testing/d-form-testing.component.html +197 -0
  13. package/src/lib/shared/components/d-form-testing/d-form-testing.component.less +12 -0
  14. package/src/lib/shared/components/d-form-testing/d-form-testing.component.spec.ts +22 -0
  15. package/src/lib/shared/components/d-form-testing/d-form-testing.component.ts +197 -0
  16. package/src/lib/shared/components/dynamic-field/dynamic-field.component.html +218 -0
  17. package/src/lib/shared/components/dynamic-field/dynamic-field.component.scss +28 -0
  18. package/src/lib/shared/components/dynamic-field/dynamic-field.component.spec.ts +21 -0
  19. package/src/lib/shared/components/dynamic-field/dynamic-field.component.ts +67 -0
  20. package/src/lib/shared/components/field-user-config/field-user-config.component.html +38 -0
  21. package/src/lib/shared/components/field-user-config/field-user-config.component.scss +29 -0
  22. package/src/lib/shared/components/field-user-config/field-user-config.component.ts +33 -0
  23. package/src/lib/shared/components/fields/components/suffix/suffix.component.html +42 -0
  24. package/src/lib/shared/components/fields/components/suffix/suffix.component.scss +3 -0
  25. package/src/lib/shared/components/fields/components/suffix/suffix.component.spec.ts +22 -0
  26. package/src/lib/shared/components/fields/components/suffix/suffix.component.ts +32 -0
  27. package/src/lib/shared/components/fields/date/date-picker/date-picker.component.html +20 -0
  28. package/src/lib/shared/components/fields/date/date-picker/date-picker.component.scss +5 -0
  29. package/src/lib/shared/components/fields/date/date-picker/date-picker.component.spec.ts +21 -0
  30. package/src/lib/shared/components/fields/date/date-picker/date-picker.component.ts +148 -0
  31. package/src/lib/shared/components/fields/date/date-range/date-range.component.html +33 -0
  32. package/src/lib/shared/components/fields/date/date-range/date-range.component.scss +22 -0
  33. package/src/lib/shared/components/fields/date/date-range/date-range.component.spec.ts +21 -0
  34. package/src/lib/shared/components/fields/date/date-range/date-range.component.ts +133 -0
  35. package/src/lib/shared/components/fields/input/input-formatted/input-formatted.component.html +31 -0
  36. package/src/lib/shared/components/fields/input/input-formatted/input-formatted.component.scss +3 -0
  37. package/src/lib/shared/components/fields/input/input-formatted/input-formatted.component.spec.ts +21 -0
  38. package/src/lib/shared/components/fields/input/input-formatted/input-formatted.component.ts +148 -0
  39. package/src/lib/shared/components/fields/input/input-password/input-password.component.html +18 -0
  40. package/src/lib/shared/components/fields/input/input-password/input-password.component.scss +0 -0
  41. package/src/lib/shared/components/fields/input/input-password/input-password.component.spec.ts +21 -0
  42. package/src/lib/shared/components/fields/input/input-password/input-password.component.ts +56 -0
  43. package/src/lib/shared/components/fields/input/input-text/input-text.component.html +73 -0
  44. package/src/lib/shared/components/fields/input/input-text/input-text.component.scss +0 -0
  45. package/src/lib/shared/components/fields/input/input-text/input-text.component.spec.ts +21 -0
  46. package/src/lib/shared/components/fields/input/input-text/input-text.component.ts +98 -0
  47. package/src/lib/shared/components/fields/select/multi-select/multi-select.component.html +73 -0
  48. package/src/lib/shared/components/fields/select/multi-select/multi-select.component.scss +21 -0
  49. package/src/lib/shared/components/fields/select/multi-select/multi-select.component.spec.ts +21 -0
  50. package/src/lib/shared/components/fields/select/multi-select/multi-select.component.ts +212 -0
  51. package/src/lib/shared/components/fields/select/single-select/single-select.component.html +55 -0
  52. package/src/lib/shared/components/fields/select/single-select/single-select.component.scss +1 -0
  53. package/src/lib/shared/components/fields/select/single-select/single-select.component.spec.ts +21 -0
  54. package/src/lib/shared/components/fields/select/single-select/single-select.component.ts +217 -0
  55. package/src/lib/shared/components/fields/select/single-select/sl.component.old.txt +337 -0
  56. package/src/lib/shared/components/fields/select/single-select/sl.component.old2.txt +340 -0
  57. package/src/lib/shared/components/fields/select/tree-select/tree-select.component.html +38 -0
  58. package/src/lib/shared/components/fields/select/tree-select/tree-select.component.scss +1 -0
  59. package/src/lib/shared/components/fields/select/tree-select/tree-select.component.spec.ts +21 -0
  60. package/src/lib/shared/components/fields/select/tree-select/tree-select.component.ts +401 -0
  61. package/src/lib/shared/components/fields/textarea/textarea.component.html +29 -0
  62. package/src/lib/shared/components/fields/textarea/textarea.component.scss +17 -0
  63. package/src/lib/shared/components/fields/textarea/textarea.component.spec.ts +21 -0
  64. package/src/lib/shared/components/fields/textarea/textarea.component.ts +76 -0
  65. package/src/lib/shared/components/fields/upload/avatar-upload/avatar-upload.component.html +30 -0
  66. package/src/lib/shared/components/fields/upload/avatar-upload/avatar-upload.component.scss +22 -0
  67. package/src/lib/shared/components/fields/upload/avatar-upload/avatar-upload.component.spec.ts +22 -0
  68. package/src/lib/shared/components/fields/upload/avatar-upload/avatar-upload.component.ts +217 -0
  69. package/src/lib/shared/components/fields/upload/multi-file-upload/multi-file-upload.component.html +101 -0
  70. package/src/lib/shared/components/fields/upload/multi-file-upload/multi-file-upload.component.scss +117 -0
  71. package/src/lib/shared/components/fields/upload/multi-file-upload/multi-file-upload.component.spec.ts +22 -0
  72. package/src/lib/shared/components/fields/upload/multi-file-upload/multi-file-upload.component.ts +480 -0
  73. package/src/lib/shared/components/fields/upload/multi-image-upload/multi-image-upload.component.html +82 -0
  74. package/src/lib/shared/components/fields/upload/multi-image-upload/multi-image-upload.component.less +80 -0
  75. package/src/lib/shared/components/fields/upload/multi-image-upload/multi-image-upload.component.spec.ts +22 -0
  76. package/src/lib/shared/components/fields/upload/multi-image-upload/multi-image-upload.component.ts +288 -0
  77. package/src/lib/shared/components/fields/upload/single-file-upload/single-file-upload.component.html +104 -0
  78. package/src/lib/shared/components/fields/upload/single-file-upload/single-file-upload.component.scss +37 -0
  79. package/src/lib/shared/components/fields/upload/single-file-upload/single-file-upload.component.spec.ts +21 -0
  80. package/src/lib/shared/components/fields/upload/single-file-upload/single-file-upload.component.ts +317 -0
  81. package/src/lib/shared/config/dynamic-form.config.ts +11 -0
  82. package/src/lib/shared/consts/common/common.const.ts +5 -0
  83. package/src/lib/shared/consts/common/field-dependency.const.ts +1 -0
  84. package/src/lib/shared/consts/common/file.const.ts +130 -0
  85. package/{lib/shared/consts/common/index.d.ts → src/lib/shared/consts/common/index.ts} +4 -4
  86. package/src/lib/shared/consts/common/sample-test.const.ts +1648 -0
  87. package/src/lib/shared/consts/common/standalone-import.const.ts +51 -0
  88. package/src/lib/shared/consts/controls/control.const.ts +11 -0
  89. package/{lib/shared/consts/controls/index.d.ts → src/lib/shared/consts/controls/index.ts} +1 -1
  90. package/src/lib/shared/consts/fields/date-field.const.ts +12 -0
  91. package/src/lib/shared/consts/fields/field.const.ts +23 -0
  92. package/{lib/shared/consts/fields/index.d.ts → src/lib/shared/consts/fields/index.ts} +5 -5
  93. package/src/lib/shared/consts/fields/input-field.const.ts +4 -0
  94. package/src/lib/shared/consts/fields/input-formatted-field.const.ts +35 -0
  95. package/src/lib/shared/consts/fields/radio-field.const.ts +31 -0
  96. package/src/lib/shared/consts/fields/select-field.const.ts +7 -0
  97. package/src/lib/shared/consts/fields/upload-field.const.ts +1 -0
  98. package/src/lib/shared/consts/validators/index.ts +0 -0
  99. package/src/lib/shared/consts/validators/pattern.const.ts +117 -0
  100. package/src/lib/shared/enums/common/component-ui.enum.ts +8 -0
  101. package/src/lib/shared/enums/common/field-dependency.enum.ts +10 -0
  102. package/src/lib/shared/enums/common/file-size-type.enum.ts +6 -0
  103. package/{lib/shared/enums/common/index.d.ts → src/lib/shared/enums/common/index.ts} +4 -4
  104. package/src/lib/shared/enums/common/output-value-type.enum.ts +22 -0
  105. package/src/lib/shared/enums/common/view-mode.enum.ts +4 -0
  106. package/src/lib/shared/enums/controls/control.enum.ts +12 -0
  107. package/{lib/shared/enums/controls/index.d.ts → src/lib/shared/enums/controls/index.ts} +1 -1
  108. package/src/lib/shared/enums/fields/date-field.enum.ts +4 -0
  109. package/src/lib/shared/enums/fields/field.enum.ts +21 -0
  110. package/{lib/shared/enums/fields/index.d.ts → src/lib/shared/enums/fields/index.ts} +6 -6
  111. package/src/lib/shared/enums/fields/input-field.enum.ts +6 -0
  112. package/src/lib/shared/enums/fields/input-formatted-field.enum.ts +6 -0
  113. package/src/lib/shared/enums/fields/select-field.enum.ts +4 -0
  114. package/src/lib/shared/enums/fields/upload-field.enum.ts +5 -0
  115. package/src/lib/shared/enums/form/dynamic-form.enum.ts +6 -0
  116. package/{lib/shared/enums/form/index.d.ts → src/lib/shared/enums/form/index.ts} +1 -1
  117. package/{lib/shared/enums/validators/index.d.ts → src/lib/shared/enums/validators/index.ts} +2 -2
  118. package/src/lib/shared/enums/validators/regex.enum.ts +47 -0
  119. package/src/lib/shared/enums/validators/validators.enum.ts +17 -0
  120. package/src/lib/shared/factories/auto-numeric-options.factory.ts +14 -0
  121. package/src/lib/shared/models/common/api-config.model.ts +7 -0
  122. package/src/lib/shared/models/common/api-response.model.ts +20 -0
  123. package/src/lib/shared/models/common/common.model.ts +39 -0
  124. package/src/lib/shared/models/common/field-dependency.model.ts +27 -0
  125. package/src/lib/shared/models/common/file-config.model.ts +22 -0
  126. package/{lib/shared/models/common/index.d.ts → src/lib/shared/models/common/index.ts} +7 -7
  127. package/src/lib/shared/models/common/obj-field.model.ts +68 -0
  128. package/src/lib/shared/models/common/object-storage.model.ts +15 -0
  129. package/{lib/shared/models/common/output-value-type.model.d.ts → src/lib/shared/models/common/output-value-type.model.ts} +4 -4
  130. package/src/lib/shared/models/common/table-models.ts +16 -0
  131. package/{lib/shared/models/common/view-mode.model.d.ts → src/lib/shared/models/common/view-mode.model.ts} +1 -1
  132. package/{lib/shared/models/controls/control.model.d.ts → src/lib/shared/models/controls/control.model.ts} +3 -2
  133. package/{lib/shared/models/controls/index.d.ts → src/lib/shared/models/controls/index.ts} +1 -1
  134. package/src/lib/shared/models/fields/date/base-date-field.model.ts +52 -0
  135. package/src/lib/shared/models/fields/date/date-picker-field.model.ts +43 -0
  136. package/src/lib/shared/models/fields/date/date-range-field.model.ts +52 -0
  137. package/{lib/shared/models/fields/date/index.d.ts → src/lib/shared/models/fields/date/index.ts} +3 -3
  138. package/src/lib/shared/models/fields/field-grid.model.ts +10 -0
  139. package/src/lib/shared/models/fields/field-group.model.ts +50 -0
  140. package/src/lib/shared/models/fields/field-user-config.model.ts +8 -0
  141. package/src/lib/shared/models/fields/field.model.ts +184 -0
  142. package/{lib/shared/models/fields/index.d.ts → src/lib/shared/models/fields/index.ts} +2 -2
  143. package/{lib/shared/models/fields/input/index.d.ts → src/lib/shared/models/fields/input/index.ts} +4 -4
  144. package/src/lib/shared/models/fields/input/input-field.model.ts +111 -0
  145. package/src/lib/shared/models/fields/input/input-formatted-field.model.ts +38 -0
  146. package/src/lib/shared/models/fields/input/input-password-field.model.ts +38 -0
  147. package/src/lib/shared/models/fields/input/input-text-field.model.ts +26 -0
  148. package/src/lib/shared/models/fields/radio/index.ts +0 -0
  149. package/src/lib/shared/models/fields/radio/radio-base-field.model.ts +84 -0
  150. package/src/lib/shared/models/fields/select/index.ts +0 -0
  151. package/src/lib/shared/models/fields/select/multi-select-field.model.ts +46 -0
  152. package/src/lib/shared/models/fields/select/select-base-field.model.ts +125 -0
  153. package/src/lib/shared/models/fields/select/single-select-field.model.ts +43 -0
  154. package/src/lib/shared/models/fields/select/tree-select-field.model.ts +44 -0
  155. package/src/lib/shared/models/fields/textarea/index.ts +0 -0
  156. package/src/lib/shared/models/fields/textarea/textarea-field.model.ts +97 -0
  157. package/src/lib/shared/models/fields/upload/avatar-upload-field.model.ts +33 -0
  158. package/src/lib/shared/models/fields/upload/base-upload-field.model.ts +105 -0
  159. package/{lib/shared/models/fields/upload/index.d.ts → src/lib/shared/models/fields/upload/index.ts} +5 -5
  160. package/src/lib/shared/models/fields/upload/multi-file-upload-field.model.ts +34 -0
  161. package/src/lib/shared/models/fields/upload/multi-image-upload-field.model.ts +42 -0
  162. package/src/lib/shared/models/fields/upload/single-file-upload-field.model.ts +33 -0
  163. package/{lib/shared/models/form/dynamic-form.model.d.ts → src/lib/shared/models/form/dynamic-form.model.ts} +1 -1
  164. package/{lib/shared/models/form/index.d.ts → src/lib/shared/models/form/index.ts} +1 -1
  165. package/src/lib/shared/models/validations/index.ts +1 -0
  166. package/src/lib/shared/models/validations/validations.model.ts +14 -0
  167. package/src/lib/shared/services/dynamic-form.service.spec.ts +16 -0
  168. package/src/lib/shared/services/dynamic-form.service.ts +251 -0
  169. package/src/lib/shared/services/field.service.spec.ts +16 -0
  170. package/src/lib/shared/services/field.service.ts +152 -0
  171. package/src/lib/shared/services/file.service.spec.ts +16 -0
  172. package/src/lib/shared/services/file.service.ts +53 -0
  173. package/src/lib/shared/services/form-config.service.ts +53 -0
  174. package/src/lib/shared/services/form-data.service.spec.ts +16 -0
  175. package/src/lib/shared/services/form-data.service.ts +114 -0
  176. package/src/lib/shared/services/form-utils.ts +52 -0
  177. package/src/lib/shared/services/suffix.service.spec.ts +16 -0
  178. package/src/lib/shared/services/suffix.service.ts +16 -0
  179. package/src/lib/shared/services/validation.service.spec.ts +16 -0
  180. package/src/lib/shared/services/validation.service.ts +38 -0
  181. package/src/lib/shared/utils/common/common.utils.ts +52 -0
  182. package/{lib/shared/utils/common/index.d.ts → src/lib/shared/utils/common/index.ts} +1 -1
  183. package/src/lib/shared/utils/date/date.utils.ts +129 -0
  184. package/{lib/shared/utils/date/index.d.ts → src/lib/shared/utils/date/index.ts} +1 -1
  185. package/src/lib/shared/utils/dynamic-field/dynamic-field.ts +0 -0
  186. package/src/lib/shared/utils/dynamic-field/index.ts +0 -0
  187. package/src/lib/shared/utils/dynamic-form/dynamic-form.ts +114 -0
  188. package/src/lib/shared/utils/dynamic-form/index.ts +0 -0
  189. package/src/lib/shared/utils/fields/fields.ts +78 -0
  190. package/{lib/shared/utils/fields/index.d.ts → src/lib/shared/utils/fields/index.ts} +1 -1
  191. package/src/lib/shared/utils/file/file.utils.ts +37 -0
  192. package/{lib/shared/utils/file/index.d.ts → src/lib/shared/utils/file/index.ts} +1 -1
  193. package/src/lib/shared/utils/validators/custom-validators.ts +158 -0
  194. package/src/lib/shared/utils/validators/default-error-message.ts +42 -0
  195. package/{lib/shared/utils/validators/index.d.ts → src/lib/shared/utils/validators/index.ts} +3 -3
  196. package/src/lib/shared/utils/validators/pattern.ts +26 -0
  197. package/{public-api.d.ts → src/public-api.ts} +68 -28
  198. package/tsconfig.lib.json +15 -0
  199. package/tsconfig.lib.prod.json +11 -0
  200. package/tsconfig.spec.json +15 -0
  201. package/fesm2022/ldquocc-dynamic-form.mjs +0 -4938
  202. package/fesm2022/ldquocc-dynamic-form.mjs.map +0 -1
  203. package/index.d.ts +0 -5
  204. package/lib/dynamic-form.component.d.ts +0 -67
  205. package/lib/shared/base/base-select.component.d.ts +0 -19
  206. package/lib/shared/base/base-value-accessor.d.ts +0 -69
  207. package/lib/shared/components/dynamic-field/dynamic-field.component.d.ts +0 -23
  208. package/lib/shared/components/field-user-config/field-user-config.component.d.ts +0 -10
  209. package/lib/shared/components/fields/components/suffix/suffix.component.d.ts +0 -12
  210. package/lib/shared/components/fields/date/date-picker/date-picker.component.d.ts +0 -17
  211. package/lib/shared/components/fields/date/date-range/date-range.component.d.ts +0 -17
  212. package/lib/shared/components/fields/input/input-formatted/input-formatted.component.d.ts +0 -20
  213. package/lib/shared/components/fields/input/input-password/input-password.component.d.ts +0 -12
  214. package/lib/shared/components/fields/input/input-text/input-text.component.d.ts +0 -14
  215. package/lib/shared/components/fields/select/multi-select/multi-select.component.d.ts +0 -21
  216. package/lib/shared/components/fields/select/single-select/single-select.component.d.ts +0 -21
  217. package/lib/shared/components/fields/select/tree-select/tree-select.component.d.ts +0 -28
  218. package/lib/shared/components/fields/textarea/textarea.component.d.ts +0 -14
  219. package/lib/shared/components/fields/upload/avatar-upload/avatar-upload.component.d.ts +0 -25
  220. package/lib/shared/components/fields/upload/multi-file-upload/multi-file-upload.component.d.ts +0 -76
  221. package/lib/shared/components/fields/upload/multi-image-upload/multi-image-upload.component.d.ts +0 -27
  222. package/lib/shared/components/fields/upload/single-file-upload/single-file-upload.component.d.ts +0 -29
  223. package/lib/shared/config/dynamic-form.config.d.ts +0 -9
  224. package/lib/shared/consts/common/common.const.d.ts +0 -5
  225. package/lib/shared/consts/common/field-dependency.const.d.ts +0 -1
  226. package/lib/shared/consts/common/file.const.d.ts +0 -148
  227. package/lib/shared/consts/common/standalone-import.const.d.ts +0 -7
  228. package/lib/shared/consts/controls/control.const.d.ts +0 -1
  229. package/lib/shared/consts/fields/date-field.const.d.ts +0 -7
  230. package/lib/shared/consts/fields/field.const.d.ts +0 -4
  231. package/lib/shared/consts/fields/input-field.const.d.ts +0 -4
  232. package/lib/shared/consts/fields/input-formatted-field.const.d.ts +0 -4
  233. package/lib/shared/consts/fields/radio-field.const.d.ts +0 -10
  234. package/lib/shared/consts/fields/select-field.const.d.ts +0 -6
  235. package/lib/shared/consts/fields/upload-field.const.d.ts +0 -1
  236. package/lib/shared/consts/validators/pattern.const.d.ts +0 -2
  237. package/lib/shared/enums/common/field-dependency.enum.d.ts +0 -9
  238. package/lib/shared/enums/common/file-size-type.enum.d.ts +0 -6
  239. package/lib/shared/enums/common/output-value-type.enum.d.ts +0 -19
  240. package/lib/shared/enums/common/view-mode.enum.d.ts +0 -4
  241. package/lib/shared/enums/controls/control.enum.d.ts +0 -11
  242. package/lib/shared/enums/fields/date-field.enum.d.ts +0 -4
  243. package/lib/shared/enums/fields/field.enum.d.ts +0 -20
  244. package/lib/shared/enums/fields/input-field.enum.d.ts +0 -6
  245. package/lib/shared/enums/fields/input-formatted-field.enum.d.ts +0 -6
  246. package/lib/shared/enums/fields/select-field.enum.d.ts +0 -4
  247. package/lib/shared/enums/fields/upload-field.enum.d.ts +0 -5
  248. package/lib/shared/enums/form/dynamic-form.enum.d.ts +0 -6
  249. package/lib/shared/enums/validators/regex.enum.d.ts +0 -38
  250. package/lib/shared/enums/validators/validators.enum.d.ts +0 -14
  251. package/lib/shared/factories/auto-numeric-options.factory.d.ts +0 -5
  252. package/lib/shared/models/common/api-config.model.d.ts +0 -4
  253. package/lib/shared/models/common/api-response.model.d.ts +0 -19
  254. package/lib/shared/models/common/common.model.d.ts +0 -29
  255. package/lib/shared/models/common/field-dependency.model.d.ts +0 -16
  256. package/lib/shared/models/common/file-config.model.d.ts +0 -11
  257. package/lib/shared/models/common/obj-field.model.d.ts +0 -59
  258. package/lib/shared/models/common/object-storage.model.d.ts +0 -15
  259. package/lib/shared/models/common/table-models.d.ts +0 -15
  260. package/lib/shared/models/fields/date/base-date-field.model.d.ts +0 -19
  261. package/lib/shared/models/fields/date/date-picker-field.model.d.ts +0 -12
  262. package/lib/shared/models/fields/date/date-range-field.model.d.ts +0 -12
  263. package/lib/shared/models/fields/field-grid.model.d.ts +0 -9
  264. package/lib/shared/models/fields/field-group.model.d.ts +0 -21
  265. package/lib/shared/models/fields/field-user-config.model.d.ts +0 -3
  266. package/lib/shared/models/fields/field.model.d.ts +0 -57
  267. package/lib/shared/models/fields/input/input-field.model.d.ts +0 -31
  268. package/lib/shared/models/fields/input/input-formatted-field.model.d.ts +0 -13
  269. package/lib/shared/models/fields/input/input-password-field.model.d.ts +0 -11
  270. package/lib/shared/models/fields/input/input-text-field.model.d.ts +0 -9
  271. package/lib/shared/models/fields/radio/radio-base-field.model.d.ts +0 -26
  272. package/lib/shared/models/fields/select/multi-select-field.model.d.ts +0 -11
  273. package/lib/shared/models/fields/select/select-base-field.model.d.ts +0 -40
  274. package/lib/shared/models/fields/select/single-select-field.model.d.ts +0 -9
  275. package/lib/shared/models/fields/select/tree-select-field.model.d.ts +0 -10
  276. package/lib/shared/models/fields/textarea/textarea-field.model.d.ts +0 -20
  277. package/lib/shared/models/fields/upload/avatar-upload-field.model.d.ts +0 -9
  278. package/lib/shared/models/fields/upload/base-upload-field.model.d.ts +0 -25
  279. package/lib/shared/models/fields/upload/multi-file-upload-field.model.d.ts +0 -10
  280. package/lib/shared/models/fields/upload/multi-image-upload-field.model.d.ts +0 -12
  281. package/lib/shared/models/fields/upload/single-file-upload-field.model.d.ts +0 -11
  282. package/lib/shared/services/dynamic-form.service.d.ts +0 -48
  283. package/lib/shared/services/field.service.d.ts +0 -41
  284. package/lib/shared/services/file.service.d.ts +0 -20
  285. package/lib/shared/services/form-config.service.d.ts +0 -10
  286. package/lib/shared/services/form-data.service.d.ts +0 -53
  287. package/lib/shared/services/form-utils.d.ts +0 -5
  288. package/lib/shared/services/suffix.service.d.ts +0 -12
  289. package/lib/shared/services/validation.service.d.ts +0 -10
  290. package/lib/shared/utils/common/common.utils.d.ts +0 -7
  291. package/lib/shared/utils/date/date.utils.d.ts +0 -6
  292. package/lib/shared/utils/dynamic-form/dynamic-form.d.ts +0 -11
  293. package/lib/shared/utils/fields/fields.d.ts +0 -20
  294. package/lib/shared/utils/file/file.utils.d.ts +0 -4
  295. package/lib/shared/utils/validators/custom-validators.d.ts +0 -11
  296. package/lib/shared/utils/validators/default-error-message.d.ts +0 -6
  297. package/lib/shared/utils/validators/pattern.d.ts +0 -4
  298. /package/{styles → src/lib/shared/styles}/_common-form.scss +0 -0
@@ -0,0 +1,15 @@
1
+ {
2
+ "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
3
+ "dest": "../../dist/dynamic-form",
4
+ "lib": {
5
+ "entryFile": "src/public-api.ts"
6
+ },
7
+ "assets": [
8
+ {
9
+ "glob": "**/*.scss",
10
+ "input": "src/lib/shared/styles",
11
+ "output": "/styles"
12
+ }
13
+ ],
14
+ "allowedNonPeerDependencies": ["lodash-es", "date-fns", "@zeedhi/autonumeric"]
15
+ }
package/package.json CHANGED
@@ -1,31 +1,20 @@
1
- {
2
- "name": "@ldquocc/dynamic-form",
3
- "version": "0.0.8",
4
- "peerDependencies": {
5
- "@angular/common": "^19.2.0",
6
- "@angular/core": "^19.2.0",
7
- "@angular/forms": "^19.2.0",
8
- "@angular/router": "^19.2.0",
9
- "ng-zorro-antd": "^19.2.1",
10
- "ngx-currency": "^19.0.0",
11
- "rxjs": "~7.8.0"
12
- },
13
- "dependencies": {
14
- "tslib": "^2.3.0",
15
- "date-fns": "^2.30.0",
16
- "lodash-es": "^4.17.21",
17
- "@zeedhi/autonumeric": "^4.6.5"
18
- },
19
- "sideEffects": false,
20
- "module": "fesm2022/ldquocc-dynamic-form.mjs",
21
- "typings": "index.d.ts",
22
- "exports": {
23
- "./package.json": {
24
- "default": "./package.json"
25
- },
26
- ".": {
27
- "types": "./index.d.ts",
28
- "default": "./fesm2022/ldquocc-dynamic-form.mjs"
29
- }
30
- }
31
- }
1
+ {
2
+ "name": "@ldquocc/dynamic-form",
3
+ "version": "0.0.9",
4
+ "peerDependencies": {
5
+ "@angular/common": "^19.2.0",
6
+ "@angular/core": "^19.2.0",
7
+ "@angular/forms": "^19.2.0",
8
+ "@angular/router": "^19.2.0",
9
+ "ng-zorro-antd": "^19.2.1",
10
+ "ngx-currency": "^19.0.0",
11
+ "rxjs": "~7.8.0"
12
+ },
13
+ "dependencies": {
14
+ "tslib": "^2.3.0",
15
+ "date-fns": "^2.30.0",
16
+ "lodash-es": "^4.17.21",
17
+ "@zeedhi/autonumeric": "^4.6.5"
18
+ },
19
+ "sideEffects": false
20
+ }
@@ -0,0 +1,121 @@
1
+ @if (formGroup) {
2
+ <form [formGroup]="formGroup">
3
+ @if (oldFields().length > 0) {
4
+ <!-- 1. Nếu không có groups thì render từ fields -->
5
+ @if (!fieldGroups() || fieldGroups().length === 0) {
6
+ <div nz-row [nzGutter]="[12, 0]">
7
+ @for (field of oldFields(); track field?.id) {
8
+ @if (!isHiddenField(field)) {
9
+ <div
10
+ nz-col
11
+ [nzXXl]="field?.grid?.xxl ?? null"
12
+ [nzXl]="field?.grid?.xl ?? null"
13
+ [nzLg]="field?.grid?.lg ?? null"
14
+ [nzMd]="field?.grid?.md ?? null"
15
+ [nzSm]="field?.grid?.sm ?? null"
16
+ [nzXs]="field?.grid?.xs ?? null"
17
+ [nzFlex]="
18
+ !field?.grid?.xs &&
19
+ !field?.grid?.sm &&
20
+ !field?.grid?.md &&
21
+ !field?.grid?.lg &&
22
+ !field?.grid?.xl &&
23
+ !field?.grid?.xxl
24
+ ? '1 0 0'
25
+ : null
26
+ "
27
+ class="field"
28
+ >
29
+ <app-dynamic-field
30
+ [field]="field"
31
+ [fields]="oldFields()"
32
+ [mode]="mode()"
33
+ [control]="formGroup.get(field.key)!"
34
+ [formGroup]="formGroup"
35
+ [formId]="formId"
36
+ />
37
+ </div>
38
+ }
39
+ }
40
+ </div>
41
+ }
42
+
43
+ <!-- 2. Nếu có groups thì render từ groupedFields -->
44
+ @if (fieldGroups() && fieldGroups().length > 0) {
45
+ <!-- Các field noGroup -->
46
+ @if (groupedFields().noGroup.length > 0 && displayNonGroupedFields()) {
47
+ <div nz-row [nzGutter]="24">
48
+ @for (field of groupedFields().noGroup; track field?.id) {
49
+ @if (!isHiddenField(field)) {
50
+ <div
51
+ nz-col
52
+ [nzXXl]="field?.grid?.xxl ?? null"
53
+ [nzXl]="field?.grid?.xl ?? null"
54
+ [nzLg]="field?.grid?.lg ?? null"
55
+ [nzMd]="field?.grid?.md ?? null"
56
+ [nzSm]="field?.grid?.sm ?? null"
57
+ [nzXs]="field?.grid?.xs ?? null"
58
+ class="field"
59
+ >
60
+ <app-dynamic-field
61
+ [field]="field"
62
+ [fields]="oldFields()"
63
+ [mode]="mode()"
64
+ [control]="formGroup.get(field.key)!"
65
+ [formGroup]="formGroup"
66
+ [formId]="formId"
67
+ />
68
+ </div>
69
+ }
70
+ }
71
+ </div>
72
+ }
73
+
74
+ <!-- Group các field -->
75
+ @if (groupedFields().grouped.length > 0) {
76
+ <nz-collapse [nzBordered]="false">
77
+ @for (
78
+ groupField of groupedFields().grouped;
79
+ track groupField.group.code
80
+ ) {
81
+ <nz-collapse-panel
82
+ [nzHeader]="groupField.group.template ?? groupField.group.label"
83
+ [nzActive]="groupField.group.isActive"
84
+ [nzShowArrow]="groupField.group.showArrow"
85
+ [nzDisabled]="groupField.group.disabled"
86
+ [style]="collapseCustomStyle"
87
+ class="mt-sm"
88
+ >
89
+ <div nz-row [nzGutter]="24">
90
+ @for (field of groupField.fields; track field?.id) {
91
+ @if (!isHiddenField(field)) {
92
+ <div
93
+ nz-col
94
+ [nzXXl]="field?.grid?.xxl ?? null"
95
+ [nzXl]="field?.grid?.xl ?? null"
96
+ [nzLg]="field?.grid?.lg ?? null"
97
+ [nzMd]="field?.grid?.md ?? null"
98
+ [nzSm]="field?.grid?.sm ?? null"
99
+ [nzXs]="field?.grid?.xs ?? null"
100
+ class="field"
101
+ >
102
+ <app-dynamic-field
103
+ [field]="field"
104
+ [fields]="oldFields()"
105
+ [mode]="mode()"
106
+ [control]="formGroup.get(field.key)!"
107
+ [formGroup]="formGroup"
108
+ [formId]="formId"
109
+ />
110
+ </div>
111
+ }
112
+ }
113
+ </div>
114
+ </nz-collapse-panel>
115
+ }
116
+ </nz-collapse>
117
+ }
118
+ }
119
+ }
120
+ </form>
121
+ }
@@ -0,0 +1,7 @@
1
+ :host ::ng-deep {
2
+ .ant-collapse .ant-collapse-item-disabled > .ant-collapse-header,
3
+ .ant-collapse .ant-collapse-item-disabled > .ant-collapse-header > .arrow {
4
+ color: #575757;
5
+ cursor: not-allowed;
6
+ }
7
+ }
@@ -0,0 +1,21 @@
1
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
2
+
3
+ import { DynamicFormComponent } from './dynamic-form.component';
4
+
5
+ describe('DynamicFormComponent', () => {
6
+ let component: DynamicFormComponent;
7
+ let fixture: ComponentFixture<DynamicFormComponent>;
8
+
9
+ beforeEach(() => {
10
+ TestBed.configureTestingModule({
11
+ imports: [DynamicFormComponent],
12
+ });
13
+ fixture = TestBed.createComponent(DynamicFormComponent);
14
+ component = fixture.componentInstance;
15
+ fixture.detectChanges();
16
+ });
17
+
18
+ it('should create', () => {
19
+ expect(component).toBeTruthy();
20
+ });
21
+ });
@@ -0,0 +1,237 @@
1
+ import { BaseField } from "./shared/models/fields/field.model";
2
+ import {
3
+ Component,
4
+ computed,
5
+ effect,
6
+ EventEmitter,
7
+ inject,
8
+ input,
9
+ Input,
10
+ output,
11
+ Output,
12
+ signal,
13
+ untracked,
14
+ } from "@angular/core";
15
+ import { AbstractControl, FormGroup } from "@angular/forms";
16
+ import { buildForm } from "./shared/utils/dynamic-form/dynamic-form";
17
+ import { FieldTypeEnum } from "./shared/enums/fields";
18
+ import { NzFormModule } from "ng-zorro-antd/form";
19
+ import { FormModeEnum } from "./shared/enums/form";
20
+ import { FormMode } from "./shared/models/form";
21
+ import { DynamicFormService } from "./shared/services/dynamic-form.service";
22
+
23
+ import { FORM_MODULES } from "./shared/consts/common";
24
+ import { InputPasswordField } from "./shared/models/fields/input";
25
+ import { clone, isEqual } from "lodash-es";
26
+ import {
27
+ FieldGroup,
28
+ GroupedFields,
29
+ } from "./shared/models/fields/field-group.model";
30
+ import { buildGroupedFields } from "./shared/utils/fields";
31
+ import { DynamicFieldComponent } from "./shared/components/dynamic-field/dynamic-field.component";
32
+ import { debounceTime, distinctUntilChanged, Subject, takeUntil } from "rxjs";
33
+
34
+ import { NzGridModule } from "ng-zorro-antd/grid";
35
+ import { NzCollapseModule } from "ng-zorro-antd/collapse";
36
+ import { FormDataService } from "./shared/services/form-data.service";
37
+
38
+ const ZORRO_MODULES = [NzFormModule, NzGridModule, NzCollapseModule];
39
+ const COMPONENTS = [DynamicFieldComponent];
40
+
41
+ @Component({
42
+ selector: "app-dynamic-forms",
43
+ standalone: true,
44
+ imports: [...FORM_MODULES, ...ZORRO_MODULES, ...COMPONENTS],
45
+ templateUrl: "./dynamic-form.component.html",
46
+ styleUrls: ["./dynamic-form.component.scss"],
47
+ })
48
+ export class DynamicFormComponent {
49
+ // Id phân biệt form.
50
+ @Input() formId!: string;
51
+
52
+ // ===================== NGOÀI LUỒNG LOGIC ======================== //
53
+ // Debounce time for form value changes (ms)
54
+ // Default: 300ms
55
+ // Thời gian trễ cho sự thay đổi giá trị của form (ms)
56
+ @Input() valueChangesDebounceTime = 300;
57
+
58
+ // Emit form value changes
59
+ @Output() readonly formValueChanges = new EventEmitter<
60
+ Record<string, unknown>
61
+ >();
62
+
63
+ // Subject for cleanup subscriptions
64
+ private readonly destroy$ = new Subject<void>();
65
+ // ====================================================== //
66
+
67
+ // Input.
68
+ // Mảng fields mới.
69
+ readonly fields = input.required<BaseField<any>[]>();
70
+ // Signal model data.
71
+ readonly model = input<Record<string, any>>({});
72
+ // Signal form mode.
73
+ readonly mode = input<FormMode>(FormModeEnum.Add);
74
+ // Input signal groupFields.
75
+ readonly fieldGroups = input<FieldGroup[]>([]);
76
+
77
+ // Cho phép show nhưng field không thuộc group nào
78
+ readonly displayNonGroupedFields = input(true);
79
+
80
+ // Output.
81
+ readonly formSubmit = output<Record<string, unknown>>();
82
+ readonly formInvalid = output<BaseField<any>[]>();
83
+
84
+ // Mảng field cũ.
85
+ protected readonly oldFields = signal<BaseField<any>[]>([]);
86
+
87
+ // Check mode là add hay edit.
88
+ readonly isAddOrEdit = computed(() =>
89
+ [FormModeEnum.Add, FormModeEnum.Edit].includes(this.mode() as FormModeEnum),
90
+ );
91
+
92
+ // FormState check sự thay đổi giữa mode, newFields, model chỉ chạy 1 lần dù 3 cái đều đổi.
93
+ readonly formState = computed(() => ({
94
+ mode: this.mode(),
95
+ newFields: this.fields(),
96
+ model: this.model(),
97
+ }));
98
+
99
+ // Computed chạy lại khi fields hoặc groupFields thay đổi.
100
+ groupedFields = computed<GroupedFields>(() => {
101
+ const groups = this.fieldGroups();
102
+ const groupedFields = buildGroupedFields(this.oldFields(), groups);
103
+ return groupedFields;
104
+ });
105
+
106
+ protected readonly formModeEnum = FormModeEnum;
107
+ protected readonly fieldTypeEnum = FieldTypeEnum;
108
+
109
+ // Kiểm tra có cần ẩn field không?
110
+ isHiddenField = (field: BaseField<any>): boolean => {
111
+ const { isHidden } = field;
112
+ if (isHidden) return true;
113
+ if (field instanceof InputPasswordField) {
114
+ if (
115
+ this.mode() === FormModeEnum.Edit ||
116
+ this.mode() === FormModeEnum.View
117
+ ) {
118
+ return true;
119
+ }
120
+ }
121
+ return false;
122
+ };
123
+
124
+ // Form group cho reactive form.
125
+ formGroup!: FormGroup;
126
+
127
+ readonly formDataService = inject(FormDataService);
128
+ constructor(private dFormService: DynamicFormService) {
129
+ // Trigger form từ submit bên ngoài.
130
+ effect(() => {
131
+ this.dFormService.submitTrigger();
132
+ if (untracked(() => this.isAddOrEdit())) {
133
+ const submit = this.dFormService.submitTrigger();
134
+
135
+ if (submit?.formId === this.formId) {
136
+ this.handleSubmit();
137
+ this.dFormService.triggerSubmit(null);
138
+ }
139
+ }
140
+ });
141
+ }
142
+
143
+ ngOnDestroy(): void {
144
+ // Cleanup subscriptions
145
+ // Dọn dẹp các subscription
146
+ this.destroy$.next();
147
+ this.destroy$.complete();
148
+
149
+ // Loại bỏ output data khi dynamic form destroy component.
150
+ this.formDataService.clear(this.formId);
151
+ }
152
+
153
+ // Đồng bộ fields sang dynamic-form service mỗi lần fields signal change.
154
+ syncFieldsEffect = effect(() => {
155
+ this.dFormService.setFields(this.fields());
156
+ this.formDataService.clear(this.formId);
157
+ });
158
+
159
+ /**
160
+ * Effect tạo FormGroup.
161
+ */
162
+ createFormGroupEffect = effect(() => {
163
+ const { mode, newFields, model } = this.formState();
164
+ this.oldFields.set(newFields);
165
+ const computedFields = clone(newFields);
166
+ this.formGroup = buildForm(computedFields, mode, model);
167
+ this.dFormService.setFormGroup(this.formGroup);
168
+ });
169
+
170
+ /**
171
+ * Subscribe to form value changes
172
+ * Subscribe lắng nghe sự thay đổi giá trị của form
173
+ */
174
+ private subscribeToFormValueChanges(): void {
175
+ if (!this.formGroup) return;
176
+
177
+ // Unsubscribe previous subscription if exists
178
+ this.destroy$.next();
179
+
180
+ this.formGroup.valueChanges
181
+ .pipe(
182
+ debounceTime(this.valueChangesDebounceTime),
183
+ distinctUntilChanged((prev, curr) => isEqual(prev, curr)),
184
+ takeUntil(this.destroy$),
185
+ )
186
+ .subscribe((formValue) => {
187
+ // console.log('Form value changed:', formValue);
188
+ this.formValueChanges.emit(formValue);
189
+ });
190
+ }
191
+
192
+ /**
193
+ * Xử lý submit form và output ra ngoài.
194
+ */
195
+ private handleSubmit() {
196
+ const form = this.formGroup;
197
+ const invalidFields: Array<BaseField<any>> = [];
198
+
199
+ // Xử lý trường hợp form chưa thỏa mãn validation.
200
+ if (form.invalid) {
201
+ const controls = form.controls;
202
+ for (const name in controls) {
203
+ if (controls[name].invalid) {
204
+ this.markControlForValidation(controls[name]);
205
+ const field = this.fields().find((field) => field.key === name);
206
+ if (field) invalidFields.push(field);
207
+ }
208
+ }
209
+ this.formInvalid.emit(invalidFields);
210
+ return;
211
+ }
212
+
213
+ const formData = untracked(() => this.formDataService.get(this.formId));
214
+ this.formSubmit.emit(formData ?? {});
215
+ }
216
+
217
+ /**
218
+ * Kích submit form ảo khi nhấn nút submit.
219
+ */
220
+ private markControlForValidation(control: AbstractControl) {
221
+ control.markAllAsTouched();
222
+ control.markAsDirty();
223
+ control.updateValueAndValidity({ onlySelf: true, emitEvent: true });
224
+ }
225
+
226
+ /**
227
+ * TrackBy function for ngFor to optimize rendering.
228
+ */
229
+ protected trackByField(index: number, field: BaseField<any>): string {
230
+ return field.key || `field-${index}`;
231
+ }
232
+
233
+ readonly collapseCustomStyle = {
234
+ background: "#fff",
235
+ border: "0px",
236
+ };
237
+ }
@@ -0,0 +1,16 @@
1
+ import { TestBed } from '@angular/core/testing';
2
+
3
+ import { DynamicFormService } from './dynamic-form.service';
4
+
5
+ describe('DynamicFormService', () => {
6
+ let service: DynamicFormService;
7
+
8
+ beforeEach(() => {
9
+ TestBed.configureTestingModule({});
10
+ service = TestBed.inject(DynamicFormService);
11
+ });
12
+
13
+ it('should be created', () => {
14
+ expect(service).toBeTruthy();
15
+ });
16
+ });
@@ -0,0 +1,9 @@
1
+ import { Injectable } from '@angular/core';
2
+
3
+ @Injectable({
4
+ providedIn: 'root'
5
+ })
6
+ export class DynamicFormService {
7
+
8
+ constructor() { }
9
+ }
@@ -0,0 +1,183 @@
1
+ import { computed, Directive, effect, untracked } from "@angular/core";
2
+ import { rxResource, toObservable, toSignal } from "@angular/core/rxjs-interop";
3
+ import { combineLatest, Observable, of } from "rxjs";
4
+ import { catchError, map, switchMap } from "rxjs/operators";
5
+ import { DependTypeEnum } from "../enums/common";
6
+ import { ApiResponse } from "../models/common/api-response.model";
7
+ import {
8
+ FieldDependency,
9
+ FieldDependencyType,
10
+ } from "../models/common/field-dependency.model";
11
+ import { SelectBaseField } from "../models/fields/select/select-base-field.model";
12
+ import { BaseValueAccessor } from "./base-value-accessor";
13
+
14
+ @Directive()
15
+ export abstract class BaseSelectComponent<T> extends BaseValueAccessor<T> {
16
+ // Alias to strongly type the field input for Select base logic
17
+ protected readonly selectField = computed(
18
+ () => this.fieldInput() as SelectBaseField<any>,
19
+ );
20
+
21
+ protected isFirstLoad = true;
22
+
23
+ // 1. Tạo stream params từ các field phụ thuộc
24
+ private dependParams$ = toObservable(this.selectField).pipe(
25
+ switchMap((field) => {
26
+ const { dependsOn, dependType } = field;
27
+
28
+ if (!dependsOn || dependsOn.length === 0) {
29
+ return of({ valid: true, params: {} });
30
+ }
31
+
32
+ const dependObs$ = this.createDependencyStreams(dependsOn);
33
+
34
+ return combineLatest(dependObs$).pipe(
35
+ map((results) => this.evaluateDependencies(results, dependType)),
36
+ );
37
+ }),
38
+ );
39
+
40
+ private createDependencyStreams(
41
+ dependsOn: FieldDependency[],
42
+ ): Observable<{ key: string; value: string | null }>[] {
43
+ return dependsOn.map((depend) =>
44
+ this.fieldService
45
+ .getDependValueChanges$(depend.fieldKey)
46
+ .pipe(map((val) => ({ key: depend.paramKey, value: val }))),
47
+ );
48
+ }
49
+
50
+ private evaluateDependencies(
51
+ results: { key: string; value: string | null }[],
52
+ dependType: FieldDependencyType,
53
+ ): { valid: boolean; params: Record<string, unknown> } {
54
+ const isValidValue = (val: string | null) =>
55
+ val !== null && val !== "" && val !== undefined;
56
+
57
+ let isConditionMet = false;
58
+
59
+ if (dependType === DependTypeEnum.Any) {
60
+ isConditionMet = results.some((r) => isValidValue(r.value));
61
+ } else {
62
+ isConditionMet = results.every((r) => isValidValue(r.value));
63
+ }
64
+
65
+ const params = results.reduce(
66
+ (acc, curr) => ({ ...acc, [curr.key]: curr.value }),
67
+ {},
68
+ );
69
+ return { valid: isConditionMet, params };
70
+ }
71
+
72
+ private dependParamsSignal = toSignal(this.dependParams$, {
73
+ initialValue: { valid: false, params: {} },
74
+ });
75
+
76
+ // 2. rxResource lấy danh sách
77
+ optionsResource = rxResource({
78
+ request: () => {
79
+ if (this.isView()) return undefined;
80
+
81
+ const state = this.dependParamsSignal();
82
+ if (!state || !state.valid) return undefined;
83
+ return state.params;
84
+ },
85
+ loader: ({ request: params }) => {
86
+ const field = untracked(() => this.selectField());
87
+ const fullApiUrl = field?.apiConfig?.baseUrl || "";
88
+ const apiParams = field?.apiConfig?.params || {};
89
+
90
+ const rawParams = { ...apiParams, ...params };
91
+ const finalParams = Object.fromEntries(
92
+ Object.entries(rawParams).filter(
93
+ ([_, val]) => val !== null && val !== undefined && val !== "",
94
+ ),
95
+ );
96
+
97
+ if (!fullApiUrl) {
98
+ return of({
99
+ code: 0,
100
+ message: "Success",
101
+ data: field?.options ?? [],
102
+ } as ApiResponse<Record<string, unknown>[]>);
103
+ }
104
+
105
+ return this.fieldService.getAllOptions(fullApiUrl, finalParams).pipe(
106
+ catchError((error) =>
107
+ of({ code: -1, message: "Error", data: [] } as ApiResponse<
108
+ Record<string, unknown>[]
109
+ >),
110
+ ),
111
+ );
112
+ },
113
+ });
114
+
115
+ // Gom chung loading từ rxResource và isDisabledSignal
116
+ disabled = computed(
117
+ () => this.optionsResource.isLoading() || this.disabledSignal(),
118
+ );
119
+
120
+ // 3. Xào nấu Options (Prepend, Append, Filter)
121
+ options = computed<Record<string, unknown>[]>(() => {
122
+ const state = this.dependParamsSignal();
123
+ const isLoading = this.optionsResource.isLoading();
124
+ const res = this.optionsResource.value();
125
+
126
+ if (!state || !state.valid || isLoading || !res || res.code !== 0) {
127
+ return [];
128
+ }
129
+
130
+ const field = this.selectField();
131
+ const { optionsToPrepend, optionsToAppend, exceptOptions } = field;
132
+ const rawData = res.data || [];
133
+
134
+ return [...optionsToPrepend, ...rawData, ...optionsToAppend].filter(
135
+ (opt) =>
136
+ !exceptOptions.some(
137
+ (ex) => ex[field.bindValue] === opt[field.bindValue],
138
+ ),
139
+ );
140
+ });
141
+
142
+ constructor() {
143
+ super();
144
+
145
+ // 4. Effect xử lý toàn bộ logic Form Control (Disable/Enable/Reset)
146
+ effect(() => {
147
+ const state = this.dependParamsSignal();
148
+ const isLoading = this.optionsResource.isLoading();
149
+ const res = this.optionsResource.value();
150
+
151
+ untracked(() => {
152
+ // CASE 1 & 2: CHƯA ĐỦ ĐIỀU KIỆN HOẶC ĐANG GỌI API -> Disable và Reset
153
+ if (!state || !state.valid || isLoading) {
154
+ this.disableControl();
155
+ if (!this.isFirstLoad) {
156
+ this.resetControl();
157
+ }
158
+ this.isFirstLoad = false;
159
+ return;
160
+ }
161
+
162
+ // CASE 3: API ĐÃ LOAD XONG
163
+ if (res) {
164
+ if (res.code !== 0) {
165
+ this.disableAndResetControl();
166
+ return;
167
+ }
168
+
169
+ const field = this.selectField();
170
+ if (!field.isDisabled) {
171
+ this.enableControl();
172
+ }
173
+
174
+ // Móc nối cho các class con có nhu cầu đồng bộ data sau khi load API
175
+ this.onApiOptionsLoaded();
176
+ }
177
+ });
178
+ });
179
+ }
180
+
181
+ // Cho phép component con overide nếu cần (ví dụ để hydrate sourceObject)
182
+ protected onApiOptionsLoaded(): void {}
183
+ }