The validation status of Angular's custom form array remains in a PENDING state when utilizing asynchronous validators

I've created a custom asynchronous postal code validator that can be used with Template Driven forms.

@Directive({
  selector: '[appAsyncPostalCode]',
  providers: [
    { provide: NG_ASYNC_VALIDATORS, useExisting: AsyncPostalCodeValidatorDirective, multi: true }
  ]
})
export class AsyncPostalCodeValidatorDirective implements AsyncValidator {
  public validate(
    control: AbstractControl
  ): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> {
    return this.postalCodeValidator()(control);
  }

  public postalCodeValidator(): AsyncValidatorFn {
    return (control: AbstractControl): Observable<ValidationErrors | null> => {
      const postalCodePattern = /^\d{5}$/;
      if (control.value && postalCodePattern.test(control.value)) {
        return checkPostalCodeXHR(control.value).pipe(
          map(name => null),
          catchError(() => {
            if (control.value) {
              control.markAsTouched();
            }
            return of({ postalCodeInvalid: true });
          })
        );
      } else {
        return of(control.value ? { postalCode: true } : null);
      }
    };
  }
}

Additionally, I have implemented a custom solution to utilize Form Array in Template Driven forms.

@Component({
  selector: 'app-array-input',
  templateUrl: './array-input.component.html',
  styleUrls: ['./array-input.component.scss'],
  providers: [
    { provide: NG_VALUE_ACCESSOR, useExisting: ArrayInputComponent, multi: true },
    { provide: NG_ASYNC_VALIDATORS, useExisting: ArrayInputComponent, multi: true }
  ]
})
export class ArrayInputComponent implements ControlValueAccessor, AsyncValidator {
  @Input() public name!: string;

  @ContentChild(ArrayItemDirective, { static: true })
  public itemTemplate!: ArrayItemDirective;

  // Remaining code for ArrayInputComponent implementation...

The issue arises when validate is called upon value changes but the control's statusChanges doesn't trigger even though the status changes. This causes the array control to remain in a pending state and renders the entire form invalid.

To workaround this issue, a temporary solution involves manually checking the validity of the parent component after the asynchronous validation completes in the next tick.

...
return checkPostalCodeXHR(control.value).pipe(
  map(name => {
    setTimeout(() => control.parent?.updateValueAndValidity());
    return null;
  }),
  catchError(() => {
    if (control.value) {
      control.markAsTouched();
    }
    setTimeout(() => control.parent?.updateValueAndValidity());
    return of({ postalCodeInvalid: true });
  })
);
...

I'm aware of cases where Angular sets the emitEvent to false for updates, but some adjustments were made to address this specifically for async validators (https://github.com/profanis/angular/commit/bae96682e7d5ea1942c72583e7d68a24507c1a5a).

If anyone has a more permanent solution or insight into why this issue occurs, please share your thoughts.
It could be related to potential errors in my declarations or an alternative method to validate the array's status?

Thank you for any assistance provided.

EDIT: A reproducible example of the problem has been generated on Stackblitz. https://stackblitz.com/edit/angular-template-form-array-jf9h5v.
Please refer to the browser console for a better understanding of the issue rather than relying solely on the Stackblitz console. It showcases how the first control's VALID status isn't emitted, although it displays as valid in the evaluated log.

Answer №1

This updated version of the Stackblitz code addresses the issue at hand.
https://stackblitz.com/edit/angular-template-form-array-jf9h5v-uh3t4x

It appears that when it comes to the controls in FormArray, the statusChanges does not trigger the new status update. However, the statusChanges for the array itself does. This insight can be leveraged for correct validation of the array.

