Angular Reactive Forms Basics
Tags: angular, forms, rxjs
Published: 2024-04-07 12:20:39

---
title: Angular Reactive Forms Basics
published: true
description: Angular Reactive forms guide
tags: angular, forms, rxjs
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4u26smm4u6jo4j5fs0lf.png
# Use a ratio of 100:42 for best results.
# published_at: 2024-04-06 15:59 +0000
---

Angular Reactive Forms is a feature provided by the Angular framework that allows you to create and manage complex forms reactively. Reactive forms provide a model-driven approach to handling form inputs whose values change over time.

### Why use Reactive forms?

#### Model-Driven approach
With Reactive forms, you create a form model, controls, and validations in the TypeScript component and bind it to the template.

#### Declarative dynamic behavior
Once the form model is created and is wired with the template,  any changes made to the form controls are automatically reflected in the form's state, without the need for explicit event handling.

#### Observables
Reactive Forms in Angular heavily utilize observables. Each form control is represented as an observable stream of values that you can operate on (using Rx.js operators).

#### Form Controls
Each form field is a form control that is part of the form group. You declare the controls within the component and set a default value and validation rules for each. Controls and validations can be dynamically added or removed as well.

## Creating your first form

### Step 1: Importing ReactiveFormsModule in your App Module:

```ts
@NgModule({
  imports: [
    // other imports...
    ReactiveFormsModule
  ],
  // other declarations and providers...
})
export class AppModule { }
```

If you're using Standalone components then you import the module into the imports array:

```ts
import { Component } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [CommonModule, ReactiveFormsModule],
  templateUrl: './app.component.html',
  styleUrl: './app.component.scss',
})
export class AppComponent
```

### Step 2: Setup a form using FormBuilder

```ts
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrl: './app.component.scss',
})
export class AppComponent implements OnInit {
  myForm!: FormGroup;

  constructor(private fb: FormBuilder) {}

  ngOnInit() {
    this.myForm = this.fb.group({ // Form Group
      username: [''], // Form Control
      email: [''], // Form Control
      password: [''], // Form Control
    });
  }

  onSubmit() {
    console.log('Form values: ', this.myForm.value);
  }
}
```
### Step 3 - Binding Form to the template

As we can see the form group contains a list of form controls. We're going to use the form group and the controls to bind each field in the HTML template.

```html
<form [formGroup]="myForm" (ngSubmit)="onSubmit()">
  <div>
    <label>
      Username:
      <input formControlName="username" placeholder="YOUR PLACEHOLDER">
    </label>
  </div>

  <div>
    <label>
      Email:
      <input formControlName="email" placeholder="">
    </label>
  </div>

  <div>
    <label>
      Password:
      <input formControlName="password" placeholder="*******">
    </label>
  </div>

  <button type="submit">Create</button>
</form>
```
Using `(ngSubmit)="onSubmit()"` we'll call the onSubmit() function in the TypeScript every time we click the submit button.

![form-submit-preview](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zkbylam7tkwilmjsrp3l.png)

### Step 4 - Adding Validations
When creating new form controls you can set the starter (default) value to the control:

```ts
    this.myForm = this.fb.group({
      username: ['E.g. SuperUser'], // Form Control
      ...
    });
```

Following the starter value, you can add a set of synchronous validations:

```ts
    this.myForm = this.fb.group({
      username: ['E.g. SuperUser', [Validators.required]],
      email: ['', [Validators.required, Validators.email]],
      password: [
        '',
        [
          Validators.required,
          Validators.minLength(3),
          Validators.pattern('<REGEX>'),
        ],
      ],
    });
```

There is a set of built-in validations:
* required
* valid email
* min/max length
* follow a specific pattern, etc.

But you can also create your custom validators
Additionally, you can add Async validators that validate data against APIs.

```ts
    this.myForm = this.fb.group({
      username: [
        '',
        [
          /* Sync validators */
        ],
        [
          /* Async validators */
        ],
      ],
     ....
```

The typical use case of an async validator would be the search functionality. As you type your input into a search field, Angular will send data to the backend, check if there is a match, and return the response in real-time, e.g., check if a username exists while typing on the sign-up form.

### Step 5 - Applying form validations in the HTML template

Now we tell Angular to display an error for each control when it is invalid.

```html
   <div>
      <label>
        Email:
        <input formControlName="email" placeholder="">
        <div 
          style="color: red;" 
          *ngIf="myForm.get('email')?.invalid">
            Please provide a valid email address.
          </div>
      </label>
    </div>
```

