(Angular Reactive) Forms with Benefits 😉

Last update: Apr 30, 2022


Test MIT commitizen PRs styled with prettier ngneat

(Angular Reactive) Forms with Benefits 😉

How many times have you told yourself "I wish Angular Reactive Forms would support types", or "I really want an API to query the form reactively. It missed some methods."

Your wish is my command!
This library extends every Angular AbstractControl, and provides features that don't exist in the original one. It adds types, reactive queries, and helper methods. The most important thing is that you can start using it today! In most cases, the only thing that you need to change is the import path. So don't worry, no form refactoring required - we've got you covered;

Let's take a look at all the neat things we provide:

🔮 Features

✅ Offers (almost) seamless FormControl, FormGroup, FormArray Replacement
✅ Allows Typed Forms!
✅ Provides Reactive Queries
✅ Provides Helpful Methods
✅ Typed and DRY ControlValueAccessor
✅ Typed FormBuilder
✅ Persist the form's state to local storage

👉 npm install @ngneat/reactive-forms

Table of Contents

Control Type

FormControl/FormArray takes a generic that defines the type of the control. This type is than used to enhance every method exposed by Angular or this library.

Use it with a FormControl:

import { FormControl } from '@ngneat/reactive-forms';

const control = new FormControl<string>();

// Or auto infer it based on the initial value
const control = new FormControl('');

control.valueChanges.subscribe(value => {
  // value is typed as string
});

Use it with a FormArray:

import { FormArray, FormControl } from '@ngneat/reactive-forms';

const control = new FormArray<string>([new FormControl('')]);

control.value$.subscribe(value => {
  // value is typed as string[]
});

If you use a FormGroup, it'll automatically infer the type based on the controls you supply:

import { FormGroup, FormControl } from '@ngneat/reactive-forms';

const profileForm = new FormGroup({
  firstName: new FormControl(''),
  lastName: new FormControl(''),
  address: new FormGroup({
    street: new FormControl(''),
    city: new FormControl('')
  })
});


profileForm.setValue(new Profile());

profileForm.patchValue({ firstName: 'Netanel' });

You can use the experimental ControlsOf feature if you want to force a FormGroup to implement an external type:

import { FormGroup, FormControl, ControlsOf } from '@ngneat/reactive-forms';

interface Profile {
  firstName: string;
  lastName: string;
  address: {
    street: string;
    city: string;
  };
}

const profileForm = new FormGroup<ControlsOf<Profile>>({
  firstName: new FormControl(''),
  lastName: new FormControl(''),
  address: new FormGroup({
    street: new FormControl(''),
    city: new FormControl('')
  })
});

Gotchas

  • When using array types, it'll automatically infer it as FormArray. If you need a FormControl, you must set it within your interface explicitly:
interface User {
  name: string;
  // 👇🏻
  skills: FormControl<string[]>;
}
  • Optional fields will only work with top-level values, and will not work with FormGroup:
interface User {
  name?: string;
  foo?: string[]; 
  // 👇🏻 will not work 
  nested?: {
    id: string;
  };
}

Control Queries

value$

Observes the control's value. Unlike the behavior of the built-in valueChanges observable, it emits the current rawValue immediately (which means you'll also get the values of disabled controls).

import { FormControl } from '@ngneat/reactive-forms';

const control = new FormControl('');
control.value$.subscribe(value => ...);

disabled$

Observes the control's disable status.

import { FormControl } from '@ngneat/reactive-forms';

const control = new FormControl('');
control.disabled$.subscribe(isDisabled => ...);

enabled$

Observes the control's enable status.

import { FormControl } from '@ngneat/reactive-forms';

const control = new FormControl('');
control.enabled$.subscribe(isEnabled => ...);

invalid$

Observes the control's invalid status.

import { FormControl } from '@ngneat/reactive-forms';

const control = new FormControl('');
control.invalid$.subscribe(isInvalid => ...);

valid$

Observes the control's valid status.

import { FormControl } from '@ngneat/reactive-forms';

const control = new FormControl('');
control.valid$.subscribe(isValid => ...);

status$

Observes the control's status.

import { FormControl } from '@ngneat/reactive-forms';

const control = new FormControl('');
control.status$.subscribe(status => ...);

The status is typed as ControlState (valid, invalid, pending or disabled).

touch$

Observes the control's touched status.

import { FormControl } from '@ngneat/reactive-forms';

const control = new FormControl('');
control.touch$.subscribe(isTouched => ...);

This emits a value only when markAsTouched, or markAsUnTouched, has been called.

dirty$

Observes the control's dirty status.

import { FormControl } from '@ngneat/reactive-forms';

const control = new FormControl('');
control.dirty$.subscribe(isDirty => ...);

This emits a value only when markAsDirty, or markAsPristine, has been called.

errors$

Observes the control's errors.

import { FormControl } from '@ngneat/reactive-forms';

