Angular Guru
Angular Guru

Reputation: 71

Validator in not a function in angular

I have created own directive for adding validation and removing validation based input parameters of directive. I am getting error when loading and form field value changing validator is not working the validation

 @Directive({
    selector:[appdirective1],
    providers:[{
    provide:NG_VAILDATORS,
    useExisting:forwardRef(()=>directiveName)
    
    }]
    });
    
    export class directiveName implements OnChanges,OnInit{
    @Input varName1="";
    @Input varName2="";
    @Input callBack1:()=>{}
@Input callBack2:()=>{}
    constructor(private _injector:Injector){
    }
    
    validate1():ValidateFn{
    return (control:AbstractControl):{[key:string]:any}|null =>{
    //logic here
    }
    }
    validate2():ValidateFn{
    return (control:AbstractControl):{[key:string]:any}|null =>{
const obj1=this.callBack1();
    //logic here
    }
    }
    validate3():ValidateFn{
    return (control:AbstractControl):{[key:string]:any}|null =>{
const obj1=this.callBack2();
    //logic here
    }
    }
    ngOnInit(){
    const control=this._injector.get(NgControl);
    control.control.setValidators(this.validate1);
    if(this.varName1 !=="" && this.varName1 !== undefined){
    control.control.setValidators(this.validate2);
    }
    if(this.varName2 !=="" && this.varName2 !== undefined){
    control.control.setValidators(this.validate3);
    }
    control.control.updateValueAndValidity();
    
    
    }
    ngOnChanges(){
    const control=this._injector.get(NgControl);
    if(this.varName1 !=="" && this.varName1 !== undefined){
    control.control.setValidators(this.validate2);
    }
    if(this.varName2 !=="" && this.varName2 !== undefined){
    control.control.setValidators(this.validate3);
    }
    control.control.updateValueAndValidity();
    }
    }
    }

In the html file , I have added directive selectors and input paramets

<input appdirective1 name="name2" [varName1]="success" [callBack1]="method1" />

<input appdirective1 name="name3" [varName2]="success" [callBack2]="method2"/>

Already method method1 and method2 binded in component file. I need to used this file many places.

I am getting Error like :

 TypeError : validator is not a function
at Forms.mjs
at Array.map(<anonymous>)
at executeValidators(forms.mjs:896:21)
at Forms.mjs
at Array.map(<anonymous>)
at executeValidators(forms.mjs:896:21)
at Formcontrol2._ComposedValidatorsFn(forms.mjs)
at Formcontrol2._runValidator(forms.mjs)
at Formcontrol2.updateValueandValidity(forms.mjs)

Upvotes: 2

Views: 73

Answers (2)

Eliseo
Eliseo

Reputation: 57909

The code has no logic. If you have a directive to "Validate" a control simply implements Validator.

NOTE: The error is because you forget add "multi:true" to your provider.

e.g.

@Directive({
  selector: '[appdirective1]',
  standalone: true,
  providers: [
    {
      provide: NG_VALIDATORS,
      useExisting: forwardRef(() => DirectiveName),
      multi:true
    }
  ],
})
export class DirectiveName implements Validator {
  validate(control: AbstractControl): ValidationErrors | null {
    return control.value=='Angular'?null:{ custom: true };
  }
}

An unique function validate where you add the logic.

This allow add another validators to your formControl or ngModel. Of course, forget these "ugly" setValidators, updateValueAndValidy, etc.

a stackblitz

Upvotes: 1

Naren Murali
Naren Murali

Reputation: 56002

These is no need for NG_VAILDATORS provider, since you are adding validators programatically using setValidators and addValidators. We use this provider when we want to autowire a validator, which is not the case in your code.

To learn more about NG_VALIDATORS try this Angular Custom Form Controls: Complete Guide article.

Instead of setValidators you need to use addValidators since setValidators replaces the previous validator you had set.

control?.control?.addValidators(this.validate1());

You should always have a ngModel or form control configured for the elements you have this directive on, only then does NgControl exists for usage.

<input appdirective1 name="name2" [varName1]="success" [callBack1]="method1" [formControl]="control" #controlRef="ngForm"/> 
<input appdirective1 name="name3" [varName2]="success" [callBack2]="method2" [(ngModel)]="name" #modelRef="ngModel"/>

I made the repeating direcive code into it's own function for preventing code duplication.

Full Code:

import {
  Component,
  Directive,
  OnChanges,
  OnInit,
  forwardRef,
  Input,
  Injector,
} from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import {
  NG_VALIDATORS,
  ValidatorFn,
  AbstractControl,
  ValidationErrors,
  NgControl,
  ReactiveFormsModule,
  FormsModule,
  FormControl,
} from '@angular/forms';
import { CommonModule } from '@angular/common';

@Directive({
  selector: '[appdirective1]',
  standalone: true,
})
export class directiveName implements OnChanges, OnInit {
  @Input() varName1 = '';
  @Input() varName2 = '';
  @Input() callBack1!: (control: AbstractControl) => ValidationErrors | null;
  @Input() callBack2!: (control: AbstractControl) => ValidationErrors | null;
  constructor(private _injector: Injector) {}

  validate1(): ValidatorFn {
    return (control: AbstractControl): ValidationErrors | null => {
      //logic here
      return control.value === 'Angular'
        ? {
            invalidName: control.value,
          }
        : null;
    };
  }
  validate2(): ValidatorFn {
    return (control: AbstractControl): ValidationErrors | null => {
      const obj1 = this.callBack1(control);
      //logic here
      return obj1;
    };
  }
  validate3(): ValidatorFn {
    return (control: AbstractControl): ValidationErrors | null => {
      const obj1 = this.callBack2(control);
      //logic here
      return obj1;
    };
  }
  ngOnInit() {
    this.conditionallyAddValidators();
  }

  conditionallyAddValidators() {
    const control = this._injector.get(NgControl);
    control?.control?.addValidators(this.validate1());
    if (this.varName1 !== '' && this.varName1 !== undefined) {
      control?.control?.addValidators(this.validate2());
    }
    if (this.varName2 !== '' && this.varName2 !== undefined) {
      control?.control?.addValidators(this.validate3());
    }
    control?.control?.updateValueAndValidity();
  }

  ngOnChanges() {
    this.conditionallyAddValidators();
  }
}

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [directiveName, ReactiveFormsModule, CommonModule, FormsModule],
  template: `
    <input appdirective1 name="name2" [varName1]="success" [callBack1]="method1" [formControl]="control" #controlRef="ngForm"/>
    <br/>
    {{controlRef.errors | json}}
    <br/>
    <hr/>
<input appdirective1 name="name3" [varName2]="success" [callBack2]="method2" [(ngModel)]="name" #modelRef="ngModel"/>
    <br/>
    {{modelRef.errors | json}}
    <br/>
    <hr/>
  `,
})
export class App {
  control = new FormControl('Angular');
  name = 'Angular';
  success = 'required';

  method1(control: AbstractControl) {
    const value = control?.value;
    return value?.length <= 2
      ? {
          minLength: true,
        }
      : null;
  }

  method2(control: AbstractControl) {
    const value = control?.value;
    return value?.length >= 10
      ? {
          maxLength: true,
        }
      : null;
  }
}

bootstrapApplication(App);

Stackblitz Demo

Upvotes: 0

Related Questions