![email-error](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kdt07sr9i3b62b1l778r.png)

To keep errors from popping up as soon as the page loads, you can apply validations only when certain conditions are met:
* form control is invalid
* form control is clicked on (touched)
* form control has been typed on (dirty)

```html
    <div>
      <label>
        Email:
        <input formControlName="email" placeholder="">
        <div
            style="color: red;"
            *ngIf="myForm.get('email')?.invalid && (myForm.get('email')?.dirty || myForm.get('email')?.touched)">
            Please provide a valid email address.
          </div>
      </label>
    </div>
```

![before-after](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g0d3luf3f702mqpzg0to.png)


What I like to do is create getter functions for each control and wrap the validation logic:

```ts
  get isInvalidEmail(): boolean {
    return !!(
      this.myForm.get('email')?.invalid &&
      (this.myForm.get('email')?.dirty || this.myForm.get('email')?.touched)
    );
  }
```
And apply it in the template:

```html
    <div>
      <label>
        Email:
        <input formControlName="email" placeholder="">
        <div
            style="color: red;"
            *ngIf="isInvalidEmail">
            Please provide a valid email address.
          </div>
      </label>
    </div>
```


## More Reactive form goodies

### Inspect form

Read form value

```ts
this.myForm.value
```

Check form validity

```ts
this.myForm.valid // true / false
```

Inspect form interactions

```ts
this.myForm.status // INVALID / VALID
this.myForm.dirty // true / false
this.myForm.touched // true / false
this.myForm.pristine // true / false
this.myForm.untouched// true / false
```

Look up form controls

```ts
this.myForm.get('username') // Abstract Control
this.myForm.get('email') // Abstract Control
this.myForm.get('password') // Abstract Control
```

### Alter Form Behavior

Clear form values

```ts
this.myForm.reset();
```

Clear errors

```ts
this.myForm.setErrors(null);
```

Mark as dirty

```ts
this.myForm.markAsDirty();
```

Mark as touched

```ts
this.myForm.markAsTouched();
```

Update validity

```ts
this.myForm.updateValueAndValidity();
```

As well as add/remove controls and validators.

Using the same methods for FormGroup, you can inspect or alter individual controls:

```ts
this.myForm.get('username')?.value;
this.myForm.get('username')?.valid;
this.myForm.get('username')?.touched;
this.myForm.get('username')?.dirty;
this.myForm.get('username')?.markAsDirty();
this.myForm.get('username')?.patchValue('New value');
this.myForm.get('username')?.setErrors(null);
this.myForm.get('username')?.hasError('required') // true / false
// and so on...
```

### Use of Rx.js

Form Groups and Abstract Controls have a `valueChanges` property that converts form value into an Observable stream.

```ts
    this.myForm.get('password')?.valueChanges
      .pipe(
        /* Rx.js operators */
      )
      .subscribe((data: string) => {
        console.log('data :>> ', data);
      })
```

An example scenario would be to replace each character with an asterisk:

```ts
import { debounceTime, distinctUntilChanged, map } from 'rxjs';

    const passwordControl = this.myForm.get('password');

    passwordControl?.valueChanges
      .pipe(
        // replace all characters with asterisk
        map(currentValue => currentValue.replace(/./g, '*'))
      )
      .subscribe((newValue: string) => {
        // Observables are immutable, which is why we need to update form control with new value
        // We're using { emitEvent: false } to prevent triggering valueChanges after patchValue
        passwordControl.patchValue(newValue, { emitEvent: false })
      })

// P.S. <input type="password" does this automatically
```

Or emit values to an Observable only after user has stopped typing:

```ts
    this.myForm.get('searchText')?.valueChanges
      .pipe(
        // emit 500ms after user stopped typing
        debounceTime(500),
        // emit only when value changes
        distinctUntilChanged()
      )
      .subscribe((data: string) => {
        console.log('data :>> ', data);
      })
```

If you prefer not to use Observables, you can always use `getRawValue()` method.

```ts
    const val = this.myForm.get('password')?.getRawValue();
    console.log('val :>> ', val);
```

### Next Chapter ➡️
* [**Reactive Validation Triggers in Angular Forms**](https://dev.to/mirzaleka/reactive-validation-triggers-in-angular-forms-4de0)

That's all for today!

For everything else awesome, follow me on [Dev.to](https://dev.to/mirzaleka) and on [Twitter](https://twitter.com/mirzaleka) to stay up to date with my content updates.