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.