  public validate() {
    return this.array.statusChanges.pipe(
      startWith(this.array.status),
      filter(() => !this.array.pending),
      map(() => (this.array.valid ? null : { array: true })),
      take(1)
    );
  }

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Dynamic importing fails to locate file without .js extension

I created a small TS app recently. Inside the project, there is a file named en.js with the following content: export default { name: "test" } However, when I attempt to import it, the import does not work as expected: await import("./e ...

The Angular Ngrx store function, store.select('selector name'), should ideally provide a list of Books but instead, it is returning a non-iterable list

Can someone help me troubleshoot this issue? It's my first time using state management. The result I get when trying to fetch it from the state is not what I expected. https://i.sstatic.net/QD7o5.png Here is the reducer: import {createReducer, on} ...

The comparison between StrictNullChecks and Union Types in terms of syntax usage

Understanding StrictNullChecks in TypeScript Traditionally, null and undefined have been valid first class type citizens in JavaScript. TypeScript formerly did not enforce this, meaning you couldn't specify a variable to potentially be null or unde ...

Angular 6 and above: The use of ProvidedIn in a submodule is leading to a circular dependency issue

A resolve service is being implemented using the new providedIn attribute. This translations resolver is utilized in a protected module: import { Injectable } from '@angular/core'; import { Observable , pipe } from 'rxjs'; import { ...

Identify when the user ceases typing in Angular 2

I am currently working on implementing a feature that detects whether the user is typing or not. I need to determine when the user has stopped typing for at least 3 seconds in order to perform certain actions. I have successfully detected when the user sta ...

What is the ideal method to manage API post requests in Angular?

I have a requirement to search for a document based on its name property by providing a string input from my Angular application. This involves calling a REST API endpoint with a function that resembles the following code snippet: exports.idea_search = f ...

How to add a service to a static function in Angular

After incorporating a logger service into my project, I have encountered an issue with using it in NGXS static selectors. The selectors in NGXS are static methods, which prevent me from accessing the logger service injected via Angular DI. Are there any e ...

Ensuring the Presence of a Legitimate Instance in NestJS

I have been working on validating my request with the Product entity DTO. Everything seems to be in order, except for the 'From' and 'To' fields. The validation works correctly for the Customer and Type fields, but when incorrect data i ...

Service testing in Angular with dependencies

I need to test a service that relies on another service, but I am facing some challenges. Service Definition: @Injectable() export class LanguageService { constructor(private translate: TranslateService) { } Testing Scenario: describe('LanguageSe ...

Encountering an issue with PrimeNG's <p-calendar> component: the error message "date

I encountered an issue resulting in the following error message: core.es5.js:1020 ERROR Error: Uncaught (in promise): TypeError: date.getMonth is not a function TypeError: date.getMonth is not a function This error occurs whenever I attempt to implement ...

Find the distinct values from an array of objects containing varying elements using Typescript

My array contains dynamic elements within objects: [ { "Value1": [ "name", "surname", "age" ], "Value2": [ "name" ...

Can variables be declared for file paths within the HTML code in a TypeScript file?

We utilize the Vaadin designer to create the frontend of our project. Within the .ts files, we have images where we aim to establish variables for the file paths. Currently, the setup looks like this: <img src="../../themes/light/img/example.jpg&q ...

Angular component causing style disruptions

As a newcomer to Angular, I am currently in the process of converting my previous project from .net to angular. However, I have encountered an issue that is specifically related to Angular, so please forgive me if it seems trivial. I am facing difficulti ...

Having trouble with routerLink in your custom library while using Angular 4?

In my Angular 4 project, I have developed a custom sidebar library and integrated it into the main project. My current issue is that I want to provide the option for users to "open in new tab/window" from the browser's context menu without having the ...

Tips for transmitting static information from route configuration to components

I am facing an issue with passing static data from a route to a component in Angular. Despite trying to pass the data in the route configuration, I keep receiving empty data when subscribing to it from the ActivatedRoute. Below is the code snippet that I h ...

Should I implement jquery within angular 2, or opt for the current css/js frameworks available?

Embarking on the development of a website with Angular 2, my deadline is set within a month. I need to select a CSS/JS framework that seamlessly integrates with Angular 2. While using jQuery within Angular 2 is discouraged, I am curious if there are altern ...

Create a dynamic styled component with tags based on props

Looking to craft a dynamic tag using styled components, where the tag is passed in via props. Here's an example of the code: import * as React from 'react'; import styled from 'styled-components'; type ContainerProps = { chi ...

``There is an issue arising from the interaction between EventEmitter and Custom

Working with an Angular 8.3.14 project. We have implemented an EventEmitter to communicate a string value to the parent component. Child Component Setup @Output() pepe = new EventEmitter<any>(); ngOnInit() { this.pepe.emit('pepe'); } ...

The ASPX validation will not be able to access the .js file

I am facing an issue with client-side validation using JavaScript (.js). Despite linking the path in the head section, the ASP file doesn't seem to reach the JavaScript file. <head runat="server"> <meta http-equiv="Content-Type" content="tex ...

Angular index.html file can include a conditional script

I am currently working on an Angular project, where the index.html serves as the main entry point for the application, just like in any other Angular project. This file contains important links and configurations. Within the HTML code snippet below, you w ...