const control = new FormControl('');
control.errors$.subscribe(errors => ...);

select()

Selects a slice of the form's state based on the given predicate.

import { FormGroup } from '@ngneat/reactive-forms';

const group = new FormGroup({
  name: new FormControl('')
});

group.select(state => state.name).subscribe(name => ...)

Control Methods

setValue()

In addition to the built-in method functionality, it can also take an observable.

import { FormGroup } from '@ngneat/reactive-forms';

const group = new FormGroup({
  name: new FormControl('')
});

group.setValue(store.select('formValue'));

patchValue()

In addition to the built-in method functionality, it can also take an observable.

import { FormGroup } from '@ngneat/reactive-forms';

const group = new FormGroup({
  name: new FormControl('')
});

group.patchValue(store.select('formValue'));

disabledWhile()

Takes an observable that emits a boolean indicating whether to disable the control.

import { FormControl } from '@ngneat/reactive-forms';

const control = new FormControl('');
control.disabledWhile(store.select('isDisabled'));

enabledWhile()

Takes an observable that emits a boolean indicating whether to enable the control.

import { FormControl } from '@ngneat/reactive-forms';

const control = new FormControl('');
control.enabledWhile(store.select('isEnabled'));

markAllAsDirty()

Marks all the group's controls as dirty.

import { FormGroup } from '@ngneat/reactive-forms';

const control = new FormGroup();
control.markAllAsDirty();

hasErrorAndTouched()

A syntactic sugar method to be used in the template:

import { FormControl } from '@ngneat/reactive-forms';

const control = new FormControl('', Validators.required);
">
<span *ngIf="control.hasErrorAndTouched('required')">span>

hasErrorAndDirty()

A syntactic sugar method to be used in the template:

import { FormControl } from '@ngneat/reactive-forms';

const control = new FormControl('', Validators.required);
">
<span *ngIf="control.hasErrorAndDirty('required')">span>

setEnable()

Sets whether the control is enabled.

import { FormControl } from '@ngneat/reactive-forms';

const control = new FormControl('');
control.setEnable();
control.setEnable(false);

setDisable()

Sets whether the control is disabled.

import { FormControl } from '@ngneat/reactive-forms';

const control = new FormControl('');
control.setDisable();
control.setDisable(false);

get()

A method with typed parameters which obtains a reference to a specific control.

import { FormGroup } from '@ngneat/reactive-forms';

const group = new FormGroup({
  name: new FormControl(''),
  address: new FormGroup({
    street: new FormControl(''),
    city: new FormControl('')
  })
});

const name = group.get('name'); // FormControl
const city = group.get(['address', 'city']); // FormControl

// Don't use it like this
group.get('address.city') // AbstractControl

mergeErrors()

Merge validation errors. Unlike setErrors(), this will not overwrite errors already held by the control.

import { FormGroup } from '@ngneat/reactive-forms';

const group = new FormGroup(...);
group.mergeErrors({ customError: true });

removeError()

Remove an error by key from the control.

import { FormGroup } from '@ngneat/reactive-forms';

const group = new FormGroup(...);
group.removeError('customError');

FormArray methods

remove()

Remove a control from an array based on its value

import { FormArray } from '@ngneat/reactive-forms';

const array = new FormArray<string>(...);
// Remove empty strings
array.remove('')

removeIf()

Remove a control from an array based on a predicate

import { FormArray } from '@ngneat/reactive-forms';

const array = new FormArray(...);
// Only keep addresses in NYC
array.removeIf((control) => control.get('address').get('city').value !== 'New York')

Control Operators

Each valueChanges or values$ takes an operator diff(), which emits only changed parts of form:

import { FormGroup, FormControl, diff } from '@ngneat/reactive-forms';

const control = new FormGroup({
  name: new FormControl(''),
  phone: new FormGroup({
    num: new FormControl(''),
    prefix: new FormControl('')
  }),
  skills: new FormArray<string>([])
});

control.value$
  .pipe(diff())
  .subscribe(value => {
    // value is emitted only if it has been changed, and only the changed parts.
  });

ControlValueAccessor

The library exposes a typed version of ControlValueAccessor, which already implements registerOnChange and registerOnTouched under the hood:

import { ControlValueAccessor } from '@ngneat/reactive-forms';

@Component({
  selector: 'my-checkbox',
  host: { '(change)': 'onChange($event.target.checked)', '(blur)': 'onTouched()' },
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: MyCheckboxComponent,
      multi: true
    }
  ]
})
export class MyCheckboxComponent extends ControlValueAccessor<boolean> {
  writeValue(value: boolean) {

  }

  // `this.onChange`, and `this.onTouched` are already here!
}

Note that you can also use it as interface.

Form Builder

We also introduce a typed version of FormBuilder which returns a typed FormGroup, FormControl and FormArray with all our sweet additions:

import { FormBuilder } from '@ngneat/reactive-forms';

constructor(
  private fb: FormBuilder
) {}

