Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@ngneat/reactive-forms

Package Overview
Dependencies
Maintainers
3
Versions
53
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@ngneat/reactive-forms

(Angular Reactive) Forms with Benefits

  • 1.0.0-beta.5
  • Source
  • npm
  • Socket score

Version published
Weekly downloads
4K
decreased by-54.13%
Maintainers
3
Weekly downloads
 
Created
Source


Test MIT commitizen PRs styled with prettier All Contributors ngneat spectator

(Angular Reactive) Forms with Benefits 😉

How many times have you told yourself "I wish Angular Reactive Forms would support types", or "I really want 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! 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; One schematics command, and you're done!

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

🔮 Features

✅ Offers seamless FormControl, FormGroup, FormArray Replacement
✅ Allows Typed Forms!
✅ Provides Reactive Queries
✅ Provides Helpful Methods
✅ Typed and DRY ControlValueAccessor
✅ Typed FormBuilder

👉 npm install @ngneat/reactive-forms

Table of Contents

Control Type

Each AbstractControl takes a generic, which serves as the type for any method exposed by Angular or this library:

Use it with a FormControl:

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

const control = new FormControl('');
control.valueChanges$.subscribe(value => {
  // value is typed as string
});

Use it with a FormGroup:

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

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

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

// typed as Profile
profileForm.setValue(new Profile());
// typed as Partial<Profile>
profileForm.patchValue(new Profile());

Use it with a FormArray:

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

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

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

Control Queries

valueChanges$

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.valueChanges$.subscribe(value => ...);

disabledChanges$

Observes the control's disable status.

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

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

enabledChanges$

Observes the control's enable status.

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

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

statusChanges$

Observes the control's status.

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

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

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

touchChanges$

Observes the control's touched status.

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

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

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

dirtyChanges$

Observes the control's dirty status.

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

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

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

errorsChanges$

Observes the control's errors.

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

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

select()

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

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

const control = new FormGroup<Person>(...);
control.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 control = new FormGroup<Person>();
control.setValue(query.select('formValue'));

patchValue()

In addition to the built-in method functionality, it can take an observable or a callback function.

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

const control = new FormGroup<Person>();
control.patchValue(query.select('formValue'));

control.patchValue(state => ({
  ...state,
  name: state.someProp ? 'someName' : 'anotherName'
}));

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(query.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(query.select('isEnabled'));

mergeValidators()

Unlike the built-in setValidator() method, it persists any existing validators.

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

const control = new FormControl('', Validators.required);
control.mergeValidators(Validators.minLength(2));
control.mergeAsyncValidators(...);

markAllAsDirty()

Marks all the group's controls as dirty.

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

const control = new FormGroup<Person>();
control.markAllAsDirty();

validateOn()

Takes an observable that emits a response, which is either null or an error object (ValidationErrors). The control's setErrors() method is called whenever the source emits.

const passwordValidator = combineLatest([
  this.signup.select(state => state.password),
  this.signup.select(state => state.repeatPassword)
]).pipe(
  map(([password, repeat]) => {
    return password === repeat
      ? null
      : {
          isEqual: false
        };
  })
);

this.signup.validateOn(passwordValidator);

hasErrorAndTouched()

A syntactic sugar method to be used in the template:

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

this.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';

this.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);

getControl()

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

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

const group = new FormGroup<Person>(...);
const nameControl = group.getControl('name');
const nestedFieldControl = group.getControl('nested', 'field');

Control Path

The array path variation of hasError(), getError(), and get() is now typed:

group.get(['phone', 'num']);
group.hasError('required', ['phone', 'num']);
group.getError('required', ['phone', 'num']);

Control Errors

Each AbstractControl takes a second generic, which serves as the type of the errors:

type MyErrors = { isEqual: false };

const control = new FormControl<string, MyErrors>();
control.getError('isEqual'); // keyof MyErrors
control.hasError('isEqual'); // keyof MyErrors

// error type is MyErrors['isEqual']
const error = control.getError('isEqual'); // keyof MyErrors

The library provides a type for the built-in Angular validators types:

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

const control = new FormControl<string, NgValidatorsErrors>();

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) {
    // value is typed a 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';

const fb = new FormBuilder();
// Returns a FormGroup<{name: string, id: number}>
const group = fb.group({ name: 'ngneat', id: 1 });

interface User {
  userName: string;
  email: string;
}

// We'll get an error because "id" does not exist in type `User`
const userGroup: FormGroup<User> = fb.group({ id: 1, userName: 'User', email: 'Email' });

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, Validators, Validator from @angular/forms.

Check out the documentation.

Schematics

The command will replace entities coming from @angular/reactive-forms with @ngneat/reactive-forms.

ng g @ngneat/reactive-forms:migrate

Further information about the script can be found here.

Contributors ✨

Thanks goes to these wonderful people (emoji key):


Netanel Basal

💻 📖 🤔 🚇

Colum Ferry

💻 📖

Dan Roujinsky

💻 📖 🤔

Inbal Sinai

📖

Itay Oded

💻 🤔 📖 ⚠️ 🔧

This project follows the all-contributors specification. Contributions of any kind welcome!

Keywords

FAQs

Package last updated on 31 May 2020

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc