@rxdi/forms 0.7.218 → 0.7.220

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/README.md CHANGED
@@ -200,11 +200,10 @@ export class TagsComponent extends LitElement {
200
200
  model: 'tags', // Triggers form.patchValue(this.tags) on INIT
201
201
  })
202
202
  form = new FormGroup({
203
- tags: new FormArray<{ value: string }>([], {
204
- name: 'tags',
205
- // Factory describes how to create new controls from model data
206
- itemFactory: (value) => new FormGroup({ value: value.value || value }),
207
- }),
203
+ tags: new FormArray<{ value: string }>(
204
+ [],
205
+ (value) => new FormGroup({ value: value.value || value }) // Factory handles population automatically
206
+ ),
208
207
  });
209
208
 
210
209
  addTag() {
@@ -223,6 +222,75 @@ export class TagsComponent extends LitElement {
223
222
 
224
223
  ````
225
224
 
225
+ ### Typed Subscriptions & Virtual Inputs
226
+
227
+ You can subscribe to `valueChanges` on individual inputs, even if they aren't in the DOM yet!
228
+
229
+ ```typescript
230
+ // Works even if 'email' input is inside an *ngIf or not yet rendered
231
+ this.form.get('email').valueChanges.subscribe(value => {
232
+ console.log('Email changed:', value); // 'value' is strongly typed!
233
+ });
234
+ ```
235
+
236
+ This is powered by "Virtual Inputs" which mock the input interface if the model key exists but the DOM element is missing.
237
+
238
+ ### Working with Value Changes
239
+
240
+ The `valueChanges` observable is powerful for creating interactive forms.
241
+
242
+ #### 1. Debounced Search
243
+
244
+ ```typescript
245
+ import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
246
+
247
+ this.form.get('search').valueChanges.pipe(
248
+ debounceTime(300),
249
+ distinctUntilChanged()
250
+ ).subscribe(term => {
251
+ this.searchService.search(term);
252
+ });
253
+ ```
254
+
255
+ #### 2. Dependant Fields (Cascading Dropdowns)
256
+
257
+ Reset or modify dependent fields when a parent field changes.
258
+
259
+ ```typescript
260
+ this.form.get('country').valueChanges.subscribe(country => {
261
+ // Reset state when country changes
262
+ this.form.get('state').value = '';
263
+
264
+ // Update state options dynamically based on country
265
+ this.loadStatesFor(country);
266
+ });
267
+ ```
268
+
269
+ #### 3. Dynamic Disabling
270
+
271
+ Disable controls based on the value of others.
272
+
273
+ ```typescript
274
+ this.form.get('subscribeNewsletter').valueChanges.subscribe(shouldSubscribe => {
275
+ const emailControl = this.form.get('newsletterEmail');
276
+ if (shouldSubscribe) {
277
+ emailControl.disabled = false;
278
+ } else {
279
+ emailControl.disabled = true;
280
+ emailControl.value = ''; // Optional: clear value
281
+ }
282
+ });
283
+ ```
284
+
285
+ ### Generic Typed AbstractInput
286
+
287
+ `AbstractInput` is now generic, propagating types through the form.
288
+
289
+ ```typescript
290
+ const emailInput: AbstractInput<string> = this.form.get('email');
291
+ // emailInput.value is string
292
+ ```
293
+
226
294
  ## API Reference
227
295
 
228
296
  ### Validators
@@ -32,4 +32,5 @@ export declare class FormArray<T = any> implements AbstractControl<T[]> {
32
32
  getParentElement(): LitElement;
33
33
  set value(values: T[]);
34
34
  patchValue(values: T[]): void;
35
+ updateValueAndValidity(): Promise<any[]>;
35
36
  }
@@ -138,5 +138,25 @@ class FormArray {
138
138
  });
139
139
  this.updateValue();
140
140
  }
