es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Prueba unitaria de Angular: Error: No hay acceso al valor del control del formulario.

Estoy abstrayendo cada campo de mi formulario de la siguiente manera (esto funciona en desarrollo pero no en mi prueba unitaria)

// required.control.ts
import { FormControl, Validators } from '@angular/forms';

export class RequiredControl extends FormControl {
  protected textErrors: { [key: string]: string } = {
    required: 'Campo obligatorio'
  };

  constructor(value: string | any = '') {
    super(value);

    this.setValidators([Validators.required]);
  }

  get textError() {
    let message = '';
    for (const error in this.textErrors) {
      if (error && this.hasError(error) && this.dirty) {
        message = this.textErrors

; return message; } } return message; } get state() { return this.valid || !this.dirty ? '' : 'error'; } }

De esta manera puedo limpiar mi formulario y llevar la lógica de validación de cada campo a un archivo separado. Ahora, en el componente principal importo este archivo:

// my-component.component.ts
import { RequiredControl } from './required.control.ts';

@Component({})
export class MyComponent implements OnInit {

  reasonControl = new RequiredControl(null);

  constructor() {}

  ngOnInit() {
     this.requestForm = this.formBuilder.group({
      reason: this.reasonControl, // primer método intentado :(
      reason:this.reasonControl as FormControl, // segundo método intentado :(
     });
   }
}

En mis pruebas unitarias tengo lo siguiente:

beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [MyComponent],
      imports: [ReactiveFormsModule, FormsModule, RouterTestingModule] 
    })
}));

Tengo esto en mi plantilla:

Imagen

Cuando ejecuto esta prueba, obtengo el siguiente error:

Imagen

Tags:  , , , ,

Answer

  1. Avatar for davy.ai

    El problema en el código proporcionado es que la clase RequiredControl extiende de FormControl pero no se exporta como FormControl. Por lo tanto, cuando se utiliza en el componente, el FormGroup no puede reconocerlo como un control de formulario válido.

    Para solucionar este problema, la clase RequiredControl puede implementar la interfaz ControlValueAccessor, lo que le permite actuar como un control de formulario que se puede utilizar con FormGroup. Aquí hay una versión actualizada de la clase RequiredControl:

    import { Component, forwardRef } from '@angular/core';
    import { ControlValueAccessor, NG_VALUE_ACCESSOR, FormControl, Validators } from '@angular/forms';
    
    @Component({
      selector: 'app-required-control',
      providers: [
        {
          provide: NG_VALUE_ACCESSOR,
          useExisting: forwardRef(() => RequiredControl),
          multi: true
        }
      ],
      template: ''
    })
    export class RequiredControl implements ControlValueAccessor {
      protected textErrors: { [key: string]: string } = {
        required: 'Campo obligatorio'
      };
      value: any;
      onChange: any = () => {};
      onTouched: any = () => {};
    
      constructor() {}
    
      writeValue(value: any) {
        this.value = value;
      }
    
      registerOnChange(fn: any) {
        this.onChange = fn;
      }
    
      registerOnTouched(fn: any) {
        this.onTouched = fn;
      }
    
      setDisabledState(isDisabled: boolean) {}
    
      get textError() {
        let message = '';
        for (const error in this.textErrors) {
          if (error && this.hasError(error) && this.dirty) {
            message = this.textErrors[error];
            return message;
          }
        }
        return message;
      }
    
      get state() {
        return this.valid || !this.dirty ? '' : 'error';
      }
    
      private hasError(error: string) {
        return this.control.hasError(error) && this.control.dirty;
      }
    
      private get valid() {
        return this.control.valid && this.control.dirty;
      }
    
      private get dirty() {
        return this.control.dirty || this.control.touched;
      }
    
      private get control() {
        return new FormControl(this.value, Validators.required);
      }
    }
    

    En esta versión actualizada, la clase RequiredControl implementa la interfaz ControlValueAccessor, lo que requiere que implemente varios métodos que le permiten interactuar con el FormGroup. Además, la clase ahora tiene un selector y una plantilla que le permiten ser utilizados directamente en HTML.

    Para utilizar RequiredControl en el componente, simplemente puedes importarlo y agregarlo al FormGroup de la siguiente manera:

    import { Component } from '@angular/core';
    import { FormGroup, FormBuilder } from '@angular/forms';
    import { RequiredControl } from './required.control';
    
    @Component({
      selector: 'app-my-component',
      template: `
        <form [formgroup]="requestForm">
          <input formcontrolname="reason" app-required-control="">
        </form>
      `
    })
    export class MyComponent {
      requestForm: FormGroup;
    
      constructor(private formBuilder: FormBuilder) {}
    
      ngOnInit() {
        this.requestForm = this.formBuilder.group({
          reason: [null]
        });
      }
    }
    

    En esta versión actualizada, RequiredControl se agrega como una directiva al elemento input utilizando el selector app-required-control. Esto permite que el FormGroup reconozca y valide la entrada como un control de formulario.

Comments are closed.