Skip to content

Commit f622d25

Browse files
committed
feat(lib): support for mark* methods
Use mark* methods to change state of a form Closes #12
1 parent 7811352 commit f622d25

File tree

3 files changed

+291
-1
lines changed

3 files changed

+291
-1
lines changed

README.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,48 @@ formsManager.patchValue('onboarding', value, options);
170170
formsManager.setValue('onboarding', value, options);
171171
```
172172

173+
- `markAllAsTouched()` - A proxy to the original `markAllAsTouched` method
174+
175+
```ts
176+
formsManager.markAllAsTouched('onboarding', options);
177+
```
178+
179+
- `markAsTouched()` - A proxy to the original `markAsTouched` method
180+
181+
```ts
182+
formsManager.markAsTouched('onboarding', options);
183+
```
184+
185+
- `markAllAsDirty()` - Marks the control and all its descendant controls as dirty
186+
187+
```ts
188+
formsManager.markAllAsDirty('onboarding', options);
189+
```
190+
191+
- `markAsDirty()` - A proxy to the original `markAsDirty` method
192+
193+
```ts
194+
formsManager.markAsDirty('onboarding', options);
195+
```
196+
197+
- `markAsPending()` - A proxy to the original `markAsPending` method
198+
199+
```ts
200+
formsManager.markAsPending('onboarding', options);
201+
```
202+
203+
- `markAsPristine()` - A proxy to the original `markAsPristine` method
204+
205+
```ts
206+
formsManager.markAsPristine('onboarding', options);
207+
```
208+
209+
- `markAsUntouched()` - A proxy to the original `markAsUntouched` method
210+
211+
```ts
212+
formsManager.markAsUntouched('onboarding', options);
213+
```
214+
173215
- `unsubscribe()` - Unsubscribe from the form's `valueChanges` observable (always call it on `ngOnDestroy`)
174216

175217
```ts

projects/ngneat/forms-manager/src/lib/forms-manager.spec.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1282,4 +1282,75 @@ describe('FormsManager', () => {
12821282
expect(formsManager.getControl('updateOnBlurGroup', 'name').value).toEqual('Smith');
12831283
});
12841284
});
1285+
1286+
describe('Mark*', () => {
1287+
let formsManager: NgFormsManager, userForm: FormGroup;
1288+
1289+
beforeEach(() => {
1290+
formsManager = new NgFormsManager(new NgFormsManagerConfig());
1291+
1292+
userForm = new FormGroup({
1293+
name: new FormControl(),
1294+
email: new FormControl(),
1295+
date: new FormControl(),
1296+
phone: new FormGroup({
1297+
number: new FormControl(),
1298+
prefix: new FormControl(),
1299+
}),
1300+
});
1301+
1302+
formsManager.upsert('user', userForm);
1303+
});
1304+
1305+
it('should mark control and its descendants as touched', () => {
1306+
formsManager.markAllAsTouched('user');
1307+
1308+
expect(formsManager.getControl('user').touched).toBeTrue();
1309+
expect(formsManager.getControl('user', 'email').touched).toBeTrue();
1310+
expect(formsManager.getControl('user', 'phone.number').touched).toBeTrue();
1311+
});
1312+
1313+
it('should mark control as touched', () => {
1314+
formsManager.markAsTouched('user');
1315+
1316+
expect(formsManager.getControl('user').touched).toBeTrue();
1317+
});
1318+
1319+
it('should mark control and its descendants as dirty', () => {
1320+
formsManager.markAllAsDirty('user');
1321+
1322+
expect(formsManager.getControl('user').dirty).toBeTrue();
1323+
expect(formsManager.getControl('user', 'email').dirty).toBeTrue();
1324+
expect(formsManager.getControl('user', 'phone.number').dirty).toBeTrue();
1325+
});
1326+
1327+
it('should mark control as dirty', () => {
1328+
formsManager.markAsDirty('user');
1329+
1330+
expect(formsManager.getControl('user').dirty).toBeTrue();
1331+
});
1332+
1333+
it('should mark control as pending', () => {
1334+
formsManager.markAsPending('user');
1335+
1336+
expect(formsManager.getControl('user').pending).toBeTrue();
1337+
});
1338+
1339+
it('should mark control as pristine', () => {
1340+
formsManager.markAsPristine('user');
1341+
1342+
expect(formsManager.getControl('user').pristine).toBeTrue();
1343+
});
1344+
1345+
it('should mark control as untouched', () => {
1346+
formsManager.markAsUntouched('user');
1347+
1348+
expect(formsManager.getControl('user').untouched).toBeTrue();
1349+
});
1350+
1351+
afterEach(() => {
1352+
formsManager.unsubscribe();
1353+
formsManager = null;
1354+
});
1355+
});
12851356
});

projects/ngneat/forms-manager/src/lib/forms-manager.ts

Lines changed: 178 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Inject, Injectable, Optional } from '@angular/core';
2-
import { AbstractControl, FormGroup } from '@angular/forms';
2+
import { AbstractControl, FormGroup, FormArray } from '@angular/forms';
33
import { coerceArray, filterControlKeys, filterNil, isBrowser, mergeDeep } from './utils';
44
import { EMPTY, merge, Observable, Subject, Subscription, timer } from 'rxjs';
55
import { debounce, distinctUntilChanged, filter, map, mapTo, tap } from 'rxjs/operators';
@@ -279,6 +279,163 @@ export class NgFormsManager<FormsState = any> {
279279
this.initialValues$$.set(name, value);
280280
}
281281

