0

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?

2
  • Have you tried a custom validator? Commented Mar 10, 2018 at 2:22
  • I didn't see an obvious way to apply a custom validator to this problem based on my reading of the docs, but @yurzui came up with exactly what I was looking for below. Commented Mar 10, 2018 at 21:07

1 Answer 1

2

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:

Sign up to request clarification or add additional context in comments.

3 Comments

That was exactly what I needed. Thanks a lot! ng-run looks awesome btw.
How about if you want a reactive form instead of a template driven form? Could you provide an example of that please?
@olefrank It should be much differ from template driven. You can find examples here stackoverflow.com/questions/39242219/…

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.