All Projects โ†’ ngneat โ†’ Forms Manager

ngneat / Forms Manager

Licence: mit
๐Ÿฆ„ The Foundation for Proper Form Management in Angular

Programming Languages

typescript
32286 projects

Projects that are alternatives of or similar to Forms Manager

Card settings
A flutter package for building card based forms.
Stars: โœญ 256 (-29.48%)
Mutual labels:  forms
Reactive Forms
(Angular Reactive) Forms with Benefits ๐Ÿ˜‰
Stars: โœญ 276 (-23.97%)
Mutual labels:  forms
A11y styled form controls
Various styled accessible form controls
Stars: โœญ 335 (-7.71%)
Mutual labels:  forms
Survey Library
JavaScript Survey and Form Library
Stars: โœญ 3,060 (+742.98%)
Mutual labels:  forms
Forms
๐Ÿ“ Generating, validating and processing secure forms in PHP. Handy API, fully customizable, server & client side validation and mature design.
Stars: โœญ 272 (-25.07%)
Mutual labels:  forms
Surveykit
Android library to create beautiful surveys (aligned with ResearchKit on iOS)
Stars: โœญ 288 (-20.66%)
Mutual labels:  forms
react-gridforms
React components for Gridforms form layout
Stars: โœญ 29 (-92.01%)
Mutual labels:  forms
Deform
A Python HTML form library.
Stars: โœญ 357 (-1.65%)
Mutual labels:  forms
Tellform
โœ๏ธ Free Opensource Alternative to TypeForm or Google Forms โ›บ
Stars: โœญ 2,941 (+710.19%)
Mutual labels:  forms
Reagent Forms
Bootstrap form components for Reagent
Stars: โœญ 325 (-10.47%)
Mutual labels:  forms
Awesome Gravity Forms
A collection of third party add-ons for Gravity Forms plugin.
Stars: โœญ 267 (-26.45%)
Mutual labels:  forms
Formvuelate
Dynamic schema-based form rendering for VueJS
Stars: โœญ 262 (-27.82%)
Mutual labels:  forms
Airform
Functional HTML forms for Front-End Developers.
Stars: โœญ 307 (-15.43%)
Mutual labels:  forms
React Reactive Form
Angular like reactive forms in React.
Stars: โœญ 259 (-28.65%)
Mutual labels:  forms
Formous
Simple and elegant form-handling for React - ABANDONWARE
Stars: โœญ 335 (-7.71%)
Mutual labels:  forms
scrupulous
Simple inline form validation using HTML5 attributes that plays nicely with Bootstrap
Stars: โœญ 12 (-96.69%)
Mutual labels:  forms
Reform
Reasonably making forms sound good ๐Ÿ› 
Stars: โœญ 282 (-22.31%)
Mutual labels:  forms
Django Fobi
Form generator/builder application for Django done right: customisable, modular, user- and developer- friendly.
Stars: โœญ 360 (-0.83%)
Mutual labels:  forms
Formik Persist
๐Ÿ’พ Persist and rehydrate a Formik form to localStorage
Stars: โœญ 345 (-4.96%)
Mutual labels:  forms
Vue Flow Form
Create conversational conditional-logic forms with Vue.js.
Stars: โœญ 315 (-13.22%)
Mutual labels:  forms

The Foundation for Proper Form Management in Angular

Build Status commitizen PRs coc-badge semantic-release styled with prettier All Contributors

๐Ÿ”ฎ Features

โœ… 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:

  1. It will automatically save the current control value and update the form value according to the value in the store when the user navigates back to the form.
  2. It provides an API so you can query a formโ€™s values and properties from anywhere. This can be useful for things like multi-step forms, cross-component validation and more.
  3. It can persist the form's state to local storage.

Buy Me A Coffee

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:

Installation

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:

API

  • valueChanges() - Observe the control's value
