Components: Custom control does not show error when reactive form control is touched programmatically

Created on 19 Jul 2018  路  7Comments  路  Source: angular/components

Bug, feature request, or proposal:

Bug

What is the expected behavior?

When invoking markAsTouched for reactive form control whose value is invalid, corresponding custom material control (which implements ControlValueAccessor), should be highlighted and errors should be shown.

What is the current behavior?

The control is highlighted, but no errors are shown. If the control get focused, then errors are shown.

What are the steps to reproduce?

StackBlitz starter: https://stackblitz.com/edit/angular-a2rrbx

Click the touch button. Then focus on the input.

What is the use-case or motivation for changing an existing behavior?

Current behavior of custom control is not consistent with built-in material controls.

Which versions of Angular, Material, OS, TypeScript, browsers are affected?

Angular 6.0.9, Angular/Material 6.4.0. Also present in previous versions, at least in 5.2.x. Tried in latest Chrome and IE 11.

P4 docs

Most helpful comment

Checks the source code of MatInput:
https://github.com/angular/material2/blob/master/src/lib/input/input.ts

export class CustomComponent extends CustomComponentBase {
...
constructor(
  ..., 
  @Optional() @Self() public ngControl: NgControl,
  @Optional() _parentForm: NgForm,
  @Optional() _parentFormGroup: FormGroupDirective,
  _defaultErrorStateMatcher: ErrorStateMatcher
  ...,
) {
  super(_defaultErrorStateMatcher, _parentForm, _parentFormGroup, ngControl);
  // Setting the value accessor directly (instead of using
  // the providers) to avoid running into a circular import.
  if (this.ngControl != null) { this.ngControl.valueAccessor = this; }
}
...

404 for the link

All 7 comments

This is not a bug, you can look the source of MatInput:
https://github.com/angular/material2/blob/master/src/lib/input/input.ts

Your custom form component has to extend what you get from mixinErrorState:

export class CustomComponentBase {
   constructor(
       public _defaultErrorStateMatcher: ErrorStateMatcher,
       public _parentForm: NgForm,
       public _parentFormGroup: FormGroupDirective,
       public ngControl: NgControl
   ) { }
}
export const _CustomComponentMixinBase = mixinErrorState(CustomComponentBase);

As you implement MatFormFieldControl, the property errorState is mandatory but the mixinErrorState will implement this property for you :)
Then, call updateErrorState on ngDoCheck to display (or not) errors.

I guess the documentation could be better on this point ;)

Thanks!
I still don't understand though how to properly implement the mixin for my case - it throws injection error for NgForm.

Checks the source code of MatInput:
https://github.com/angular/material2/blob/master/src/lib/input/input.ts

export class CustomComponent extends CustomComponentBase {
...
constructor(
  ..., 
  @Optional() @Self() public ngControl: NgControl,
  @Optional() _parentForm: NgForm,
  @Optional() _parentFormGroup: FormGroupDirective,
  _defaultErrorStateMatcher: ErrorStateMatcher
  ...,
) {
  super(_defaultErrorStateMatcher, _parentForm, _parentFormGroup, ngControl);
  // Setting the value accessor directly (instead of using
  // the providers) to avoid running into a circular import.
  if (this.ngControl != null) { this.ngControl.valueAccessor = this; }
}
...

Thank you! I the error was because I forgot to put Self on ngControl.

Checks the source code of MatInput:
https://github.com/angular/material2/blob/master/src/lib/input/input.ts

export class CustomComponent extends CustomComponentBase {
...
constructor(
  ..., 
  @Optional() @Self() public ngControl: NgControl,
  @Optional() _parentForm: NgForm,
  @Optional() _parentFormGroup: FormGroupDirective,
  _defaultErrorStateMatcher: ErrorStateMatcher
  ...,
) {
  super(_defaultErrorStateMatcher, _parentForm, _parentFormGroup, ngControl);
  // Setting the value accessor directly (instead of using
  // the providers) to avoid running into a circular import.
  if (this.ngControl != null) { this.ngControl.valueAccessor = this; }
}
...

404 for the link

Thanks @rsaenen , this helped a lot. And you are right, the documentation could be better. It is not documented anywhere in the docs.
Also, if anyone is still looking the new source is at
https://github.com/angular/components/blob/master/src/material/input/input.ts

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

_This action has been performed automatically by a bot._

Was this page helpful?
0 / 5 - 0 ratings

Related issues

3mp3ri0r picture 3mp3ri0r  路  3Comments

xtianus79 picture xtianus79  路  3Comments

savaryt picture savaryt  路  3Comments

MurhafSousli picture MurhafSousli  路  3Comments

constantinlucian picture constantinlucian  路  3Comments