1

I am trying to write up a common input component for Material Angular, so I don't have to duplicate 20 lines of HTML code for every single field. But I am having issues getting the mat-error to show the validation message.

So this is the code I had in MyFormComponent:

<div class="form-group">

  <mat-form-field appearance="fill" hintLabel="Max 10 characters">
  
    <mat-label> Name Old (works) </mat-label>
    
    <input matInput type="text" formControlName="nameOld" />

    <mat-hint align="end">the/10</mat-hint>

    <mat-error *ngIf=" (formModal.get('nameOld').dirty || formModal.get('nameOld').touched) &&
        formModal.get('nameOld').errors && formModal.get('nameOld').errors['required'] ">
      Name Old is required.
    </mat-error>
      
  </mat-form-field>
  
</div>

Now I've moved this to a custom component CommonInputComponent as the following:

HTML:

<div class="form-group">
  <mat-form-field appearance="fill" hintLabel="Max 10 characters">
    <mat-label>{{ fieldInfo.label }}</mat-label>
    <input
      matInput
      [type]="fieldInfo.type"
      [placeholder]="fieldInfo.placeholder"
      [value]="value"
      (input)="onChange($event.target.value)"
      (blur)="onTouched()"
    />

    <mat-hint align="end">{{ ngControl.value?.length || 0 }}/10</mat-hint>

    <mat-error
      *ngIf="
        (ngControl.dirty || ngControl.touched) &&
        ngControl.errors &&
        ngControl.errors['required']
      "
      >Name is required.</mat-error
    >
  </mat-form-field>

  <pre>{{ ngControl.errors | json }}</pre>
  <pre>{{ { dirty: ngControl.dirty, touched: ngControl.touched } | json }}</pre>
</div>

and then I try to use it in my Form MyFormComponent as:

<app-common-input
  formControlName="name"
  [fieldInfo]="{ label: 'name', name: 'name', type: 'text' }"
></app-common-input>

All the bindings seem to work fine, the properties are updated across both the parent and child components, but the only issue I am facing is that mat-error doesn't seem to work as it did in the previous example.

What I am expecting in both fields below is that it should show the "Name is required." error message for both fields when the input is blank. But I only see it for the 2nd field (which is not using my common component).

enter image description here

I have created a working example in StackBlitz (https://stackblitz.com/edit/add-angular-material-yph1vt?file=src/app/app.component.ts)

1
  • It appears that the ng-invalid class is not added on the input. The component doesn't know MatError, if you change the tag to <p> or something it is there. And the necessary scss for styling the ng-invalid class is not loaded when you manually set it. I'm not sure how to fix it. But I'm curious and will investigate when I have more time. Commented May 16, 2022 at 23:58

1 Answer 1

1

The mat-input inside your app-common-input is not bound to any FormControl. You're working around it by binding value, onChange and onTouched events to the <input>, but none of that actually passes the information about validations and state of the FormControl itself.

And the way that mat-error works inside of a mat-form-field, it needs to know that the bound FormControl has some errors to display.

Since what you seem to be looking for is kind of a "pass-through" ControlValueAccessor, you can just replace this part:

<input
  matInput
  [type]="fieldInfo.type"
  [placeholder]="fieldInfo.placeholder"
  [value]="value"
  (input)="onChange($event.target.value)"
  (blur)="onTouched()"
/>

with this:

<input
  matInput
  [type]="fieldInfo.type"
  [placeholder]="fieldInfo.placeholder"
  [formControl]="ngControl.control"
/>

This means that everything that happens to your components will be passed down to the internal matInput and vice-versa.

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

3 Comments

Wow, that makes sense. I knew I was doing something wrong, but first time working in the new Angular couldn't trace it. So based on that, just a follow-up question. Do I really need to implement ControlValueAccessor in CommonInputComponent?
Because I don't really want to implement my own input, I just want to use the matInput. And just have a common HTML for label, hints, error messages.
I think it SHOULD work without implementing ControlValueAccessor - just getting the formControl from your component and passing it down to proper place. But I've never done this, so you should check it yourself.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.