Search code examples
angularvalidationcustom-validatorsangular-reactive-forms

Override the custom validator angular 2


I am new in angular 2 and I want to implement a custom validator for form control that takes a boolean ( requiredAttribute) as parameter.

if this parameter is true so the form control is required else it is not required.

I have implemented this but seems don't works. All inputs (Form contol) becoms required. I have implemented this function that represent the custom validator.

 function inputRequired( requiredAttribute: boolean) {
  return (control: FormControl): { [s: string]: boolean } => {
    if (requiredAttribute === false) {
      return {'input is not required': true};
    }else {
      return null;
    }
  };
}

And I have placed it in the initForm method. Then for the input form text for my reactive form:

 text: new FormControl('', [Validators.compose([inputRequired(this.selectedOperation.inputs[i].required)])]),

The final code

private initForm() {
function inputRequired( requiredAttribute: boolean) {
  return (control: FormControl): { [s: string]: boolean } => {
    if (requiredAttribute === false) {
      return {'input is not required': true};
    }else {
      return null;
    }
  };
}
let operationName: any;
const operationInputs: FormArray = new FormArray([]);

if (this.selectedOperation.inputs != null) {
  for (let i = 0; i < this.selectedOperation.inputs.length; i++ ) {
    operationInputs.push(
      new FormGroup({
        name: new FormControl(this.selectedOperation.inputs[i].name),
        text: new FormControl('', [Validators.compose([inputRequired(this.selectedOperation.inputs[i].required)])]),
    defaultText: new FormControl(this.selectedOperation.inputs[i].defaultText),
      complexType: new FormControl(this.selectedOperation.inputs[i].complexType),
      type: new FormControl(this.selectedOperation.inputs[i].type),
      isMultivalued: new FormControl(this.selectedOperation.inputs[i].isMultiValued),
      values: new FormControl(this.selectedOperation.inputs[i].values),
      indicator: new FormControl(this.selectedOperation.inputs[i].indicator),
      required: new FormControl(this.selectedOperation.inputs[i].required),
      isSelected: new FormControl(this.selectedOperation.inputs[i].isSelected),
      simpleTypeVarietyOrComplexTypeContent: new FormControl(this.selectedOperation.inputs[i].simpleTypeVarietyOrComplexTypeContent),
      choiceContent: new FormControl(this.selectedOperation.inputs[i].choiceContent),
      inputQname: new FormControl(this.selectedOperation.inputs[i].inputQname),
        attributes: new FormControl(this.selectedOperation.inputs[i].attributes),
      children: operationInputsChildren,

      })
    );
  }
}
operationName = this.selectedOperation.name;
this.operationRequestForm = this.formBuilder.group({
    wsdlPath: [this.wsdlPath],
    name: [operationName],
    inputs: operationInputs,
  operationDateInvoke: ['', Validators.required],
  operationTimeInvoke: ['', Validators.required]
});

}

and the input is an object of CustomInput class that has required as attribute.

  export class CustomInput {

              constructor(public name: string, public text: string, public 
               defaultText: string,
          public complexType: boolean, public type: string, public children: 
            CustomInput[] = [],
          public isMultiValued: boolean,
          public values: string[] = [], public indicator: string, public 
           required: boolean,
          public isSelected: boolean, public 
            simpleTypeVarietyOrComplexTypeContent: number,
          public choiceContent: boolean, public inputQname: string,
          public attributes: Map<string, string> = new Map<string, string>() 
    ) {}
   }

An operation has many inputs elements. I want to create a reactive form for the operation. If the input element is required( its attribute required eqaul to true) then the HTML input associated to the operation input element is required.

So how I implement a custom validator that takes a boolean parameter and if this parameter is true then the form control is required else is not.

Thanks


Solution

  • UPDATE

    Now when looking closer at post, I realize you don't need a custom validator at all. When building the form, you can just call a function that checks the value of this.selectedOperation.inputs[i].required and if it's true set the validator required, if not, do nothing.

    So call the function after the build of the nested formgroup, but before the end of the iteration:

       }); // end of formgroup build
       this.checkValidator(this.selectedOperation.inputs[i].required, i)
    ) // end of iteration
    

    And the function:

    checkValidator(bool: boolean, index: number) {
      const control = this.operationRequestForm.controls.operationInputs.controls[index].controls.text
      if(bool) {
        control.setValidators(Validators.required)
        control.updateValueAndValidity();
      }
    }
    

    A very simplified Plunker that showcases that it works fine with setValidators() and updateValueAndValidity()


    ORIGINAL POST:

    Without testing your code (meaning that if there would be other problems), you are actually made it reverse in your custom validator. You want to...

    if requiredAttribute is true the form control is required, else it is not required

    Now you are doing the opposite in your custom validator.

    The funny thing is with validating forms, is that null is considered valid, and whatever else you return is considered not valid. So your custom validator should actually look like this:

    if (requiredAttribute === true) { 
      return {'inputRequired': true}; // field is required
    }else {
      return null; // field is not required when 'requiredAttribute' is 'false'
    }