const group = this.fb.group({ name: 'ngneat', id: 1 });

group.get('name') // FormControl

Due to the complexity of the builder API, we are currently couldn't create a "good" implementation of ControlsOf for the builder.

Persist Form

Automatically persist the AbstractControl's value to the given storage:

import { persistControl } from '@ngneat/reactive-forms';

const group = new FormGroup(...);
const unsubscribe = persistControl(group, 'profile').subscribe();

The persistControl function will also set the FromGroup value to the latest state available in the storage before subscribing to value changes.

PersistOptions

Change the target storage or debounceTime value by providing options as a second argument in the persist function call.

Option Description Default
debounceTime Update delay in ms between value changes 250
manager A manager implementing the PersistManager interface LocalStorageManager
arrControlFactory Factory functions for FormArray
persistDisabledControls Defines whether values of disabled controls should be persisted false

By default the library provides LocalStorageManager and SessionStorageManager. It's possible to store the form value into a custom storage. Just implement the PersistManager interface, and use it when calling the persistControl function.

export class StateStoreManager<T> implements PersistManager<T> {
  setValue(key: string, data: T) {
     ...
  }

  getValue(key: string) {
    ...
  }
}

export class FormComponent implements OnInit {
  group = new FormGroup();

  ngOnInit() {
    persist(this.group, 'profile', { manager: new StateStoreManager() }).subscribe();
  }
}
Using FormArray Controls.

When working with a FormArray, it's required to pass a factory function that defines how to create the controls inside the FormArray.

const group = new FormGroup({
  skills: new FormArray<string>([])
});

persist(group, 'profile', {
  arrControlFactory: {
     skills: value => new FormControl(value)
  }
});

Because the form is strongly typed, you can only configure factories for properties that are of type Array. The library makes it also possible to correctly infer the type of value for the factory function.

ESLint Rule

We provide a special lint rule that forbids the imports of any token we expose, such as the following: AbstractControl, AsyncValidatorFn, ControlValueAccessor, FormArray, FormBuilder, FormControl, FormGroup, ValidatorFn, from @angular/forms.

Check out the documentation.

GitHub