141
+ updateValueAndValidity() {
142
+ return __awaiter(this, void 0, void 0, function* () {
143
+ this.valid = true;
144
+ this.invalid = false;
145
+ this.errors = {};
146
+ const errors = [];
147
+ for (const [index, control] of this.controls.entries()) {
148
+ if (control.updateValueAndValidity) {
149
+ const result = yield control.updateValueAndValidity();
150
+ if (control.invalid) {
151
+ this.valid = false;
152
+ this.invalid = true;
153
+ this.errors[index] = result;
154
+ errors.push(...result);
155
+ }
156
+ }
157
+ }
158
+ return errors;
159
+ });
160
+ }
141
161
  }
142
162
  exports.FormArray = FormArray;
@@ -121,17 +121,22 @@ class FormGroup {
121
121
  this.setElementDirty(i);
122
122
  return yield this.validate(i);
123
123
  })));
124
+ const inputsWithErrors = inputs.filter((e) => e.errors.length);
125
+ const nestedErrors = [];
124
126
  for (const [key, control] of this.controls.entries()) {
125
127
  if (control.updateValueAndValidity) {
126
- yield control.updateValueAndValidity();
128
+ const result = (yield control.updateValueAndValidity());
127
129
  if (control.invalid) {
128
130
  this.invalid = true;
129
131
  this.valid = false;
132
+ if (Array.isArray(result)) {
133
+ nestedErrors.push(...result);
134
+ }
130
135
  }
131
136
  }
132
137
  }
133
138
  this.getParentElement().requestUpdate();
134
- return inputs.filter((e) => e.errors.length) || (this.invalid ? [{ message: 'Invalid Form' }] : []);
139
+ return [...inputsWithErrors, ...nestedErrors];
135
140
  });
136
141
  }
137
142
  updateValueAndValidityOnEvent(method) {
@@ -423,8 +428,8 @@ class FormGroup {
423
428
  }
424
429
  Object.keys(value).forEach((key) => {
425
430
  const control = this.controls.get(key);
426
- if (control === null || control === void 0 ? void 0 : control['patchValue']) {
427
- control['patchValue'](value[key]);
431
+ if (control === null || control === void 0 ? void 0 : control.patchValue) {
432
+ control.patchValue(value[key]);
428
433
  }
429
434
  else {
430
435
  this.setValue(key, value[key]);
@@ -50,6 +50,9 @@ export interface AbstractControl<T = any> {
50
50
  name?: string;
51
51
  push?(control: AbstractControl): void;
52
52
  getFormElement?(): HTMLFormElement;
53
+ patchValue?(value: T): void;
54
+ validationMessage?: string;
55
+ get?(name: string): any;
53
56
  }
54
57
  export type ValidatorFn = (element: AbstractInput | HTMLInputElement) => Promise<InputErrorMessage | void> | InputErrorMessage | void;
55
58
  export interface FormInputOptions {
@@ -68,7 +71,7 @@ export interface AbstractInput<T = any> extends HTMLInputElement {
68
71
  invalid?: boolean;
69
72
  dirty?: boolean;
70
73
  touched?: boolean;
71
- valueChanges?: Observable<any>;
74
+ valueChanges?: Observable<T>;
72
75
  }
73
76
  export declare const InputValidityState: {
74
77
  badInput: "badInput";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rxdi/forms",
3
- "version": "0.7.218",
3
+ "version": "0.7.220",
4
4
  "main": "./dist/index.js",
5
5
  "author": "Kristiyan Tachev",
6
6
  "license": "MIT",
@@ -12,7 +12,7 @@
12
12
  "build": "tsc"
13
13
  },
14
14
  "devDependencies": {
15
- "@rxdi/lit-html": "^0.7.217",
15
+ "@rxdi/lit-html": "^0.7.219",
16
16
  "@types/node": "^25.0.3",
17
17
  "rxjs": "^7.8.2",
18
18
  "typescript": "^5.9.3"