@arsedizioni/ars-utils 22.5.32 → 22.5.34

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.
@@ -71,6 +71,86 @@ class DateInterval {
71
71
  }
72
72
  }
73
73
 
74
+ /**
75
+ * Conversions between the objects the API speaks (DTOs) and the models a signal form works with.
76
+ *
77
+ * The two shapes differ in one systematic way. A DTO leaves out what it does not have: a place
78
+ * with no phone number simply has no `phone`. A form model cannot afford that — signal forms only
79
+ * build a child field for a key whose value is defined, and the string validators need a `string`
80
+ * rather than `string | undefined` — so every editable field is present, holding its empty value
81
+ * (`''` for text and for the boxes of a datepicker, `false` for a checkbox, `0` for a counter).
82
+ *
83
+ * Doing that conversion by hand means a `?? ''` per field on the way in and a `|| undefined` per
84
+ * field on the way out, twice per form, kept in sync forever. {@link DtoUtils.fromModel} and
85
+ * {@link DtoUtils.toModel} do it once, driven by a single "empty model" constant that also serves as
86
+ * the initial value of the form and as what a "clear" button restores.
87
+ */
88
+ /** The two conversions between an API DTO and the model a signal form works with. */
89
+ class DtoUtils {
90
+ /**
91
+ * Builds a form model from the object returned by the API.
92
+ *
93
+ * The shape of the result is decided by `empty`, never by the DTO: every key of `empty` is in
94
+ * the result, taken from the DTO when it carries a usable value and left at its empty value
95
+ * otherwise. A DTO field that is absent and one that is explicitly `null` are treated the same,
96
+ * because both mean "the record has no value here" and the form wants its empty value for both.
97
+ *
98
+ * Keys that exist only in the model (a multi-select decomposed from a bitmask, a checkbox
99
+ * extracted from a flags value) simply keep their empty value: fill them in around the call.
100
+ *
101
+ * @param empty - The neutral model: it declares every field the form owns, and the value each
102
+ * one takes when the DTO does not carry it.
103
+ * @param dto - The object coming from the API, or `undefined` when creating a new record.
104
+ * @returns A new model, never the same reference as `empty` nor as `dto`.
105
+ * @example
106
+ * const EMPTY_PLACE: PlaceForm = { name: '', city: '', phone: '', disabled: false };
107
+ * this.form().reset(DtoUtils.fromModel(EMPTY_PLACE, r.value));
108
+ */
109
+ static fromModel(empty, dto) {
110
+ const model = { ...empty };
111
+ if (!dto)
112
+ return model;
113
+ for (const key of Object.keys(empty)) {
114
+ const value = dto[key];
115
+ if (value !== undefined && value !== null) {
116
+ model[key] = value;
117
+ }
118
+ }
119
+ return model;
120
+ }
121
+ /**
122
+ * Builds the payload for the API from the current form model.
123
+ *
124
+ * Every empty value — `''`, `null` or `undefined` — is dropped, so a field the user never filled
125
+ * in travels as absent instead of reaching the backend as an empty string that would blank the
126
+ * record. `0` and `false` are NOT empty: an amount of zero and a switch left off are data, and a
127
+ * filter that wants an unticked box to mean "no filter" has to say so where it builds its query.
128
+ *
129
+ * The conversion is shallow: nested objects and arrays are passed through as they are, so a
130
+ * composite field (a bitmask to rebuild, a nested block to normalize) is handled around the call
131
+ * — usually by destructuring it out of the model first.
132
+ *
133
+ * The return type is {@link Dto}, the model without its empty values, so what the compiler sees
134
+ * matches what travels: a `coachLevel: CoachLevel | null` field arrives as `CoachLevel` and fits
135
+ * the optional `coachLevel?: CoachLevel` of the API model. At runtime the empty keys are simply
136
+ * not there, which is what `HttpClient` serializes anyway.
137
+ *
138
+ * @param model - The current form model.
139
+ * @returns A shallow copy without the empty fields.
140
+ * @example
141
+ * this.scmService.places.savePlace(DtoUtils.toModel(this.model()));
142
+ */
143
+ static toModel(model) {
144
+ const dto = {};
145
+ for (const [key, value] of Object.entries(model)) {
146
+ if (value === '' || value === null || value === undefined)
147
+ continue;
148
+ dto[key] = value;
149
+ }
150
+ return dto;
151
+ }
152
+ }
153
+
74
154
  const UtilsMessages = {
75
155
  /**
76
156
  * Messages
@@ -2256,5 +2336,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
2256
2336
  * Generated bundle index. Do not edit.
2257
2337
  */
2258
2338
 
2259
- export { AutoFocusDirective, BroadcastChannelManager, BroadcastService, CHANNEL_NAME, CopyClipboardDirective, DateFormat, DateInterval, DateIntervalChangeDirective, DeleteModel, EnvironmentService, FileInfo, FormatHtmlPipe, FormatPipe, GroupModel, IDModel, ImportModel, LoginOAuthType, QueryModel, RelationModel, RemoveFocusDirective, ReplacePipe, SafeHtmlPipe, SafeUrlPipe, ScreenService, SearchCallbackPipe, SearchFilterPipe, SelectableModel, SplashService, SystemUtils, ThemeService, UpdateRelationsModel, UtilsMessages, ValueModel };
2339
+ export { AutoFocusDirective, BroadcastChannelManager, BroadcastService, CHANNEL_NAME, CopyClipboardDirective, DateFormat, DateInterval, DateIntervalChangeDirective, DeleteModel, DtoUtils, EnvironmentService, FileInfo, FormatHtmlPipe, FormatPipe, GroupModel, IDModel, ImportModel, LoginOAuthType, QueryModel, RelationModel, RemoveFocusDirective, ReplacePipe, SafeHtmlPipe, SafeUrlPipe, ScreenService, SearchCallbackPipe, SearchFilterPipe, SelectableModel, SplashService, SystemUtils, ThemeService, UpdateRelationsModel, UtilsMessages, ValueModel };
2260
2340
  //# sourceMappingURL=arsedizioni-ars-utils-core.mjs.map