Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

(Angular Reactive) Forms with Benefits 😉

NotificationsYou must be signed in to change notification settings

ngneat/reactive-forms

Repository files navigation


TestMITcommitizenPRsstyled with prettierngneat

(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 AngularAbstractControl, 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 theimport 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) seamlessFormControl,FormGroup,FormArray Replacement
✅ Allows Typed Forms!
✅ Provides Reactive Queries
✅ Provides Helpful Methods
✅ Typed and DRYControlValueAccessor
✅ TypedFormBuilder
✅ 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 thetype of the control. Thistype is then used to enhance every method exposed by Angular or this library.

Use it with aFormControl:

import{FormControl}from'@ngneat/reactive-forms';constcontrol=newFormControl<string>();// Or auto infer it based on the initial valueconstcontrol=newFormControl('');control.valueChanges.subscribe(value=>{// value is typed as string});

Use it with aFormArray:

import{FormArray,FormControl}from'@ngneat/reactive-forms';constcontrol=newFormArray<string>([newFormControl('')]);control.value$.subscribe(value=>{// value is typed as string[]});

If you use aFormGroup, it'll automatically infer thetype based on thecontrols you supply:

import{FormGroup,FormControl}from'@ngneat/reactive-forms';constprofileForm=newFormGroup({firstName:newFormControl(''),lastName:newFormControl(''),address:newFormGroup({street:newFormControl(''),city:newFormControl('')})});profileForm.setValue(newProfile());profileForm.patchValue({firstName:'Netanel'});

You can use theexperimentalControlsOf feature if you want to force aFormGroup to implement an externaltype:

import{FormGroup,FormControl,ControlsOf}from'@ngneat/reactive-forms';interfaceProfile{firstName:string;lastName:string;address:{street:string;city:string;};}constprofileForm=newFormGroup<ControlsOf<Profile>>({firstName:newFormControl(''),lastName:newFormControl(''),address:newFormGroup({street:newFormControl(''),city:newFormControl('')})});

Gotchas

  • When usingarray types, it'll automatically infer it asFormArray. If you need aFormControl, you must set it within your interface explicitly:
interfaceUser{name:string;// 👇🏻skills:FormControl<string[]>;}
  • Optional fields will only work with top-level values, and will not work withFormGroup:
interfaceUser{name?:string;foo?:string[];// 👇🏻 will not worknested?:{id:string;};}

Control Queries

value$

Observes the control's value. Unlike the behavior of the built-invalueChanges observable, it emits the currentrawValueimmediately (which means you'll also get the values ofdisabled controls).

import{FormControl}from'@ngneat/reactive-forms';constcontrol=newFormControl('');control.value$.subscribe(value=> ...);

disabled$

Observes the control'sdisable status.

import{FormControl}from'@ngneat/reactive-forms';constcontrol=newFormControl('');control.disabled$.subscribe(isDisabled=> ...);

enabled$

Observes the control'senable status.

import{FormControl}from'@ngneat/reactive-forms';constcontrol=newFormControl('');control.enabled$.subscribe(isEnabled=> ...);

invalid$

Observes the control'sinvalid status.

import{FormControl}from'@ngneat/reactive-forms';constcontrol=newFormControl('');control.invalid$.subscribe(isInvalid=> ...);

valid$

Observes the control'svalid status.

import{FormControl}from'@ngneat/reactive-forms';constcontrol=newFormControl('');control.valid$.subscribe(isValid=> ...);

status$

Observes the control'sstatus.

import{FormControl}from'@ngneat/reactive-forms';constcontrol=newFormControl('');control.status$.subscribe(status=> ...);

Thestatus istyped asControlState (valid, invalid, pending or disabled).

touch$

Observes the control'stouched status.

import{FormControl}from'@ngneat/reactive-forms';constcontrol=newFormControl('');control.touch$.subscribe(isTouched=> ...);

This emits a valueonly whenmarkAsTouched, ormarkAsUnTouched, has been called.

dirty$

Observes the control'sdirty status.

import{FormControl}from'@ngneat/reactive-forms';constcontrol=newFormControl('');control.dirty$.subscribe(isDirty=> ...);

This emits a valueonly whenmarkAsDirty, ormarkAsPristine, has been called.

errors$

Observes the control'serrors.

import{FormControl}from'@ngneat/reactive-forms';constcontrol=newFormControl('');control.errors$.subscribe(errors=> ...);

select()

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

import{FormGroup}from'@ngneat/reactive-forms';constgroup=newFormGroup({name:newFormControl('')});group.select(state=>state.name).subscribe(name=> ...)

Control Methods

setValue()

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

import{FormGroup}from'@ngneat/reactive-forms';constgroup=newFormGroup({name:newFormControl('')});group.setValue(store.select('formValue'));

patchValue()

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

import{FormGroup}from'@ngneat/reactive-forms';constgroup=newFormGroup({name:newFormControl('')});group.patchValue(store.select('formValue'));

disabledWhile()

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

import{FormControl}from'@ngneat/reactive-forms';constcontrol=newFormControl('');control.disabledWhile(store.select('isDisabled'));

enabledWhile()

Takes an observable that emits aboolean indicating whether toenable the control.

import{FormControl}from'@ngneat/reactive-forms';constcontrol=newFormControl('');control.enabledWhile(store.select('isEnabled'));

markAllAsDirty()

Marks all the group's controls asdirty.

import{FormGroup}from'@ngneat/reactive-forms';constcontrol=newFormGroup();control.markAllAsDirty();

hasErrorAndTouched()

A syntactic sugar method to be used in the template:

import{FormControl}from'@ngneat/reactive-forms';constcontrol=newFormControl('',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';constcontrol=newFormControl('',Validators.required);
<span*ngIf="control.hasErrorAndDirty('required')"></span>

setEnable()

Sets whether the control isenabled.

import{FormControl}from'@ngneat/reactive-forms';constcontrol=newFormControl('');control.setEnable();control.setEnable(false);

setDisable()

Sets whether the control isdisabled.

import{FormControl}from'@ngneat/reactive-forms';constcontrol=newFormControl('');control.setDisable();control.setDisable(false);

get()

A method withtyped parameters which obtains a reference to a specific control.

import{FormGroup}from'@ngneat/reactive-forms';constgroup=newFormGroup({name:newFormControl(''),address:newFormGroup({street:newFormControl(''),city:newFormControl('')})});constname=group.get('name');// FormControl<string>constcity=group.get(['address','city']);// FormControl<string>// Don't use it like thisgroup.get('address.city')// AbstractControl

mergeErrors()

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

import{FormGroup}from'@ngneat/reactive-forms';constgroup=newFormGroup(...);group.mergeErrors({customError:true});

removeError()

Remove an error by key from the control.

import{FormGroup}from'@ngneat/reactive-forms';constgroup=newFormGroup(...);group.removeError('customError');

FormArray methods

remove()

Remove a control from an array based on its value

import{FormArray}from'@ngneat/reactive-forms';constarray=newFormArray<string>(...);// Remove empty stringsarray.remove('')

removeIf()

Remove a control from an array based on a predicate

import{FormArray}from'@ngneat/reactive-forms';constarray=newFormArray(...);// Only keep addresses in NYCarray.removeIf((control)=>control.get('address').get('city').value!=='New York')

Control Operators

EachvalueChanges orvalues$ takes an operatordiff(), which emits only changed parts of form:

import{FormGroup,FormControl,diff}from'@ngneat/reactive-forms';constcontrol=newFormGroup({name:newFormControl(''),phone:newFormGroup({num:newFormControl(''),prefix:newFormControl('')}),skills:newFormArray<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 atyped version ofControlValueAccessor, which already implementsregisterOnChange andregisterOnTouched 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}]})exportclassMyCheckboxComponentextendsControlValueAccessor<boolean>{writeValue(value:boolean){}// `this.onChange`, and `this.onTouched` are already here!}

Note that you can also use it asinterface.

Form Builder

We also introduce atyped version ofFormBuilder which returns atypedFormGroup,FormControl andFormArray with all our sweet additions:

import{FormBuilder}from'@ngneat/reactive-forms';constructor(privatefb:FormBuilder){}constgroup=this.fb.group({name:'ngneat',id:1});group.get('name')// FormControl<string>

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

Persist Form

Automatically persist theAbstractControl's value to the given storage:

import{persistControl}from'@ngneat/reactive-forms';constgroup=newFormGroup(...);constunsubscribe=persistControl(group,'profile').subscribe();

ThepersistControl function will also set theFromGroup value to the latest state available in the storage before subscribing to value changes.

PersistOptions

Change the target storage ordebounceTime value by providing options as a second argument in thepersist function call.

OptionDescriptionDefault
debounceTimeUpdate delay in ms between value changes250
managerA manager implementing thePersistManager interfaceLocalStorageManager
arrControlFactoryFactory functions forFormArray
persistDisabledControlsDefines whether values of disabled controls should be persistedfalse

By default the library providesLocalStorageManager andSessionStorageManager. It's possible to store the form value into a custom storage. Just implement thePersistManager interface, and use it when calling thepersistControl function.

exportclassStateStoreManager<T>implementsPersistManager<T>{setValue(key:string,data:T){     ...}getValue(key:string){    ...}}exportclassFormComponentimplementsOnInit{group=newFormGroup();ngOnInit(){persist(this.group,'profile',{manager:newStateStoreManager()}).subscribe();}}
UsingFormArray Controls.

When working with aFormArray, it's required to pass afactory function that defines how to create thecontrols inside theFormArray.

constgroup=newFormGroup({skills:newFormArray<string>([])});persist(group,'profile',{arrControlFactory:{skills:value=>newFormControl(value)}});

Because the form is strongly typed, you can only configure factories for properties that are of typeArray. The library makes it also possible to correctly infer the type ofvalue 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 thedocumentation.

About

(Angular Reactive) Forms with Benefits 😉

Topics

Resources

Code of conduct

Contributing

Stars

Watchers

Forks

Sponsor this project

 

Packages

No packages published

Contributors27


[8]ページ先頭

©2009-2025 Movatter.jp