Search code examples
angularangular-formsangular-validation

Angular FormControls Always Valid When More Than One In Form


I have two inputs that use FormControl that always show as valid but if I take one out then the remaining will be valid/invalid as expected. I would change the two formcontrols out for one ControlGroup but 1.) I want to understand what I'm doing wrong and 2.) I need to access one formcontrol for an autocomplete I have in the component.

Relevant HTML:

<form 
    fxLayout="column" 
    fxLayoutAlign="center center" 
    (ngSubmit)="onSubmit(status, count)" 
    #statusForm="ngForm"
>

    <!-- Status Input -->
    <div>
        <mat-form-field>
        <input 
            matInput placeholder="Status" 
            [formControl]="statusFormControl"
            [errorStateMatcher]="matcher"
            [matAutocomplete]="auto"
            [(ngModel)]="status"
            name="status"
            required
        >
        <mat-error *ngIf="statusFormControl.hasError('required')">
            Status is <strong>required</strong>
        </mat-error>
    </mat-form-field>

    <mat-autocomplete #auto="matAutocomplete">
        <mat-option *ngFor="let status of filteredStatusList | async" [value]="status.name">
        {{ status.name }}
        </mat-option>
    </mat-autocomplete>
    </div>


    <!-- Count Input -->
    <mat-form-field>
        <input 
            matInput placeholder="Count" 
            [formControl]="countFormControl"
            type="number"
            min="1"
            [(ngModel)]="count"
            name="count"
            required
        >
        <mat-error *ngIf="countFormControl.hasError('required')">
            Count must be a <strong>number</strong>
        </mat-error>
    </mat-form-field>

    {{statusForm.valid}}
    {{statusForm.invalid}}

    <button 
        mat-raised-button color="primary" 
        *ngIf="statusForm.valid" 
        type="submit" 
        [disabled]="statusForm.invalid"
    >
        Add
    </button>

and TS:

export class ... {

statusFormControl = new FormControl('', [
    Validators.required
]);

countFormControl = new FormControl('', [
    Validators.required
]);

...

constructor() {
this.filteredStatusList = this.statusFormControl.valueChanges
  .pipe(
    startWith(''),
    map(status => status ? this.filterStatusList(status) : this.allStatuses.slice())
  );

}

filterStatusList(statusName: string) {
return this.allStatuses.filter(status =>
  status.name.toLowerCase().indexOf(statusName.toLowerCase()) === 0);
}

}

Solution

  • Here is what one of mine looks like using reactive forms (see below). Notice the form's formGroup directive. And I used the formControlName directive instead of the formControl directive ... but either would work.

    HTML

        <form class="form-horizontal"
              novalidate
              (ngSubmit)="save()"
              [formGroup]="customerForm">
            <fieldset>
                <div class="form-group">
                    <label class="col-md-2 control-label" 
                           for="firstNameId">First Name</label>
    
                    <div class="col-md-8">
                        <input class="form-control" 
                               id="firstNameId" 
                               type="text" 
                               placeholder="First Name (required)" 
                               formControlName="firstName" />
                    </div>
                </div>
                <div class="form-group">
                    <label class="col-md-2 control-label" 
                        for="lastNameId">Last Name</label>
    
                    <div class="col-md-8">
                        <input class="form-control" 
                               id="lastNameId" 
                               type="text" 
                               placeholder="Last Name (required)" 
                               formControlName="lastName" />
                    </div>
                </div>
            </fieldset>
        </form>
    

    Component Class

    customerForm: FormGroup;
    
    constructor(private fb: FormBuilder) { }
    
    ngOnInit(): void {
        this.customerForm = this.fb.group({
            firstName: ['', [Validators.required, Validators.minLength(3)]],
            lastName: ['', [Validators.required, Validators.maxLength(50)]]
        });
    
    }
    

    Also, if you need to reference one of the FormControls using the syntax above, you can do so like this:

    this.customerForm.get('lastName')
    

    Your method would then look something like this:

    this.filteredStatusList = this.myForm.get('status').valueChanges
      .pipe(
        startWith(''),
        map(status => status ? this.filterStatusList(status) : this.allStatuses.slice())
      );