Angular material dialog pre-populated input field does not validate

Viewed 5008

I have a mat-dialog that opens from an edit button in a (mat-)table. When the dialog opens it gets passed data to populate some input fields.
One of those inputs has validation with the rule that it cannot be empty.

My problem is that the input element that receives an auto-populated value is not recognized by Angular as having a value at all (it has a 'value' in the web-inspector and on the screen).

This means that until the user edits the field either by deleting and reentering a character or typing something new, the 'save' button remains greyed-out. Not al fields need to be edited, if they receive a value it is a valid one.

So how can I get the validation to recognize that the input field is not empty but also that it does have a value? (The reason I can't just remove the validation, it has to have a value, ie != '').

So unless the user input is empty to start with or the user deletes the input, the validation should pass. Or to say it another way, if the input receives a value when the dialog opens, and the user leaves it well alone, the validation should pass.

A side effect of the auto-populated values not being recognized is that if a number is unchanged then it gets posted as null to the backend. So when the data is refreshed in the view, a number that was 30 and not edited by the user is now 0. This, because the form is displaying the auto-populated data, but somehow angular isn't "seeing" it.

simple input:

  <div class="form">
    <mat-form-field>
      <input matInput #input
             class="form-control"
             placeholder="Name"
             [(ngModel)]="data.name"
             name="name"
             selected="data.name"
             required
             [value]="this.getFunc?.name"
      >
      <mat-error *ngIf="data.name == ''">{{getErrorMessage()}}</mat-error>
    </mat-form-field>
  </div>

Not sure what to show from the component.ts files here(?!)
Using dialogRef.componentInstance = foo.bar; in the parent component, the dialog gets it's correct values in the input fields and IF the user deletes and then retypes a value, the correct value is sent back via the API.

The issue is that angular can't 'see' the input.
Tried a lot of different stuff, including every other answer on SO/here.
Sorry long text and little code, but there isn't anything to show how angular doesn't recognize that the input field holds a value.

1 Answers

I searched long and hard for an answer to this problem, and I think I have the "correct" way to it (one of them at least). So I'll post it here:

First, I changed my form to use 'formGroup' in the form element like so:

<form class="mat-dialog-content" (ngSubmit)="submit()" #formControl="ngForm" [formGroup]="patchForm">
      <div class="form">
        <mat-form-field>
          <input matInput #input
                 class="form-control"
                 placeholder="name"
                 name="name"
                 formControlName="name"
                 id="name"
                 selected="data.name"
                 required
          >
          <mat-error *ngIf="data.name== ''">{{getErrorMessage()}}</mat-error>
        </mat-form-field>
      </div>
      [... repeat div class="form" blocks for other inputs/dropdowns etc ...]

Notice that I had to remove [(ngModel)]="someVal" from all the form field elements. Angular throws a wobbly if you try to use them with formGroup.

Now add the element attribute formControlName="name" you see in the code above.

I then had to add to app.module.ts ReactiveFormsModule:

import { FormsModule, ReactiveFormsModule } from '@angular/forms';

and also add this to the imports array of that same file:

 imports: [
    BrowserModule,
    ...
    FormsModule,
    ReactiveFormsModule
  ],  

In the component.ts file I set up the [formGroup]="patchForm" according to the documentation here https://angular.io/api/forms/FormGroup#setvalue
I have 3 fields in my dialog:

  patchForm = new FormGroup({
    age: new FormControl(),
    name: new FormControl(),
    numberLegs: new FormControl()
  });

So, the formControl expects an object correspondng to the form. In my onInit function i have a call to the API to get the data I want to populate the form with, and so inside that subscriber I am using patchValue() method from Angular to set the values:

this.projectService.getProjectFunction(this.projId, this.rowId).subscribe(x=> { 
  this.fetchedProjFunc = x;
  // patchFrom expects an object matching to the formgroup, field by field.
  this.patchForm.patchValue({
    age: this.fetchedProjFunc['age'],
    name: this.fetchedProjFunc['name'],
    numberLegs: this.fetchedProjFunc['numberLegs']
   });
});

That seemed to work. Then I had to set the valules of the object I was returning to the API/database in my return object to point to the correct values, so when the user clicks the button "save" it calls a 'confirmPut' function that contains the object data to be sent, like so:

 public confirmPut(): void {
    this.projectFunction = {
        ...
        name: this.patchForm.value['name'],
        ...

which in turn calls the function that does the PUT to the API.

I think that was it, the form validates against empty strings and missing values as expected.

If I have missed anything and anyone ever reads this, let me know... :)

Related