0
votes

I have a form made with AngularMaterial form components. I want to display an error when the form control has an invalid state. That message should be calculated everytime either the status or value of the form control changes. As an input i will have the form control and the errors object.

I have approached this problem creating a structural directive but i am not sure it is the best solution for it, the syntax make me doubt about it.

Directive:

@Directive({
  selector: '[appMensajeErrores]'
})
export class ErrorValidacionDirective implements OnInit, AfterViewInit, OnDestroy {
  control: AbstractControl;
  suscripcion: Subscription;
  errores: { [key: string]: string };

  @Input() set appMensajeErroresControl(c: AbstractControl) {
    this.control = c;
  }

  @Input() set appMensajeErrores(errores: { [key: string]: string }) {
    this.errores = errores;
  }

  constructor(
    private templateRef: TemplateRef<any>,
    private viewContainer: ViewContainerRef
  ) { }

  ngOnInit(): void {
  }

  ngAfterViewInit(): void {
    this.suscripcion = merge(this.control.valueChanges, this.control.statusChanges).subscribe(_ =>
      this.manejarEstadoValidez(), e => console.error(e)
    );
  }

  manejarEstadoValidez(): void {
    if (this.control.invalid) {
      const mensaje = this.generarMensaje(this.control, this.errores);
      this.viewContainer.clear();
      this.viewContainer.createEmbeddedView(this.templateRef, { $implicit: mensaje });
    } else {
      this.viewContainer.clear();
    }
  }

  generarMensaje(control: AbstractControl, errores: { [key: string]: string }): string {
    return Object.keys(control.errors)
      .reduce((acc, error) => errores[error] ? acc.concat(errores[error]) : acc, [])
      .join(',');
  }

  ngOnDestroy(): void {
    this.suscripcion.unsubscribe();
  }

}

Directive applied:

<mat-form-field floatLabel="always" >
        <mat-label>Magnitud</mat-label>
        <input matInput type="number" formControlName="magnitud">
        <mat-error *appMensajeErrores="{ required: 'Es requerido', max: 'Maximo 600' };let msg; control: form.get('parametros.magnitud')">{{msg}}</mat-error>
      </mat-form-field>

StackBlitz: https://stackblitz.com/edit/angular-material-q3wbeh

Currently the approach i took works but i would like to know other alternatives or suggestions to improve it.

1
I have added StackBlitz example of my approachIván Portilla

1 Answers

0
votes

I would have done this using the 'hasErrors' AbstractControl. This way, we write the error messages separately inside the HTML and define the triggers in the declaration of the FormControl. Also, this cuts the need for any methods.

// Declare the 'magnitud' control
magnitud: FormControl;

// Initialize the control with the 'required' and 'max' validators
ngOnInit(): void {
  this.magnitud = new FormControl('', [Validators.required, Validators.max(600)]);
}

The HTML would look something like this:

<mat-form-field floatLabel="always" >
  <mat-label>Magnitud</mat-label>
  <input matInput type="number" formControlName="magnitud">
  <mat-error *ngIf="magnitud.hasError('required')">Es requerido</mat-error>
  <mat-error *ngIf="magnitud.hasError('max')">Maximo 600</mat-error>
</mat-form-field>

Hope this helps.