A
A
Anton2021-07-09 14:09:06
Angular
Anton, 2021-07-09 14:09:06

How to display the entire list of control errors in Angular custom control?

I create a custom control (with ControlValueAccessor, etc.), guided by the article: https://blog.angular-university.io/angular-custom-...

I want all validation errors to be displayed in this control, even external ones, but in fact inside the control there is no list of errors.

Sample code: https://stackblitz.com/edit/ng-custom-control-err?...

Actual behavior:

60e82dbdf3590223792493.png

Expected:

component1 err: { "required": true }
component1 val: ""
component2 err: { "required": true }
component2 val: ""


Actual Behavior:

60e82de3eea73399362069.png

Expected:

component1 err: { "customErr": "customErr" }
component1 val: "e"
component2 err: { "customErr": "customErr" }
component2 val: "e"


How to force custom control component to show errors of formControl itself?

Just in case,
application code


CustomControlComponent:

import { Component, forwardRef, OnDestroy, OnInit } from '@angular/core';
import {
  ControlValueAccessor,
  FormBuilder,
  FormControl,
NG_VALIDATORS,
NG_VALUE_ACCESSOR,
  ValidationErrors,
  Validator
} from '@angular/forms';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';

@Component({
  selector: 'app-custom-control',
  templateUrl: './custom-control.component.html',
  styleUrls: ['./custom-control.component.css'],
    providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => CustomControlComponent),
      multi: true
    },
    {
      provide: NG_VALIDATORS,
      useExisting: forwardRef(() => CustomControlComponent),
      multi: true,
    }
  ],
})
export class CustomControlComponent
  implements OnInit, OnDestroy, ControlValueAccessor, Validator {
  control = this.fb.control(null);

  private onTouched = () => {};

  private readonly ngUnsubscribe$ = new Subject<void>();

  constructor(
    private fb: FormBuilder
  ) {}

  ngOnInit() {}

  ngOnDestroy() {
    this.ngUnsubscribe$.next();
    this.ngUnsubscribe$.complete();
  }

  registerOnChange(onChange: (value: string | null) => void) {
    this.control.valueChanges
    .pipe(
        takeUntil(this.ngUnsubscribe$)
    )
    .subscribe(onChange);
  }

  registerOnTouched(fn: () => void): void {
    this.onTouched = fn;
  }

  writeValue(outsideValue: string | null) {
    if (outsideValue) {
      this.control.setValue(outsideValue, { emitEvent: false });
    }
  }

  setDisabledState(disabled: boolean) {
    if (disabled) {
      this.control.disable();
    } else {
      this.control.enable();
    }
  }

  validate(c: FormControl): ValidationErrors | null {
    const value = this.control.value;

    const error = value === 'e';

    return error ? { customErr: 'customErr' } : null;
  }
}

<div>component2 err: {{control?.errors | json}}</div>
<div>component2 val: {{control?.value | json}}</div>

<input [formControl]="control" class="text-control" />


AppComponent:

<div>component1 err: {{control?.errors | json}}</div>
<div>component1 val: {{control?.value | json}}</div>

<hr>

<app-custom-control [formControl]="control"></app-custom-control>


import { Component, VERSION } from '@angular/core';
import { FormControl, Validators } from '@angular/forms';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  control = new FormControl(null, Validators.required);
}


Answer the question

In order to leave comments, you need to log in

1 answer(s)
A
Anton Shvets, 2021-07-09
@fattan

Because your component that implements ControlValueAccessor knows nothing about errors in the form control. He generally knows nothing about this control, the link here is the formControl directive, which listens to the control and calls writeValue when it is changed externally.
You can make your own directive that will call another method, such as setErrors, but there is no standard such function.
Here in this article you can find a similar implementation, only pristine and touched are transferred there. https://habr.com/ru/post/491062/
Or easier, just pass errors inside via input :)
And in order to transfer internal errors to the top control, you need to implement Validator

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question