Research
Security News
Quasar RAT Disguised as an npm Package for Detecting Vulnerabilities in Ethereum Smart Contracts
Socket researchers uncover a malicious npm package posing as a tool for detecting vulnerabilities in Etherium smart contracts.
@ngneat/forms-manager
Advanced tools
The Foundation for Proper Form Management in Angular
✅ Allows Typed Forms!
✅ Auto persists the form's state upon user navigation.
✅ Provides an API to reactively querying any form, from anywhere.
✅ Persist the form's state to local storage.
✅ Built-in dirty functionality.
NgFormsManager
lets you sync Angular’s FormGroup
, FormControl
, and FormArray
, via a unique store created for that purpose. The store will hold the controls' data like values, validity, pristine status, errors, etc.
This is powerful, as it gives you the following abilities:
The goal in creating this was to work with the existing Angular form ecosystem, and save you the trouble of learning a new API. Let’s see how it works:
First, install the library:
npm i @ngneat/forms-manager
Then, create a component with a form:
import { NgFormsManager } from '@ngneat/forms-manager';
@Component({
template: `
<form [formGroup]="onboardingForm">
<input formControlName="name">
<input formControlName="age">
<input formControlName="city">
</form>
`
})
export class OnboardingComponent {
onboardingForm: FormGroup;
constructor(
private formsManager: NgFormsManager,
private builder: FormBuilder
) {}
ngOnInit() {
this.onboardingForm = this.builder.group({
name: [null, Validators.required],
age: [null, Validators.required],
city: [null, Validators.required]
});
this.formsManager.upsert('onboarding', this.onboardingForm);
}
ngOnDestroy() {
this.formsManager.unsubscribe('onboarding');
}
}
As you can see, we’re still working with the existing API in order to create a form in Angular. We’re injecting the NgFormsManager
and calling the upsert
method, giving it the form name and an AbstractForm
.
From that point on, NgFormsManager
will track the form
value changes, and update the store accordingly.
With this setup, you’ll have an extensive API to query the store and update the form from anywhere in your application:
valueChanges()
- Observe the control's valueconst value$ = formsManager.valueChanges('onboarding');
const nameValue$ = formsManager.valueChanges<string>('onboarding', 'name');
validityChanges()
- Whether the control is validconst valid$ = formsManager.validityChanges('onboarding');
const nameValid$ = formsManager.validityChanges('onboarding', 'name');
dirtyChanges()
- Whether the control is dirtyconst dirty$ = formsManager.dirtyChanges('onboarding');
const nameDirty$ = formsManager.dirtyChanges('onboarding', 'name');
disableChanges()
- Whether the control is disabledconst disabled$ = formsManager.disableChanges('onboarding');
const nameDisabled$ = formsManager.disableChanges('onboarding', 'name');
errorsChanges()
- Observe the control's errorsconst errors$ = formsManager.errorsChanges<Errors>('onboarding');
const nameErrors$ = formsManager.errorsChanges<Errors>('onboarding', 'name');
controlChanges()
- Observe the control's stateconst control$ = formsManager.controlChanges('onboarding');
const nameControl$ = formsManager.controlChanges<string>('onboarding', 'name');
getControl()
- Get the control's stateconst control = formsManager.getControl('onboarding');
const nameControl = formsManager.getControl<string>('onboarding', 'name');
controlChanges
and getControl
will return the following state:
{
value: any,
rawValue: object,
errors: object,
valid: boolean,
dirty: boolean,
invalid: boolean,
disabled: boolean,
touched: boolean,
pristine: boolean,
pending: boolean,
}
hasControl()
- Whether the control existsconst hasControl = formsManager.hasControl('onboarding');
patchValue()
- A proxy to the original patchValue
methodformsManager.patchValue('onboarding', value, options);
setValue()
- A proxy to the original setValue
methodformsManager.setValue('onboarding', value, options);
unsubscribe()
- Unsubscribe from the form's valueChanges
observable (always call it on ngOnDestroy
)formsManager.unsubscribe('onboarding');
formsManager.unsubscribe();
clear()
- Delete the form from the storeformsManager.clear('onboarding');
formsManager.clear();
destroy()
- Destroy the form (Internally calls clear
and unsubscribe
)formsManager.destroy('onboarding');
formsManager.destroy();
controlDestroyed()
- Emits when the control is destroyedformsManager.controlChanges('login').pipe(takeUntil(controlDestroyed('login')));
In the upsert
method, pass the persistState
flag:
formsManager.upsert(formName, abstractContorl, {
persistState: true;
});
The library exposes two helpers method for adding cross component validation:
export function setValidators(
control: AbstractControl,
validator: ValidatorFn | ValidatorFn[] | null
);
export function setAsyncValidators(
control: AbstractControl,
validator: AsyncValidatorFn | AsyncValidatorFn[] | null
);
Here's an example of how we can use it:
export class HomeComponent{
ngOnInit() {
this.form = new FormGroup({
price: new FormControl(null, Validators.min(10))
});
/*
* Observe the `minPrice` value in the `settings` form
* and update the price `control` validators
*/
this.formsManager.selectValue<number>('settings', 'minPrice')
.subscribe(minPrice => setValidators(this.form.get('price'), Validators.min(minPrice));
}
}
When working with a FormArray
, it's required to pass a factory
function that defines how to create the controls
inside the FormArray
. For example:
import { NgFormsManager } from '@ngneat/forms-manager';
export class HomeComponent {
skills: FormArray;
config: FormGroup;
constructor(private formsManager: NgFormsManager<FormsState>) {}
ngOnInit() {
this.skills = new FormArray([]);
/** Or inside a FormGroup */
this.config = new FormGroup({
skills: new FormArray([]),
});
this.formsManager
.upsert('skills', this.skills, { arrControlFactory: value => new FormControl(value) })
.upsert('config', this.config, {
arrControlFactory: { skills: value => new FormControl(value) },
});
}
ngOnDestroy() {
this.formsManager.unsubscribe();
}
}
NgFormsManager
can take a generic type where you can define the forms shape. For example:
interface AppForms = {
onboarding: {
name: string;
age: number;
city: string;
}
}
This will make sure that the queries are typed, and you don't make any mistakes in the form name.
export class OnboardingComponent {
constructor(private formsManager: NgFormsManager<AppForms>, private builder: FormBuilder) {}
ngOnInit() {
this.formsManager.valueChanges('onboarding').subscribe(value => {
// value now typed as AppForms['onboarding']
});
}
}
Note that you can split the types across files using a definition file:
// login-form.d.ts
interface AppForms {
login: {
email: string;
password: string
}
}
// onboarding.d.ts
interface AppForms {
onboarding: {
...
}
}
The library provides built-in support for the common "Is the form dirty?" question. Dirty means that the current control's value is different from the initial value. It can be useful when we need to toggle the visibility of a "save" button or displaying a dialog when the user leaves the page.
To start using it, you should set the withInitialValue
option:
@Component({
template: `
<button *ngIf="isDirty$ | async">Save</button>
`,
})
export class SettingsComponent {
isDirty$ = this.formsManager.initialValueChanged(name);
constructor(private formsManager: NgFormsManager<AppForms>) {}
ngOnInit() {
this.formsManager.upsert(name, control, {
withInitialValue: true,
});
}
}
You can override the default config by passing the NG_FORMS_MANAGER_CONFIG
provider:
import { NG_FORMS_MANAGER_CONFIG, NgFormsManagerConfig } from '@ngneat/forms-manager';
@NgModule({
declarations: [AppComponent],
imports: [ReactiveFormsModule],
providers: [
{
provide: NG_FORMS_MANAGER_CONFIG,
useValue: new NgFormsManagerConfig({
debounceTime: 1000, // defaults to 300
storage: {
key: 'NgFormManager',
},
}),
},
],
bootstrap: [AppComponent],
})
export class AppModule {}
Thanks goes to these wonderful people (emoji key):
Netanel Basal 💻 📖 🤔 | Colum Ferry 💻 📖 | Mehmet Erim 📖 | David Speirs 💻 📖 |
This project follows the all-contributors specification. Contributions of any kind welcome!
FAQs
Forms Manager library for Angular
The npm package @ngneat/forms-manager receives a total of 1,134 weekly downloads. As such, @ngneat/forms-manager popularity was classified as popular.
We found that @ngneat/forms-manager demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 3 open source maintainers collaborating on the project.
Did you know?
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.
Research
Security News
Socket researchers uncover a malicious npm package posing as a tool for detecting vulnerabilities in Etherium smart contracts.
Security News
Research
A supply chain attack on Rspack's npm packages injected cryptomining malware, potentially impacting thousands of developers.
Research
Security News
Socket researchers discovered a malware campaign on npm delivering the Skuld infostealer via typosquatted packages, exposing sensitive data.