Angular markAsPending() Example
September 10, 2023
On this apge we will learn to use markAsPending()
method in our Angular application.
1. The
markAsPending()
is the method of Angular AbstractControl
class that marks a control as pending.
2. A control can have status as valid, invalid, pending, disabled and enabled.
3. A control status can be pending in asynchronous validation. When a control is validated on the data obtained from a URL, then for the duration data is being fetched over the URL, the status will be pending and once the HTTP response is fetched, status can be valid or invalid.
4. We can programmatically mark a control pending using
markAsPending()
method of AbstractControl
. When we call markAsPending()
on a control, the value of pending of that control becomes true and the status of that control becomes PENDING.
In this article I will create a form validation application with asynchronous validator and use
markAsPending()
method to mark control status PENDING programatically.
Using markAsPending()
ThemarkAsPending()
is the method of AbstractControl
class that marks the control as PENDING.
markAsPending(opts: { onlySelf?: boolean; emitEvent?: boolean; } = {}): void
When emitEvent is true, the statusChanges observable emits an event with the latest status the control is marked PENDING. Default is true.
Suppose we have created a form as following.
compForm: FormGroup = this.formBuilder.group({ compName: ['', [Validators.minLength(3)], [existingCompanyValidator(this.compService)]], locations: this.formBuilder.array([ new FormControl('', [Validators.minLength(2)], [existingLocationValidator(this.compService)]), new FormControl('', [Validators.minLength(2)], [existingLocationValidator(this.compService)]) ]) });
markAsPending()
on the form controls.
1. For
FormControl
:
this.compForm.get('compName')?.markAsPending();
this.compForm.get('compName')?.statusChanges.subscribe( status => { console.log('Control status: ' + status); } );
markAsPending()
, following output will be obtained.
Control status: PENDING
markAsPending()
with emitEvent: false.
this.compForm.get('compName')?.markAsPending({emitEvent: false});
2. For
FormArray
:
this.compForm.get('locations')?.markAsPending();
FormGroup
:
this.compForm.markAsPending();
Find the code to use onlySelf with
markAsPending()
method.
this.compForm.get('compName')?.markAsPending({onlySelf: true});
true : marks pending only self.
false : marks pending all direct ancestors. This is default value.
Complete Example
Here I will create a reactive form with asynchronous validator. To show the usability ofmarkAsPending()
, I have created a button that will call a function inside which I am calling markAsPending()
on each control of the form. I am also displaying the value of pending on UI that will change to true once we call markAsPending()
method. Find the code.
existing-compname-validator.ts
import { AsyncValidatorFn, AbstractControl, ValidationErrors } from '@angular/forms'; import { Observable, timer } from "rxjs"; import { map, switchMap } from "rxjs/operators"; import { CompanyService } from './company.service'; export function existingCompanyValidator(compService: CompanyService): AsyncValidatorFn { return (control: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> => { let debounceTime = 2000; //milliseconds return timer(debounceTime).pipe( switchMap(() => compService.getCompanyByCompName(control.value)), map((comps: string[]) => { return (comps && comps.length > 0) ? { "compNameExists": true } : null; }) ); }; }
import { AsyncValidatorFn, AbstractControl, ValidationErrors } from '@angular/forms'; import { Observable, timer } from "rxjs"; import { map, switchMap } from "rxjs/operators"; import { CompanyService } from './company.service'; export function existingLocationValidator(compService: CompanyService): AsyncValidatorFn { return (control: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> => { let debounceTime = 2000; //milliseconds return timer(debounceTime).pipe( switchMap(() => compService.getLocationByLocationName(control.value)), map((loc: string[]) => { return (loc && loc.length > 0) ? { "locNameExists": true } : null; }) ); }; }
import { Component, OnInit } from '@angular/core'; import { FormGroup, FormArray, Validators, FormBuilder, ReactiveFormsModule, FormControl } from '@angular/forms'; import { CompanyService } from './company.service'; import { CommonModule } from '@angular/common'; import { existingCompanyValidator } from './existing-compname-validator'; import { existingLocationValidator } from './existing-location-validator'; @Component({ selector: 'app-comp', standalone: true, imports: [ CommonModule, ReactiveFormsModule ], templateUrl: './company.component.html' }) export class CompanyComponent implements OnInit { formSubmitted = false; constructor( private formBuilder: FormBuilder, private compService: CompanyService) { } ngOnInit(): void { this.compForm.get('compName')?.valueChanges.subscribe( compName => { console.log('Company name changed:' + compName); } ); this.compForm.get('compName')?.statusChanges.subscribe( status => { console.log('Control status: ' + status); } ); } compForm: FormGroup = this.formBuilder.group({ compName: ['', [Validators.minLength(3)], [existingCompanyValidator(this.compService)]], locations: this.formBuilder.array([ new FormControl('', [Validators.minLength(2)], [existingLocationValidator(this.compService)]), new FormControl('', [Validators.minLength(2)], [existingLocationValidator(this.compService)]) ]) }); get locations(): FormArray { return this.compForm.get('locations') as FormArray; } onFormSubmit() { this.compService.saveComp(this.compForm.value); this.formSubmitted = true; this.compForm.reset(); } markPending() { this.compForm.get('compName')?.markAsPending({onlySelf: true}); this.compForm.get('locations')?.markAsPending(); //this.compForm.markAsPending(); } }
<h3>markAsPending() Example</h3> <div *ngIf="formSubmitted && compForm.pristine" class="submitted"> Form submitted successfully. </div> <div class="team"> <form [formGroup]="compForm" (ngSubmit)="onFormSubmit()"> <p>Company Name* : <br/><input formControlName="compName"> <br /><label *ngIf="compForm.get('compName')?.hasError('minlength')" class="error"> Minimum length 3. </label> <br/> <label *ngIf="compForm.get('compName')?.hasError('compNameExists')" class="error"> Company name exists. </label> </p> Locations* : <br/> <div formArrayName="locations"> <div *ngFor="let emp of locations.controls; let idx = index"> <input [formControlName]="idx"> <br/><br/> </div> <label *ngIf="compForm.get('locations')?.invalid" class="error"> Minimum length 2. </label> <label *ngIf="compForm.get('locations')?.hasError('locNameExists')" class="error"> Location exists. </label> </div> <button [disabled]="!(compForm.status === 'VALID') || !compForm.dirty">SAVE</button> <button type="button" (click)="markPending()">MARK AS PENDING</button> </form> </div> <br/><b>Status :</b> <br/><b>Form</b> : {{compForm.status}} <br/><b>compName</b> : {{compForm.get('compName')?.status}} <br/><b>locations</b> : {{compForm.get('locations')?.status}} <br/><br/><b>Pending : </b> <br/><b>Form</b> : {{compForm.pending}} <br/><b>compName</b> : {{compForm.get('compName')?.pending}} <br/><b>locations</b> : {{compForm.get('locations')?.pending}}
import { Injectable } from '@angular/core'; import { of } from 'rxjs'; @Injectable() export class CompanyService { saveComp(data: any) { const comp = JSON.stringify(data); console.log(comp); } getCompanyByCompName(name: string) { if (name === "xyz") { return of(["xyz"]); } else { return of([]); } } getLocationByLocationName(name: string) { if (name === "vns") { return of(["vns"]); } else { return of([]); } } }
