skip to Main Content

My loginform is has 2 childs components (one for email and one for password).

I use formGroup to store input entries. On loading I have an error

  • "No value accessor for form control name: ’email’"
  • "No value accessor for form control name: ‘password’.
    and some informations are missing on display.

After clicking submit button, missing informations appear on the screen and submit is working

My code:

html parent

<form [formGroup]="loginFormGroup" (submit)="validateLoginData()">     
      <div class="login">
        <!--button container-->
        <div class="login__input__container">

          <!-- login-->
          <app-input
            [formGroup]="loginFormGroup"       
            type="text"                  
            formControlName="email"           
            placeHolder="[email protected]"
            name="email"
            id="email"
            labelText="email"
          ></app-input>          

          <!-- password-->
          <app-input
            [formGroup]="loginFormGroup"       
            type="password"                  
            formControlName="password"           
            placeHolder="mot de passe"
            name="password"
            id="password"
            labelText="password"
          ></app-input>  
        </div>
        <div class="login__button__container">
          <input type="submit" value="se connecter">
        </div>
      </div>
    </form>    

ts parent

ngOnInit() {
    this.initializeLoginFormGroup();
  }

  /**
   * Construction du formgroup
   */
  initializeLoginFormGroup() {    
    this.loginFormGroup = this.fb.group({     
      // Email
      email: ['', Validators.required],

      // Password
      password: ['', Validators.required]
    })
  }

html child

<div class="input__group" [formGroup]="formGroup">  
  <label class="input__label" [for]="id">{{labelText}}</label>
  <input
    class="input"
    [type]="type" 
    [formControlName]="formControlName"   
    [placeholder]="placeHolder"
    [name]="name" 
    [id]="id"
  >
  <!-- Erreur Message-->
  <div>
    <p>{{errorMessage}}</p>
  </div>
</div>

ts child

export class InputComponent {
 
  @Input() id: string = '';

  @Input() type: string = '';

  @Input() formControlName: string = '';

  @Input() placeHolder: string = '';

  @Input() name: string = '';

  @Input() errorMessage: string ='';

  @Input() labelText: string = ''

  @Input() formGroup: FormGroup = new FormGroup({});
}

Hope you can help to solve this error

2

Answers


  1. Chosen as BEST ANSWER

    Indeed I've to implement controlValueAccessor to make work my formControl.

    Here is my updated code with error event:

    html child

    <div class="input__group" [formGroup]="formGroup">  
      <label class="input__label" [for]="id">{{labelText}}</label>
      <input #input
        [class.border--error]="formGroup.get(formControlName)?.touched && formGroup.get(formControlName)?.errors?.['required']"
        class="input"
        [type]="type" 
        [formControlName]="formControlName"   
        [placeholder]="placeHolder"
        [name]="name" 
        [id]="id"
      >
      <!-- Erreur Message-->
      <div *ngIf="formGroup.get(formControlName)?.touched && formGroup.get(formControlName)?.errors?.['required']">
        <p>{{errorMessage}}</p>
      </div>
    </div>
    

    ts child

    @Component({
      selector: 'app-input',
      templateUrl: './input.component.html',
      styleUrls: ['./input.component.css'],
      providers: [{
        provide: NG_VALUE_ACCESSOR,
        useExisting: forwardRef(() => InputComponent),
        multi: true,
      }]
    })
    export class InputComponent implements ControlValueAccessor {
    
      @ViewChild('input', {static: false}) input!: ElementRef;
    
      @Input() autoFocus = false;
    
      @Input() id: string = '';
    
      @Input() type: string = '';
    
      @Input() formControlName: string = '';
    
      @Input() placeHolder: string = '';
    
      @Input() name: string = '';
    
      @Input() errorMessage: string ='';
    
      @Input() labelText: string = ''
    
      @Input() formGroup: FormGroup = new FormGroup({});
      
      ngAfterViewInit() {
        if(this.autoFocus) {
          this.onFocus()
        }
      }
    
      //#region ControlValueAccessor
        value = '';
    
        @Input() isDisabled!: boolean;
    
        onChange!: (value?: any) => void;
    
        onTouch!: (event: any) => void;
    
        writeValue(obj: any): void {
          this.value = obj;
        }
    
        registerOnChange(fn: any): void {
          this.onChange = fn
        }  
    
        registerOnTouched(fn: any): void {
          this.onTouch = fn
        }
    
        setDisabledState?(isDisabled: boolean): void {
          this.isDisabled = isDisabled;
        }
        
        onInput(value: any) {
          if(this.onChange) {
            this.onChange(value);
          }
        }
    
        onTouched(value: any) {
          if(this.onTouch) {
            this.onTouch(value)
          }
        }
       
      //#endregion
      
    
      onFocus() {
        //Normal Focus Method
        this.input.nativeElement.focus();
    
        // Another Method for set Focus
        //  this.input.nativeElement.select();
      }
    }
    

    thanks for your help

    Cyrille


  2. Don’t need a formBuilder but rather a plain FormGroup. I would modify the TS file this way and it should work perfectly. (In the parent)

    initializeLoginFormGroup() {    
        this.loginFormGroup = new FormGroup({     
          // Email
          email: new FormControl<string>('', {
            nonNullable: true,
            validators: Validators.required,
          }),
    
          // Password
          password: new FormControl<string>('', {
            nonNullable: true,
            validators: Validators.required,
          })
        })
    

    And, for the child, delete this from @Input:

     @Input() formGroup: FormGroup = new FormGroup({});
    

    change it to this:

    @Input() formGroup!: FormGroup;
    

    child html:

    <div class="input__group" [formGroup]="formGroup">  
      <label class="input__label" [for]="id">{{labelText}}</label>
      <input
        [ngClass]="{'error_border': formGroup.get(formControlName)?.touched && formGroup.get(formControlName)?.invalid}"
        class="input"
        [type]="type" 
        [formControlName]="formControlName"   
        [placeholder]="placeHolder"
        [name]="name" 
        [id]="id"
      >
      <!-- Erreur Message-->
      <div *ngIf='formGroup.get(formControlName)?.touched && formGroup.get(formControlName)?.invalid'>
        <p>{{errorMessage}}</p>
      </div>
    </div>
    

    child css:

    .error_border {
    border: 1px solid red;
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search