How to use RXJS's zip operator in Angular

How to use RXJS’s zip operator in Angular

Let’s say you have an Angular component with a form that contains multiple form controls. Each form control represents a field in the form that needs to be validated. You want to display an error message only when all the form controls are invalid.

First, create your Angular component template with the form controls and error message:

<form [formGroup]="myForm">
  <input type="text" formControlName="name" placeholder="Name">
  <input type="email" formControlName="email" placeholder="Email">
  <input type="password" formControlName="password" placeholder="Password">

  <div *ngIf="myForm.invalid && formSubmitted">
    All fields are required.
  </div>

  <button (click)="submitForm()">Submit</button>
</form>

Next, in your component class, import the necessary modules and set up the form:

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

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  public myForm!: FormGroup;
  formSubmitted = false;

  constructor(private formBuilder: FormBuilder) {

  }

  ngOnInit() {
    this.myForm = this.formBuilder.group({
      name: ['', Validators.required],
      email: ['', [Validators.required, Validators.email]],
      password: ['', Validators.required]
    });
  }

  submitForm() {
    this.formSubmitted = true;

    if (this.myForm.valid) {
      // Form submission logic
    }
  }
}

In the above example, we use Angular’s FormBuilder to create a form group with three form controls: name, email, and password. Each control has validators for required and email validation.

When the form is submitted, we set the formSubmitted flag to true and check if the form is valid. If the form is invalid, we want to display an error message only when all the form controls are invalid.

To achieve this, we can use the zip operator from RxJS. Modify the submitForm method as follows:

import { zip } from 'rxjs';

// ...

submitForm() {
  this.formSubmitted = true;

  if (this.myForm.invalid) {
    // Zip all form control validity observables
    const controlValidityObservables = Object.values(this.myForm.controls).map(control => control.statusChanges);

    // Subscribe to the zip operator
    zip(...controlValidityObservables).subscribe(controlStatuses => {
      const allInvalid = controlStatuses.every(status => status === 'INVALID');
      
      if (allInvalid) {
        // All form controls are invalid
        console.log('All fields are invalid');
      }
    });
  }
}

In the updated submitForm method, we use Object.values(this.myForm.controls) to retrieve an array of form controls. We then map each form control to its statusChanges observable, which emits whenever the control’s validity changes.

We pass these observables to the zip operator using the spread operator (...) and subscribe to the resulting observable. The zip operator emits an array of control statuses whenever any of the control validity observables emit a value.

Inside the subscription, we check if all the control statuses in the array are 'INVALID'. If they are, we can display the error message.

Complete Source Code

app.module.ts

import { NgModule } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    ReactiveFormsModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

app.component.ts


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

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  public myForm!: FormGroup;
  formSubmitted = false;

  constructor(private formBuilder: FormBuilder) {

   }

  ngOnInit() {
    this.myForm = this.formBuilder.group({
      name: ['', Validators.required],
      email: ['', [Validators.required, Validators.email]],
      password: ['', Validators.required]
    });
  }

  submitForm() {
    this.formSubmitted = true;

    if (this.myForm.invalid) {
      // Zip all form control validity observables
      const controlValidityObservables = Object.values(this.myForm.controls).map(control => control.statusChanges);

      // Subscribe to the zip operator
      zip(...controlValidityObservables).subscribe((controlStatuses: any[]) => {
        const allInvalid = controlStatuses.every(status => status === 'INVALID');

        if (allInvalid) {
          // All form controls are invalid
          console.log('All fields are invalid');
        }
      });

    }
  }
}

Conclusion In this blog post, we explored how the zip operator from RxJS can simplify form validation in Angular. By using the zip operator, we were able to display an error message only when all the form controls are invalid, providing a cleaner and more user-friendly form validation experience.

The zip operator is just one of the many powerful operators available in RxJS, allowing developers to handle complex scenarios with ease. Experiment with the zip operator and explore other RxJS operators to enhance your Angular applications and streamline your code.

Next Post Previous Post
No Comment
Add Comment
comment url