282+
/**
283+
*
284+
* @example
285+
*
286+
* A proxy to the original `markAllAsTouched` method
287+
*
288+
* manager.markAllAsTouched('login');
289+
*
290+
*/
291+
markAllAsTouched(name: keyof FormsState): void {
292+
if (this.instances$$.has(name)) {
293+
this.instances$$.get(name).markAllAsTouched();
294+
295+
this.updateStore(name, this.instances$$.get(name));
296+
}
297+
}
298+
299+
/**
300+
*
301+
* @example
302+
*
303+
* A proxy to the original `markAsTouched` method
304+
*
305+
* manager.markAsTouched('login');
306+
*
307+
*/
308+
markAsTouched(
309+
name: keyof FormsState,
310+
options?: {
311+
onlySelf?: boolean;
312+
emitEvent?: boolean;
313+
}
314+
): void {
315+
if (this.instances$$.has(name)) {
316+
this.instances$$.get(name).markAsTouched(options);
317+
318+
this.updateStore(name, this.instances$$.get(name));
319+
}
320+
}
321+
322+
/**
323+
*
324+
* @example
325+
*
326+
* Marks the control and all its descendant controls as dirty.
327+
*
328+
* manager.markAllAsDirty('login');
329+
*
330+
*/
331+
markAllAsDirty(
332+
name: keyof FormsState,
333+
options?: {
334+
onlySelf?: boolean;
335+
emitEvent?: boolean;
336+
}
337+
): void {
338+
if (this.instances$$.has(name)) {
339+
let control = this.instances$$.get(name);
340+
341+
this.markDescendantsAsDirty(control, options);
342+
343+
this.updateStore(name, control);
344+
}
345+
}
346+
347+
/**
348+
*
349+
* @example
350+
*
351+
* A proxy to the original `markAsDirty` method
352+
*
353+
* manager.markAsDirty('login');
354+
*
355+
*/
356+
markAsDirty(
357+
name: keyof FormsState,
358+
options?: {
359+
onlySelf?: boolean;
360+
emitEvent?: boolean;
361+
}
362+
): void {
363+
if (this.instances$$.has(name)) {
364+
this.instances$$.get(name).markAsDirty(options);
365+
366+
this.updateStore(name, this.instances$$.get(name));
367+
}
368+
}
369+
370+
/**
371+
*
372+
* @example
373+
*
374+
* A proxy to the original `markAsPending` method
375+
*
376+
* manager.markAsPending('login');
377+
*
378+
*/
379+
markAsPending(
380+
name: keyof FormsState,
381+
options?: {
382+
onlySelf?: boolean;
383+
emitEvent?: boolean;
384+
}
385+
): void {
386+
if (this.instances$$.has(name)) {
387+
this.instances$$.get(name).markAsPending(options);
388+
389+
this.updateStore(name, this.instances$$.get(name));
390+
}
391+
}
392+
393+
/**
394+
*
395+
* @example
396+
*
397+
* A proxy to the original `markAsPristine` method
398+
*
399+
* manager.markAsPristine('login');
400+
*
401+
*/
402+
markAsPristine(
403+
name: keyof FormsState,
404+
options?: {
405+
onlySelf?: boolean;
406+
emitEvent?: boolean;
407+
}
408+
): void {
409+
if (this.instances$$.has(name)) {
410+
this.instances$$.get(name).markAsPristine(options);
411+
412+
this.updateStore(name, this.instances$$.get(name));
413+
}
414+
}
415+
416+
/**
417+
*
418+
* @example
419+
*
420+
* A proxy to the original `markAsUntouched` method
421+
*
422+
* manager.markAsUntouched('login');
423+
*
424+
*/
425+
markAsUntouched(
426+
name: keyof FormsState,
427+
options?: {
428+
onlySelf?: boolean;
429+
emitEvent?: boolean;
430+
}
431+
): void {
432+
if (this.instances$$.has(name)) {
433+
this.instances$$.get(name).markAsUntouched(options);
434+
435+
this.updateStore(name, this.instances$$.get(name));
436+
}
437+
}
438+
282439
/**
283440
*
284441
* @example
@@ -462,4 +619,24 @@ export class NgFormsManager<FormsState = any> {
462619
private removeInitialValue(name: FormKeys<FormsState>) {
463620
coerceArray(name).forEach(name => this.initialValues$$.delete(name));
464621
}
622+
623+
private markDescendantsAsDirty(
624+
control: AbstractControl,
625+
options?: {
626+
onlySelf?: boolean;
627+
emitEvent?: boolean;
628+
}
629+
) {
630+
control.markAsDirty(options);
631+
632+
if (control instanceof FormGroup || control instanceof FormArray) {
633+
Object.values(control.controls).forEach((control: AbstractControl) => {
634+
control.markAsDirty(options);
635+
636+
if ((control as FormGroup | FormArray).controls) {
637+
this.markDescendantsAsDirty(control, options);
638+
}
639+
});
640+
}
641+
}
465642
}

0 commit comments

Comments
 (0)