0
votes

In ionic 3, FormBuilder to check the validity of a form we use "form.valid" property of formGroup, which returns true if valid and false if invalid,

My problem is when i am setting values from javascript, "form.valid" gives true even setting any value which is not given(available) in the options of the select control.

I want select control to be invalid if select values are different from the given option.

Here's the working Plunker link,

Codes speaks better,

Here is the design part(page.html),

<button ion-button color="app" (click)="setval_valid()">Set valid values</button>
  <button ion-button color="app" (click)="setval()">Set Different invalid values</button>
  <form [formGroup]="knowledge" *ngIf="!retryButton">
        <ion-list>
                <ion-item>
                    <ion-label floating>Educational Qualification Details <span>*</span></ion-label>
                    <ion-select formControlName="f2_edudetail" interface="popover" (change)="getTotal()">
                        <ion-option value="illiterate"> Illiterate </ion-option> 
                        <ion-option value="primary education"> Education </ion-option> 
                        <ion-option value="matriculate"> Matriculate </ion-option> 
                        <ion-option value="graduate"> Graduate </ion-option> 
                        <ion-option value="post graduate"> Post Graduate </ion-option> 
                    </ion-select>
                </ion-item>
                <div *ngIf="!knowledge.controls.f2_edudetail.valid  && (knowledge.controls.f2_edudetail.dirty || submitAttempt)">
            <p *ngIf="knowledge.controls.f2_edudetail.errors.required">This field is required.</p>
        </div>


                <ion-item>
                    <ion-label floating>What Is Your Proficiency In English?<span>*</span></ion-label>
                    <ion-select formControlName="f2_proficiency" interface="popover">
                      <ion-option value="fluent">Fluent</ion-option>
                      <ion-option value="read only">Read Only</ion-option>
                      <ion-option value="write only">Write Only</ion-option>
                      <ion-option value="speak only">Speak Only</ion-option>
                      <ion-option value="understand only">Understand Only</ion-option>
                      <ion-option value="Don't Know">Don't Know</ion-option>
                    </ion-select>
                </ion-item>
                <div *ngIf="!knowledge.controls.f2_proficiency.valid  && (knowledge.controls.f2_proficiency.dirty || submitAttempt)">
            <p *ngIf="knowledge.controls.f2_proficiency.errors.required">This field is required.</p>

        </div>


                <ion-item>
                    <ion-label floating>Participation In Farming Programs</ion-label>
                    <ion-select formControlName="f2_participation" interface="popover" (ionChange)="setValidation()">
                      <ion-option value="yes">Yes</ion-option>
                      <ion-option value="no">No</ion-option>
                    </ion-select>
                </ion-item>
        </ion-list>     
    </form>

    <button ion-button color="app" (click)="save()">Save</button>

    <br><br>

    <b>{{validity}}</b>

Here is the TS part(page.ts),

knowledge: FormGroup;
  validity:stirng = '';

  constructor(public navController: NavController, public formBuilder: FormBuilder) 
  {
    this.knowledge = formBuilder.group({
            'f2_edudetail' : ['', Validators.required], //drp
            'f2_proficiency' : ['', Validators.required], //drp
            'f2_participation' : [''], //drp
    });

  }

  setval(){

    let formData = [];
        formData['f2_edudetail']     = "Blah Blah";
        formData['f2_proficiency']   = "Blah Blah";
        formData['f2_participation'] = "Blah Blah";

    this.knowledge.setValue(formData);
    this.validity = '';
  }

  setval_valid(){

    let formData = [];
        formData['f2_edudetail']     = "illiterate";
        formData['f2_proficiency']   = "fluent";
        formData['f2_participation'] = "yes";

    this.knowledge.setValue(formData);
    this.validity = '';
  }

  save(){
    if (this.knowledge.valid){
      this.validity = "Valid (I want this to be invalid if select values are different from the given option)";
    }
    else{
      this.validity = "Invalid";
    }
  }
2

2 Answers

1
votes

What you need is a custom validator.

The way you've done it just expect the value to be something, it can't be null or undefined, so any value'll pass the required validator.

To create a custom validator you'll need a .ts file to create your validator class, let's say it's called example.ts:

import { FormControl } from '@angular/forms';

export class MyCustomValidator {
    static isValid = (control: FormControl): any => {
        // The value coming from the control
        let myProp: string = String(control.value);

         // Will check if it's different from the desired options. 
        if (myProp != 'fluent' && myProp != 'read only' && myProp != 'write only' && myProp != 'speak only' && myProp != 'understand only' && myProp != "Don't Know"){
            // if different, return a property invalid with value true
            return {
                "invalid": true
            };
        };

        // If it doesn't enter the if statement then it's valid, so return null
        return null;
    }
}

Then in your page.ts you'll import the validator and use it inside a Validators.compose

import { MyCustomValidator } from 'path/to/your/file/example';

knowledge: FormGroup;

  constructor(public navController: NavController, public formBuilder: FormBuilder) 
  {
    this.knowledge = formBuilder.group({
            'f2_edudetail' : ['', Validators.required],
            'f2_proficiency' : ['', Validators.compose([Validators.required, MyCustomValidator.isValid])], //here's your custom validator being used
            'f2_participation' : [''],
    });
  }

Hope this helps.

0
votes

I would change the options to an array, iterate those in template. When you want to try and set values to your form, check to see if they exist in the corresponding arrays, if so, set value, else leave empty. This way we can check wheather the form is valid or not. Anyway, I think it's better to iterate arrays in template to keep it clean and handle anything else in component. So do something like this:

TS:

firstOptions = ['illiterate','primary education']
secondOptions = ['fluent', 'read only']
thirdOptions = ['yes', 'no']

HTML:

<ion-select formControlName="f2_edudetail" interface="popover" (change)="getTotal()">
  <ion-option *ngFor="let opt of firstOptions" [value]="opt"> {{opt}}</ion-option> 
</ion-select>

// rest of selects

Then when you set the values, check if they are found in the arrays:

setval(){
  let formData = [];
    formData['f2_edudetail']     = this.firstOptions.find(x => x === "blah") || '';
    formData['f2_proficiency']   = this.secondOptions.find(x => x === "blah") || '';
    formData['f2_participation'] = this.thirdOptions.find(x => x === "blah") || '';

  this.knowledge.setValue(formData);
  this.validity = '';
}

Your demo: https://plnkr.co/edit/uuhMn0sS5VCyL4dZDjRG?p=preview

Probably you'd want to transfor these arrays to array of objects instead, with display name for option and value for option.