const value$ = formsManager.valueChanges('onboarding');
const nameValue$ = formsManager.valueChanges<string>('onboarding', 'name');
  • validityChanges() - Whether the control is valid
const valid$ = formsManager.validityChanges('onboarding');
const nameValid$ = formsManager.validityChanges('onboarding', 'name');
  • dirtyChanges() - Whether the control is dirty
const dirty$ = formsManager.dirtyChanges('onboarding');
const nameDirty$ = formsManager.dirtyChanges('onboarding', 'name');
  • disableChanges() - Whether the control is disabled
const disabled$ = formsManager.disableChanges('onboarding');
const nameDisabled$ = formsManager.disableChanges('onboarding', 'name');
  • errorsChanges() - Observe the control's errors
const errors$ = formsManager.errorsChanges<Errors>('onboarding');
const nameErrors$ = formsManager.errorsChanges<Errors>('onboarding', 'name');
  • controlChanges() - Observe the control's state
const control$ = formsManager.controlChanges('onboarding');
const nameControl$ = formsManager.controlChanges<string>('onboarding', 'name');
  • getControl() - Get the control's state
const 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,
   untouched: boolean,
}
  • hasControl() - Whether the control exists
const hasControl = formsManager.hasControl('onboarding');
  • patchValue() - A proxy to the original patchValue method
formsManager.patchValue('onboarding', value, options);
  • setValue() - A proxy to the original setValue method
formsManager.setValue('onboarding', value, options);
  • reset() - A proxy to the original reset method
formsManager.reset('onboarding', value, options);
  • markAllAsTouched() - A proxy to the original markAllAsTouched method
formsManager.markAllAsTouched('onboarding', options);
  • markAsTouched() - A proxy to the original markAsTouched method
formsManager.markAsTouched('onboarding', options);
  • markAllAsDirty() - Marks the control and all its descendant controls as dirty
formsManager.markAllAsDirty('onboarding', options);
  • markAsDirty() - A proxy to the original markAsDirty method
formsManager.markAsDirty('onboarding', options);
  • markAsPending() - A proxy to the original markAsPending method
formsManager.markAsPending('onboarding', options);
  • markAsPristine() - A proxy to the original markAsPristine method
formsManager.markAsPristine('onboarding', options);
  • markAsUntouched() - A proxy to the original markAsUntouched method
formsManager.markAsUntouched('onboarding', 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 store
formsManager.clear('onboarding');
formsManager.clear();
  • destroy() - Destroy the form (Internally calls clear and unsubscribe)
formsManager.destroy('onboarding');
formsManager.destroy();
  • controlDestroyed() - Emits when the control is destroyed
formsManager.controlChanges('login').pipe(takeUntil(controlDestroyed('login')));

Persist to Local Storage

In the upsert method, pass the persistState flag:

formsManager.upsert(formName, abstractContorl, {
  persistState: true;
});

Validators

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.valueChanges<number>('settings', 'minPrice')
     .subscribe(minPrice => setValidators(this.form.get('price'), Validators.min(minPrice));
  }
}

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. 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 Generic Type

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: {
    ...
  }
}

Using the Dirty Functionality

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,
    });
  }
}

setInitialValue(name, value) - Set the initial form's value

formsManager.setInitialValue('form', initialValue);

getInitialValue(name) - Get the initial value or undefined if not exist.

formsManager.getInitialValue('form');

NgFormsManager Config

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 {}

Contributors โœจ

Thanks goes to these wonderful people (emoji key):


Netanel Basal

๐Ÿ’ป ๐Ÿ“– ๐Ÿค”

Colum Ferry

๐Ÿ’ป ๐Ÿ“–

Mehmet Erim

๐Ÿ“–

David Speirs

๐Ÿ’ป ๐Ÿ“–

Emmanuel De Saint Steban

๐Ÿ’ป ๐Ÿ“–

Adrian Riepl

๐Ÿ’ป ๐Ÿ“–

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

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].