Skip to content
This repository has been archived by the owner on Mar 12, 2024. It is now read-only.

[Angular Fundamental] Reactive Form #30

Open
reboottime opened this issue Nov 24, 2023 · 7 comments
Open

[Angular Fundamental] Reactive Form #30

reboottime opened this issue Nov 24, 2023 · 7 comments
Labels

Comments

@reboottime
Copy link
Owner

reboottime commented Nov 24, 2023

Overview

This article talks about reactive form in Angular. Bellow are the main points:


At the end of this article, I also listed some FAQs that you can't find on Angular official doc.

@reboottime
Copy link
Owner Author

reboottime commented Nov 24, 2023

Analogy

Looking from a typical json data.

Consider the following example of a user's data:

{
    "firstName": "John",
    "lastName": "Doe",
    "address": {
        "city": "Springfield",
        "state": "Illinois",
        "zipCode": "62704"
    },
    "nickName": "JD",
    "pets": "['German Shepherd', 'Bull Dog']"
}

This user data is comprised of

  • granular form fields like firstName and lastName,
  • as well as dictionary data such as address, which itself is composed of granular data (city, state, and zipCode).
  • and an array like field - pets.

Angular uses the same strategy , composition, to build complex forms.

Mapping to above user data example, there are two building blocks in angular form.

- form control, which represents a single UI form field.

  • form group: a bunch of (UI) form fields grouped together to represent a concept unit form field. There are two types form group
    • form group, a solution to group lowest granular form controls into a group in dictionary format.
    • [form array]: similar to from group yet grouping form fields in array format.

As we commonly see in online forms, form validation works

  • on a single UI form field
  • on a combined validation on a bunch of fields

to

  • verify user's input data validity,
  • present corresponding error message to form user
  • check the whole form's validity

Corresponding Packages

Angular Reactive form is exported from angular reactive form module , to use them, we need import

import { FormsModule, ReactiveFormsModule } from '@angular/forms';

explicitly.

Other commonly used modules are

import {
  AbstractControl,
  FormBuilder,
  FormGroup,
  Validators,
} from '@angular/forms';

@reboottime
Copy link
Owner Author

reboottime commented Nov 24, 2023

FormControl

FormControl represents the finest granularity (a UI Form field ) in angular form. t in Angular Form. It tracks the value, interaction, and validation status of an individual UI form field. (source)

Bellow are some frequently used shortcuts:

  • Create a form field
import {FormControl} from 'angular/form';

const userEmailField = new FormControl('', []);
  • Create a form field with one validator
import {FormControl, Validators} from 'angular/form';

const userEmailField = new FormControl('', [Validators.required]);
  • Create a form field with multiple validators
const userEmail = new FormControl(
   '', {
   validators: Validator.compose([Validators.required, Validators.email])
})
  • Create an email field with customized form field validator

    • sample code

      const userEmail = new FormControl('', {
         validators: Validator.compose([Validators.required, Validators.email, this.customEmailValidator])
      });
    • Cautious: Be aware that if a form field is configured with multiple validators that are not mutually exclusive, the errors object for that field will contain all the errors generated by these validators. For instance, in the code example provided, given a field value of '', you would encounter both

      • required field error
      • email field error
  • Customized field validator example:

class CustomerForm {

   customEmailValidator = (control: AbstractControl) => {
    const email = control.value;

    if (your email validation logic) {
      return { [errorName]: true };
    }

    return null;
  };
}
  • validation timing.

    • By default, angular form control validates form control value at blur event,
    • if you prefer to validating form field value at change event, here is the example
     const userEmail = new FormControl('', {
          validators: Validator.compose([Validators.required, Validators.email, this.customEmailValidator]),
         updateOn: 'change' // ask Angular to validate data on JS change event.
     });

@reboottime
Copy link
Owner Author

reboottime commented Nov 24, 2023

Form Group

  • How it looks like: Form group represents a group of UI form field as a group, bellow is an example to create a nested form group(source)

      import { Component } from '@angular/core';
      import { FormGroup, FormControl } from '@angular/forms';
      
      @Component({
        standalone: true,
        selector: 'app-profile-editor',
        templateUrl: './profile-editor.component.html',
        styleUrls: ['./profile-editor.component.css'],
      })
      export class ProfileEditorComponent {
        profileForm = new FormGroup({
          firstName: new FormControl(''),
          lastName: new FormControl(''),
          address: new FormGroup({
            street: new FormControl(''),
            city: new FormControl(''),
            state: new FormControl(''),
            zip: new FormControl(''),
          }),
        });
      }
  • How to use it html template: Angular supports hooks form field to its closet form group automatically, for example, for component bellow

      <div formGroupName="address">
        <h2>Address</h2>
      
        <label for="street">Street: </label>
        <input id="street" type="text" formControlName="street">
      
        <label for="city">City: </label>
        <input id="city" type="text" formControlName="city">
      
        <label for="state">State: </label>
        <input id="state" type="text" formControlName="state">
      
        <label for="zip">Zip Code: </label>
        <input id="zip" type="text" formControlName="zip">
      </div>

The street, text, state and zip are mapped to ` address field group automatically.

@reboottime
Copy link
Owner Author

reboottime commented Nov 24, 2023

Form Manipulation

Manipulate form control

Form Control represents the finest granularity in angular form. You can manipulate a form field or group's state

  • set/get value
  • set/get validation status

via the form control

Take previous profile editor form example as an example, you can manipulate zip field

// Access the 'zip' FormControl
const zipControl = this.profileForm.get('address.zip');

// Clear any validators assigned to it
zipControl?.clearValidators();

// Update its value and validation status
zipControl?.updateValueAndValidity();

You first retrieve the zip form control using get('address.zip').
Then, you use clearValidators() to remove any existing validators from this control.
Finally, updateValueAndValidity() is called to ensure that the form control's status is updated immediately, reflecting the changes in validation rules.


Manipulate form data

Thera are cases where you need to update form data programmatically, for example, updating the profileForm's data using the user's data fetched from server.

const sampleUserData = {
  firstName: 'Jane',
  lastName: 'Doe',
  address: {
    street: '123 Maple Drive',
    city: 'LA',
    state: 'CA',
    zip: '12345'
  }
};

profileForm.patchValue(sampleUserData)

Check form's validity

<form>
  <button disabled="!profileForm.valid">Submit</button>
</form>

@reboottime
Copy link
Owner Author

reboottime commented Nov 24, 2023

Form Builder

Constructing form using FormGroup and FormControl could be cumbersome if your form has too many fields. Angular provides a Form builder to support constructing form in a handy way using form builder

@reboottime
Copy link
Owner Author

reboottime commented Nov 24, 2023

FAQs

  • How to validate form field using change event
  • How to combine multiple form field validators
  • How to set form data using data fetched from server
    • Update form array value

      const [firstUri, ...restUris] = registeredApplication.redirectUris.map(
        (item) => item.redirectUri
      );
      
      this.form.patchValue({
        uris: [firstUri],
      });
      
      restUris.forEach(uri => {
        this.applicationRedirectUrls.push(this.fb.control(uri, [Validators.required, this.urlValidator()]))
      });

@reboottime
Copy link
Owner Author

reboottime commented Nov 24, 2023

References

@reboottime reboottime changed the title [Angular Fundamental] Form [Angular Fundamental] Reactive Form Nov 24, 2023
@reboottime reboottime added the done article is finalized label Dec 7, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

1 participant