https://github.com/ngneat/reactive-forms
Comments
  • 1. feature: support exact control types in FormGroup and FormArray

    PR Checklist

    Please check if your PR fulfills the following requirements:

    • [x] The commit message follows our guidelines: CONTRIBUTING.md#commit
    • [x] Tests for the changes have been added (for bug fixes / features)
    • [X] Docs have been added / updated (for bug fixes / features)

    PR Type

    What kind of change does this PR introduce?

    [ ] Bugfix
    [X] Feature
    [ ] Code style update (formatting, local variables)
    [ ] Refactoring (no functional changes, no api changes)
    [ ] Build related changes
    [ ] CI related changes
    [ ] Documentation content changes
    [ ] Other... Please describe:
    

    What is the current behavior?

    From the current documentation:

    import { FormGroup } from '@ngneat/reactive-forms';
    
    const group = new FormGroup<Profile>(...);
    const address = group.getControl('name') as FormGroup<Profile['address']>;
    const city = group.getControl('address', 'city') as FormControl<string>;
    

    "Note that the return type should still be inferred." - This PR is fixing this issue.

    What is the new behavior?

    I'm enabling passing the type of the control / controls in addition to the current ability to pass the value type to FormGroup or FormArray. For example, this way I can differ between those two, otherwise identical, Form Groups:

    const group1 = new FormGroup<{
      name: string, // for primitive types, FormControl is inferred, instead of AbstractControl, which is what it was before.
      address: FormControl<{ city: string, street: string }>
    }>({
      name: new FormControl('Shachar'),
      address: new FormControl({
        city: 'Tel Aviv',
        street: 'Ibn Gavirol'
      }),
    })
    
    const group2 = new FormGroup<{
      name: string, // for primitive types, FormControl is inferred, instead of AbstractControl, which is what it was before.
      address: FormGroup<{ city: string, street: string }>
    }>({
      name: new FormControl('Shachar'),
      address: new FormGroup({
        city: new FormControl('Tel Aviv'),
        street: new FormControl('Ibn Gavirol')
      }),
    })
    

    Now I'll get automatic inference for the controls type:

    const control1 = group1.getControl('address'); // FormControl<{city: string, street: string}> is inferred
    const control2 = group1.getControl('address'); // FormGroup<{city: string, street: string}> is inferred
    

    Does this PR introduce a breaking change?

    [ ] Yes
    [X] No
    

    Note those changes are completely backward compatible! For evidence, I did not change existing tests and they all pass.

    Reviewed by sharshuv-quotient at 2020-07-26 14:42
  • 2. Required validator on FormControl not working in unit tests when using jest

    I'm submitting a...

    
    [ ] Regression (a behavior that used to work and stopped working in a new release)
    [x] Bug report  
    [ ] Performance issue
    [ ] Feature request
    [ ] Documentation issue or request
    [ ] Support request
    [ ] Other... Please describe:
    

    Current behavior

    When running unit tests in a jest environment they fail when using the required validator on a FormControl because it always returns true even when the value is invalid. Using the FormControl from the @angular/forms package works fine.

    This only happens in a jest environment, the actual functionality in a browser or when using jasmine for the unit tests works as expected. I'm sorry if I've raised this bug in the wrong place, it could be a problem with jest or jsdom. I'm going to keep trying to debug and see if I can find out the reason why.

    The following issue might be relevant from what I found when searching the closed issues: https://github.com/ngneat/reactive-forms/issues/86

    Expected behavior

    When the FormControl is invalid then it should return false when checking the valid property for unit tests running with jest.

    Minimal reproduction of the problem with instructions

    I've attached a zip file to this ticket with a sample angular project that shows the tests passing when using jasmine and failing when using jest.

    typed-forms.zip

    To run the jasmine unit tests that work use this command: npm run test

    To run the jest unit tests that fail use this command: npm run test:jest

    What is the motivation / use case for changing the behavior?

    Unit tests pass when using jest and this project.

    Environment

    Angular version: 11.2.13
    
    Browser:
    - [x] TS Jest version 26.5.6 (jsdom 16.5.3)
     
    For Tooling issues:
    - Node version: v14.9.0
    - Platform: Mac
    
    Reviewed by Ben348 at 2021-05-10 00:01
  • 3. Support strict option

    I'm submitting a...

    
    [ ] Regression (a behavior that used to work and stopped working in a new release)
    [x] Bug report  
    [ ] Performance issue
    [ ] Feature request
    [ ] Documentation issue or request
    [ ] Support request
    [ ] Other... Please describe:
    

    Current behavior

    TypeScript will not compile the project because of the following errors:

    Property 'group' in type 'FormBuilder' is not assignable to the same property in base type 'FormBuilder'.
    Property 'controls' in type 'FormGroup<T, E>' is not assignable to the same property in base type 'FormGroup'.
    

    Expected behavior

    I would expect the project to compile successfully.

    Minimal reproduction of the problem with instructions

    Please, let me know if a repo with an example project would be helpful. I'm happy to create one if necessary.

    What is the motivation / use case for changing the behavior?

    I would like to use this library with Angular v10.

    Environment

    
    Angular version: 10.0.1
    
    
    Browser:
    - [ ] Chrome (desktop) version XX
    - [ ] Chrome (Android) version XX
    - [ ] Chrome (iOS) version XX
    - [ ] Firefox version XX
    - [ ] Safari (desktop) version XX
    - [ ] Safari (iOS) version XX
    - [ ] IE version XX
    - [ ] Edge version XX
     
    For Tooling issues:
    - Node version: v12.18.2
    - Platform: MacOS
    
    Others:
    - TypeScript: v3.9.5
    
    Reviewed by chrisguttandin at 2020-07-08 10:39
  • 4. Error when trying to merge validators

    I'm submitting a...

    
    [ ] Regression (a behavior that used to work and stopped working in a new release)
    [X] Bug report  
    [ ] Performance issue
    [ ] Feature request
    [ ] Documentation issue or request
    [ ] Support request
    [ ] Other... Please describe:
    

    Current behavior

    When invoking mergeValidators an error is thrown.

    Expected behavior

    The validators are correctly merged without errors.

    Minimal reproduction of the problem with instructions

    Given a form group created with the typed FormBuilder:

    const builder = new FormBuilder();
    const group = builder.group<{ title: string }>({
       title: [ '', Validators.required ],
    });
    

    when validators are merged

    group.mergeValidators(
       (control: NgAbstractControl | AbstractControl<never, never>) => {}
    );
    

    then this error is thrown

    core.js:6157 ERROR Error: Uncaught (in promise): TypeError: Cannot read property 'validate' of null
    TypeError: Cannot read property 'validate' of null
        at isValidatorFn (forms.js:754)
        at forms.js:766
        at Array.map (<anonymous>)
        at normalizeValidators (forms.js:765)
        at composeValidators (forms.js:776)
        at coerceToValidator (forms.js:2756)
        at FormGroup.setValidators (forms.js:2951)
        at FormGroup.setValidators (ngneat-reactive-forms.js:510)
        at mergeControlValidators (ngneat-reactive-forms.js:96)
        at FormGroup.mergeValidators (ngneat-reactive-forms.js:481)
        at resolvePromise (zone-evergreen.js:798)
        at resolvePromise (zone-evergreen.js:750)
        at zone-evergreen.js:860
        at ZoneDelegate.invokeTask (zone-evergreen.js:399)
        at Object.onInvokeTask (core.js:28522)
        at ZoneDelegate.invokeTask (zone-evergreen.js:398)
        at Zone.runTask (zone-evergreen.js:167)
        at drainMicroTaskQueue (zone-evergreen.js:569)
    

    Minimal troubleshooting

    It seems related to the fact that the group has a null validator. The code that applies a merge doesn't skip it and the following one will use an array like [null, ...]. Therefore, a deeper investigation is needed 😄

    What is the motivation / use case for changing the behavior?

    Environment

    
    Angular version: 11.2.3
    
    
    Browser:
    - [X] Brave Version 1.23.71 Chromium: 90.0.4430.72 (Official Build) (x86_64)
    - [ ] Chrome (desktop) version XX
    - [ ] Chrome (Android) version XX
    - [ ] Chrome (iOS) version XX
    - [ ] Firefox version XX
    - [ ] Safari (desktop) version XX
    - [ ] Safari (iOS) version XX
    - [ ] IE version XX
    - [ ] Edge version XX
     
    For Tooling issues:
    - Node version: 14.15.1
    - Platform: Mac
    
    Others:
    
    
    Reviewed by tafax at 2021-05-01 11:44
  • 5. fix: handle 'any' values properly in 'patchValue'

    PR Checklist

    Please check if your PR fulfills the following requirements:

    • [X] The commit message follows our guidelines: CONTRIBUTING.md#commit
    • [X] Tests for the changes have been added (for bug fixes / features)
    • [ ] Docs have been added / updated (for bug fixes / features)

    PR Type

    What kind of change does this PR introduce?

    [X] Bugfix
    [ ] Feature
    [ ] Code style update (formatting, local variables)
    [ ] Refactoring (no functional changes, no api changes)
    [ ] Build related changes
    [ ] CI related changes
    [ ] Documentation content changes
    [ ] Other... Please describe:
    

    What is the current behavior?

    patchValue doesn't handle any properly.

    Issue Number: Fixes #58

    What is the new behavior?

    patchValue handle any properly.

    Does this PR introduce a breaking change?

    [ ] Yes
    [x] No
    
    Reviewed by rafaelss95 at 2020-11-19 12:45
  • 6. Deep partial update breaks build

    I'm submitting a...

    
    [x] Regression (a behavior that used to work and stopped working in a new release)
    [ ] Bug report  
    [ ] Performance issue
    [ ] Feature request
    [ ] Documentation issue or request
    [ ] Support request
    [ ] Other... Please describe:
    

    Current behavior

    interface FiltersForm {
      usersSelect: any;
      periodFrom: string;
      periodTo: string;
      dueDateFrom: string;
      dueDateTo: string;
    }
    
     public readonly form: FormGroup<FiltersForm> = this.formBuilder.group<FiltersForm>({
        usersSelect: null,
        periodFrom: '',
        periodTo: '',
        dueDateFrom: '',
        dueDateTo: ''
      });
    
     this.form.patchValue({ usersSelect: null });
    

    last line breaks build;

    ERROR in filters.component.ts:120:7 - err
    or TS2769: No overload matches this call.
      Overload 1 of 2, '(valueOrObservable: Observable<DeepPartial<ControlsValue<FiltersForm>>>, options?: Pick<ControlOptions, "onlySelf" | "em
    itEvent"> | undefined): Subscription', gave the following error.
        Argument of type '{ usersSelect: null; }' is not assignable to parameter of type 'Observable<DeepPartial<ControlsValue<FiltersForm>>>'.
          Object literal may only specify known properties, and 'usersSelect' does not exist in type 'Observable<DeepPartial<ControlsValue<Filte
    rsForm>>>'.
      Overload 2 of 2, '(valueOrObservable: DeepPartial<ControlsValue<FiltersForm>>, options?: Pick<ControlOptions, "onlySelf" | "emitEvent"> |
    undefined): void', gave the following error.
        Type 'null' is not assignable to type 'DeepPartial<any> | undefined'.
    
    120       this.form.patchValue({ usersSelect: null });
              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    filters.component.ts:28:3
        28   usersSelect: any;
             ~~~~~~~~~~~
        The expected type comes from property 'usersSelect' which is declared here on type 'DeepPartial<ControlsValue<FiltersForm>>'
    
    

    When change type of usersSelect to usersSelect: number | null; error disappears.

    Expected behavior

    Deep partials to work with any; DeepPartial = any;

    Minimal reproduction of the problem with instructions

    What is the motivation / use case for changing the behavior?

    Environment

    
    Angular version: X.Y.Z
    
    
    Browser:
    - [ ] Chrome (desktop) version XX
    - [ ] Chrome (Android) version XX
    - [ ] Chrome (iOS) version XX
    - [ ] Firefox version XX
    - [ ] Safari (desktop) version XX
    - [ ] Safari (iOS) version XX
    - [ ] IE version XX
    - [ ] Edge version XX
     
    For Tooling issues:
    - Node version: XX  
    - Platform:  
    
    Others:
    
    
    Reviewed by criskrzysiu at 2020-11-18 20:43
  • 7. Reactive form testing won't validate the form correctly

    I'm submitting a...

    
    [x] Bug report 
    
    

    Current behavior

    When testing a simple component that has a setup form method on init the test for an invalid form fails. The form is valid without having values. Swapping the ngneat form builder for the Agnualr form builder from angular core it works fine. The test will output the form's invalid property as true.

    Logging the output in the browser works as expected.

    Jest is used as a test runner

    Expected behavior

    The test should output an invalid form after setup.

    Minimal reproduction of the problem with instructions

    // component
    @Component({
      selector: 'user-login',
      changeDetection: ChangeDetectionStrategy.OnPush,
      styleUrls: ['./login.component.scss'],
      templateUrl: './login.component.html'
    })
    export class LoginComponent implements OnInit {
      form;
    
    ... 
    
      constructor(
        private fb: FormBuilder,
      ) {}
      
      ngOnInit(): void {
        this.form = this.createForm();
        // console.log("component", this.form.valid)
        // console.log(this.form)
      }
      
      private createForm() {
        return this.fb.group({
          username: ['', Validators.required],
          password: ['', [Validators.required, Validators.minLength(10)]]
        });
      }
    }
    
    // test
    import { ReactiveFormsModule } from '@angular/forms';
    import { FormBuilder, FormGroup } from '@ngneat/reactive-forms';
    
    ...
    beforeEach(waitForAsync(() => {
       ...
    
        TestBed.configureTestingModule({
          declarations: [LoginComponent],
          imports: [
            ReactiveFormsModule
          ],
          providers: [
            FormBuilder,
            { provide: UserFacade, useValue: userFacade }
          ]
        }).compileComponents();
      }));
      
      beforeEach(async () => {
        fixture = TestBed.createComponent(LoginComponent);
        component = fixture.componentInstance;
      });
    
    ...
      
      describe('form invalid', () => {
          it('should NOT call userFacade.login', () => {
            component.ngOnInit();
            console.log(component.form.valid)     // ouput is true
          });
    ...
    

    Environment

    
    Angular version: 10.1.0
    ng neat reactive forms version: 1.3.1
    
    Browser:
    - [x] Chrome (desktop) version 85.0.4183.102
    - [x] Firefox (desktop) version 80.0.1
    
     
    For Tooling issues:
    - Node version: 12.18.3
    - Platform: Macos
    
    
    Reviewed by yackinn at 2020-09-17 11:16
  • 8. ControlsOf doesn't work with optional fields

    Is this a regression?

    Yes

    Description

    I am migrating our current form service to the latest version of this lib and encounter an issue with strictNullChecks.

    When I use new FormGroup({} as ControlsOf<T>) where T is a Model defined in a Component, it seems it cannot recognize it properly.

    I bet I am just missing something but since it is experimental, I thought it could also be a bug in ControlOf, I tried some different things without it, but nothing works 100%.

    I am willed to contribute, but atm I have no clue where to start really. Everything seems right, expect the part where it tries to type to an Observable (see error below).

    Please provide a link to a minimal reproduction of the bug

    https://codesandbox.io/s/form-ngneat-1v2hy?file=/src/app/app.component.ts

    Please provide the exception or error you saw

    No overload matches this call.
      Overload 1 of 2, '(valueOrObservable: Observable<ValuesOf<ControlsOf<FormModel>>>, options?: Object | undefined): Subscription', gave the following error.
        Argument of type '{ firstname: string | undefined; lastname: string | undefined; username: string; gender: Gender | undefined; }' is not assignable to parameter of type 'Observable<ValuesOf<ControlsOf<FormModel>>>'.
          Object literal may only specify known properties, and 'firstname' does not exist in type 'Observable<ValuesOf<ControlsOf<FormModel>>>'.ts(2769)
    

    Please provide the environment you discovered this bug in

    No response

    Anything else?

    No response

    Do you want to create a pull request?

    Yes

    Reviewed by muuvmuuv at 2021-10-19 20:21
  • 9. Memory Leak

    I'm submitting a...

    
    [ ] Regression (a behavior that used to work and stopped working in a new release)
    [ ] Bug report  
    [x] Performance issue
    [ ] Feature request
    [ ] Documentation issue or request
    [ ] Support request
    [ ] Other... Please describe:
    

    Current behavior

    I'm investigating some memory bloat in a large form, and noticed that a lot of the form controls are being retained when navigating to/back to the page that contains it, as evidenced by the memory profiler in chrome:

    Screenshot 2021-01-21 at 13 34 47

    While looking through the code: https://github.com/ngneat/reactive-forms/blob/2a564549ac653b20db298f2bc01739b7ab164f84/projects/ngneat/reactive-forms/src/lib/formControl.ts#L43-L54

    It appears that none of these subscriptions or their Subjects are ever completed.

    In the case of my form it looks like around 50000 observables are leaked every time I navigate to the specific tab that contains the form.

    Note that I'm unsubscribing from the ones that I'm using, but I'm seeing leaks of others I don't use at all, such as errors$

    Expected behavior

    Subscriptions should be completed somehow.

    Minimal reproduction of the problem with instructions

    Should be reproducible in any app by default as long as the form is being re-created. Such as wrapping it in an *ngIf. I can't find a working stackblitz or I would've added a repro myself.

    What is the motivation / use case for changing the behavior?

    Prevents memory leaks.

    Environment

    
    Angular version: 11.1.0
    
    
    
    	                                    
    Reviewed by andreialecu at 2021-01-21 12:00
  • 10. FormControl typed with object doesn't expose enabledWhile (and others) as opposed to one typed with primitives

    I have a typed FormGroup that contains a few FormControls, some of them bind to primitive types like number or string, and others to objects of my app.

    Those FormControls binded to objects are not showing properties like enabledWhile or disabledWhile when I try to call them by mean of this.formGroup.getControl('myObject').enabledWhile(...).

    Is this on purpose or not? If so, why?

    Reviewed by CesarD at 2020-11-30 15:35
  • 11. Imports

    @Coly010 @itayod these are the imports we need to support both in eslint and schematics:

    Validators, AbstractControl, ValidatorFn, AsyncValidatorFn, ControlValueAccessor, Validator

    Reviewed by NetanelBasal at 2020-05-25 14:12
  • 12. Missing get method model typings

    Is this a regression?

    Yes

    Description

    group: FormGroup<TestModel> = new FormGroup({
       id: 1,
    });
    
    // should have error here as there's no 123 key in TestModel
    ctrl = this.group.get('123');
    
    interface TestModel {
      id: number;
    }
    

    There's no intellisense when typing in control name as an argument so nothing prevents from entering anything but keys of real model.

    It worked back there when it was getControl method (~v.1.7).

    Please provide a link to a minimal reproduction of the bug

    No response

    Please provide the exception or error you saw

    No response

    Please provide the environment you discovered this bug in

    "typescript": 4.4.4,
    "@ngneat/reactive-forms": 4.1.0
    

    Anything else?

    No response

    Do you want to create a pull request?

    No

    Reviewed by strigefleur at 2022-05-05 15:00
  • 13. Incorrect get method typings

    Is this a regression?

    No

    Description

    Docs mention that I can get control by using get method:

    const name = group.get('name'); // FormControl<string>
    

    Actual result is

    const name = group.get('name'); // string
    

    Please provide a link to a minimal reproduction of the bug

    No response

    Please provide the exception or error you saw

    No response

    Please provide the environment you discovered this bug in

    "typescript": 4.4.4,
    "@ngneat/reactive-forms": 4.1.0
    

    Anything else?

    No response

    Do you want to create a pull request?

    No

    Reviewed by strigefleur at 2022-05-05 14:56
  • 14. build(deps): bump nanoid from 3.1.30 to 3.3.2

    Bumps nanoid from 3.1.30 to 3.3.2.

    Changelog

    Sourced from nanoid's changelog.

    3.3.2

    • Fixed enhanced-resolve support.

    3.3.1

    • Reduced package size.

    3.3

    • Added size argument to function from customAlphabet (by Stefan Sundin).

    3.2

    • Added --size and --alphabet arguments to binary (by Vitaly Baev).

    3.1.32

    • Reduced async exports size (by Artyom Arutyunyan).
    • Moved from Jest to uvu (by Vitaly Baev).

    3.1.31

    • Fixed collision vulnerability on object in size (by Artyom Arutyunyan).
    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    Reviewed by dependabot[bot] at 2022-04-10 20:09
  • 15. build(deps): bump follow-redirects from 1.14.5 to 1.14.8

    Bumps follow-redirects from 1.14.5 to 1.14.8.

    Commits
    • 3d81dc3 Release version 1.14.8 of the npm package.
    • 62e546a Drop confidential headers across schemes.
    • 2ede36d Release version 1.14.7 of the npm package.
    • 8b347cb Drop Cookie header across domains.
    • 6f5029a Release version 1.14.6 of the npm package.
    • af706be Ignore null headers.
    • See full diff in compare view

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    Reviewed by dependabot[bot] at 2022-02-14 22:08
react-hook-form is an awsome library which provide a neat solution for building forms.

react-hook-form is an awsome library which provide a neat solution for building forms. However, there are many rules for the API which may be missed by the developer. This plugin aims to check those rules automatically thourgh ESLint. Thus brings better DX for react-hook-form.

May 7, 2022
Formst is a model-driven library for quickly building high-performance forms in React.

Formst is a model-driven library for quickly building high-performance forms in React. Unlike most form libraries that are UI-First, Formst is Data-First.

Apr 24, 2022
📋 Primitive for building forms in React

formin Primitive for building forms in react I built formin to serve as a tiny (~1.5kb) alternative to other form solutions in react with these goals

Jan 22, 2022
Forms rendered from JSON

Forms rendered from JSON

Sep 28, 2021
Reactjs-forms: a React package that contains a few React components and hook system

reactjs-forms is a React package that contains a few React components and hook system. The components are almost the same as React syntactic form elements.So you may use all attributes which you can use them on syntactic form elements.

Mar 25, 2022
RRForms is a simple utility that adds reactive conditional disabling/enabling & addition/removal of validators to Angular 2+ Reactive Forms.
RRForms is a simple utility that adds reactive conditional disabling/enabling & addition/removal of validators to Angular 2+ Reactive Forms.

ngx-rrforms Reactive Reactive Forms for Angular 2+. Self-reactivity and more (soon). CONTENT TABLE Usage examples conditional disabling conditional va

Jan 18, 2022
Angular like reactive forms in React.
Angular like reactive forms in React.

React Reactive Forms It's a library inspired by the Angular's Reactive Forms, which allows to create a tree of form control objects in the component c

Apr 15, 2022
Inventory App - a SPA (Single Page Application) project developed with Angular using Reactive Forms and VMware's Clarity components.
 Inventory App - a SPA (Single Page Application) project developed with Angular using Reactive Forms and VMware's Clarity components.

InventoryApp This project was generated with Angular CLI version 13.0.1. Development server Run ng serve for a dev server. Navigate to http://localhos

Apr 21, 2022
Implementation Angular 13 Form Validation with Submit using Reactive Forms Module and Bootstrap 4.

Implementation Angular 13 Form Validation with Submit using Reactive Forms Module and Bootstrap 4. The form has: Full Name: required Username: require

May 5, 2022
A teaching tool to introduce youth to the concept of investing and the benefits of long-term investment. (React, NodeJS, Express, PostgreSQL, Socket.io)
A teaching tool to introduce youth to the concept of investing and the benefits of long-term investment. (React, NodeJS, Express, PostgreSQL, Socket.io)

SmartMoney is an app that teachers can use to introduce students to the concept of investment and show them how it is a smart way to grow their money.

Apr 4, 2022
Angular - Best Practices of Reactive Programming.

This project is a study system in Angular and implementing using the best practices of reactive programming.

Apr 5, 2022
This application contains end to end non blocking reactive flow. From Bankend( Database, Spring) and FrontEnd Angular

SpringReactiveApp This application has following projects Spring boot reactive with Mongo Database Angular with EventSource for reactive(non-blocking

Oct 26, 2021
Consuming series data from marvel api and implementing features like infinite scroll, reactive search and lazy loading on images with Angular.

Consuming series data from marvel api and implementing features like infinite scroll, reactive search and lazy loading on images with Angular.

Apr 4, 2022
Zero-Configuration Reactive forms for Svelte
Zero-Configuration Reactive forms for Svelte

Formula + Beaker Δ→ Reactive Forms for Svelte Documentation Changelog svelte-formula is a Library for use with Svelte that super-charges your ability

May 1, 2022
Reactive forms for react and react native, using hooks and [email protected]
Reactive forms for react and react native, using hooks and Mobx@6

Reactive forms for react and react native, using hooks and [email protected] Installation: npm install -s react-fluid-form mobx mobx-react yup lodash // or: yarn

Apr 4, 2022
📓 The UI component explorer. Develop, document, & test React, Vue, Angular, Web Components, Ember, Svelte & more!
📓 The UI component explorer. Develop, document, & test React, Vue, Angular, Web Components, Ember, Svelte & more!

Build bulletproof UI components faster Storybook is a development environment for UI components. It allows you to browse a component library, view the

May 19, 2022
Use React Components in Angular

Note: For a more modern alternative to ngReact, we recommend react2angular, angular2react, and ngimport. ngReact The React.js library can be used as a

May 9, 2022
Input mask for React, Angular, Ember, Vue, & plain JavaScript
Input mask for React, Angular, Ember, Vue, & plain JavaScript

⚠️ This library is not maintained. Pull-requests and issues are not monitored. Alternatives to text-mask include: https://github.com/uNmAnNeR/imaskjs

May 16, 2022
Mobile app development framework and SDK using HTML5 and JavaScript. Create beautiful and performant cross-platform mobile apps. Based on Web Components, and provides bindings for Angular 1, 2, React and Vue.js.
Mobile app development framework and SDK using HTML5 and JavaScript. Create beautiful and performant cross-platform mobile apps. Based on Web Components, and provides bindings for Angular 1, 2, React and Vue.js.

Onsen UI - Cross-Platform Hybrid App and PWA Framework Onsen UI is an open source framework that makes it easy to create native-feeling Progressive We

May 11, 2022
Input mask for React, Angular, Ember, Vue, & plain JavaScript
Input mask for React, Angular, Ember, Vue, & plain JavaScript

Text Mask is an input mask library. It can create input masks for phone, date, currency, zip code, percentage, email, and literally anything!

May 16, 2022