Angular markAsPending()

By Arvind Rai, 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()

The markAsPending() is the method of AbstractControl class that marks the control as PENDING.
markAsPending(opts: { onlySelf?: boolean; emitEvent?: boolean; } = {}): void 
When onlySelf is true, the method marks PENDING only this control otherwise all direct ancestors are marked PENDING. The default is false.
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)])
    ])
}); 
Now call markAsPending() on the form controls.
1. For FormControl :
this.compForm.get('compName')?.markAsPending(); 
By default emitEvent is true, hence statusChanges will emit and mark status of control as PENDING.
this.compForm.get('compName')?.statusChanges.subscribe(
	status => {
	  console.log('Control status: ' + status);	
	}
); 
On the call of markAsPending(), following output will be obtained.
Control status: PENDING 
Now call markAsPending() with emitEvent: false.
this.compForm.get('compName')?.markAsPending({emitEvent: false}); 
The statusChanges will not emit.
2. For FormArray :
this.compForm.get('locations')?.markAsPending(); 
3. For FormGroup :
this.compForm.markAsPending(); 


Find the code to use onlySelf with markAsPending() method.
this.compForm.get('compName')?.markAsPending({onlySelf: true}); 
For the value of onlySelf as
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 of markAsPending(), 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;
      })
    );
  };
} 
existing-location-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 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;
      })
    );
  };
} 
company.component.ts
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();
    }
} 
company.component.html
<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}} 
company.service.ts
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([]);
        }
    }
} 
Find the print screen of the output after clicking on 'MARK AS PENDING' button.
Angular markAsPending() Example

Reference

Download Source Code

POSTED BY
ARVIND RAI
ARVIND RAI
LEARN MORE








©2024 concretepage.com | Privacy Policy | Contact Us