Ryan Lester
Ryan Lester

Reputation: 2403

Angular child form field validation

I have some existing code that's structured more or less like this:

<!-- my-form.html -->

<form ngNativeValidate>
    <my-other-component></my-other-component>
    <button mat-button type='submit'>Submit</button>
</form>


<!-- my-other-component.html -->

<input name='native-input' required />

<mat-form-field>
    <mat-select name='balls' required>
        <mat-option value='a'>a</mat-option>
        <mat-option value='b'>b</mat-option>
        <mat-option value='c'>c</mat-option>
    </mat-select>
</mat-form-field>

The problem: while the native input is validated correctly — the browser displays a "Please fill out this field." prompt upon submission when needed — an empty input to balls is happily accepted.

The ngNativeValidate behavior of showing generic/non-descriptive errors only upon submission is fine for now, and we don't have time to convert everything into an Angular form with custom validation messages, so that isn't an option. What I'm looking for is a way to switch from native validation to Angular validation with the least possible effort to maintain an acceptable UX — e.g. simply disabling the submit button until the form input is valid.

As per angular.io/guide/form-validation, I see that I can change ngNativeValidate to #myForm='ngForm' and add [disabled]='myForm.invalid' to the submit button. However, this validation is only applying to form fields directly within my-form.html, not the ones in my-other-component.

Documentation here is really sparse, so I'm not sure what to try next. Does anyone know if it's possible to configure ngForm to validate those descendant components' form fields?

Upvotes: 0

Views: 1040

Answers (1)

yurzui
yurzui

Reputation: 214047

If you want to use template driven form approach then you have to import FormsModule into your @NgModule and apply NgModel directive to all your controls

<input name='native-input' ngModel required />
                           ^^^^^^^
<mat-form-field>
  <mat-select name='balls' ngModel required>
                             ^^^^^^^
    <mat-option value='a'>a</mat-option>
    <mat-option value='b'>b</mat-option>
    <mat-option value='c'>c</mat-option>
  </mat-select>
</mat-form-field>

Now your child component should be aware about parent form. In order to do that you need to provide ControlContainer token within viewProviders array:

@Component({
  selector: 'my-other-component',
  template: `
    ...
  `,
  viewProviders: [ { provide: ControlContainer, useExisting: NgForm } ]
})
export class MyOtherComponent {

Ng-run Example

See also:

Upvotes: 2

Related Questions