Search code examples
angularcustomvalidator

Angular custom validator for reactive forms to pass array objects as parameter


I am in situation like, to pass array of objects (productlists) as parameter for custom validator. But it shows error like "only single value can be passed as parameter".

    <div class="formGroup">
        <input
          formControlName="productname"
          matInput
          #input
          type="text"
          [matAutocomplete]="auto"
          class="form-control"
          (click)="clicked(input)"
          (input)="performFiltering(input)"
            />
        <mat-autocomplete
          #auto="matAutocomplete"
          (optionSelected)="onProductChange1($event)"
          >
          <mat-option
            *ngFor="let product of filteroptions | async"
            [value]="product.productname"
          >
            {{product.productname}}
          </mat-option>
        </mat-autocomplete>
<div *ngIf="productdetailsarray.at(i).get('productname')?.errors?.['productnotavailable'] && productdetailsarray.at(i).get('productname')?.touched">
                      <span style="font-size: 10pt; color:red">Product is not valid</span>
      </div>

My ts code is

export interface productentity{
  productname:string,
  price:number,
  gst:number,
}
        productlists = [
            { productname: 'apple', price: 10, gst: 10 },
            { productname: 'orange', price: 20, gst: 12 },
            { productname: 'lemon', price: 30, gst: 20 },
            { productname: 'grape', price: 60, gst: 20 },  
            { productname: 'bringal', price: 25, gst: 20 },
            { productname: 'tomato', price: 40, gst: 20 },
            { productname: 'kiwi', price: 120, gst: 20 },
            { productname: 'potato', price: 500, gst: 20 }];
    productlist!:productentity[];
  filteroptions!: Observable<productentity[]>;
        productname: new FormControl('', [Validators.required,Productavailable(productlists)]),
    
     ngOnInit() {
        this.productlist = this.productlists.sort((a:any,b:any)=> {
          return a.productname < b.productname ? -1 : a.productname > b.productname ? 1 : 0
        });
    
      }
      clicked(input: any) {
        if (input.value) {
          this.performFiltering(input);
        }
        else{
          this.filteroptions = of(this.productlist);
        }
      }
    
      performFiltering(input: any) {
        if (input.value) {
          this.filteroptions = of(this._filter(input.value));
        } else {
          this.filteroptions = of(this.productlist);
        }
      }
      private _filter(value: string): productentity[] {
        const searchvalue = value.toLocaleLowerCase();
        return this.productlist.filter((option) =>
          option.productname.toLocaleLowerCase().includes(searchvalue)
        );
      }

My custom validator function is

export function Productavailable(val: string): ValidatorFn {
 
    return (control: AbstractControl): ValidationErrors | null => {
      let productvalue: string = control.value;
      let productlists = [
        { productname: 'apple', price: 10, gst: 10 },
        { productname: 'orange', price: 20, gst: 12 },
        { productname: 'lemon', price: 30, gst: 20 },
        { productname: 'grape', price: 60, gst: 20 },  
        { productname: 'bringal', price: 25, gst: 20 },
        { productname: 'tomato', price: 40, gst: 20 },
        { productname: 'kiwi', price: 120, gst: 20 },
        { productname: 'potato', price: 500, gst: 20 }];

        const selectedProduct = productlists.find(
            (product) => product.productname === productvalue
          );

          if (productvalue=='') {
            return null;
          }      
           if (selectedProduct==null) {
            return { 'productnotavailable': true, 'requiredValue': val }
          }      
      return null;
    }
   
  }

Here I am facing the situation to add the 'productlists" array object both in component ts and in custom validator function( ). Due to this i cant able to provide productlists as dynamically. Can someone help me how pass array object as parameter to custom validate function or any other solution to display error message if the product is not available in the productlist.


Solution

  • NOTE about "bind" to complementary the other answer.

    When you create a custom validator you can use a function that return a validator

    validator(products:any[]){
       return (control: AbstractControl): ValidationErrors | null => {
           ..inside you can use "products" variable
       }
    }
    

    And you use the validator like

    productname: new FormControl('', [Validators.required, 
                                      this.validator(this.productlists))),
    

    But this only works if "this.productList" not change after you create the formControl,

    e.g.

       //you can NOT use
       this.productList=['one','two']
       control=new FormControl('',this.validator(this.productList))
       this.productList=['one','two','three']
    
       //because inside the validator "products" are ALWAYS ['one','two']
    

    You use "bind(this)" if the variable can change after you create the formControl -then you can use all the variables of the component inside the validator function using "this."+variable_of_component