angularangular-template-form

Angular - input wrapped in custom component not working in template driven form


Just like the title says, if I wrap an input in my custom component, it's not being picked up by Angular's template form. Here's the stackblitz

Here's some code from StackBlitz

app.component.html

<div ngForm #myForm="ngForm">
  <app-custom-textbox
    customName="wrappedInput"
    customValue="wrapped"
  ></app-custom-textbox>
  <hr />
  <input name="noWrapper" [(ngModel)]="message" />
</div>
<hr />
<p>myForm.value is below:</p>
{{ myForm.value | json }}

custom-textbox.component.html

<p>My custom textbox component</p>
<input type="textbox" name="{{ customName }}" [(ngModel)]="customValue" />

custom-textbox.component.ts

import { Component, Input } from '@angular/core';

@Component({
  selector: 'app-custom-textbox',
  templateUrl: './custom-textbox.component.html',
})
export class CustomTextboxComponent {
  @Input() customName: string = '';
  @Input() customValue: string = '';
}

I expect to see both wrappedInput and noWrapper properties in myForm.value output, but only noWrapper is picked up.

enter image description here


Solution

  • Need to implement ControlValueAccessor for the CustomTextBoxComponent like below:~

    import { Component, forwardRef, Input } from '@angular/core';
    import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
    
    @Component({
      selector: 'app-custom-textbox',
      templateUrl: './custom-textbox.component.html',
      providers: [
        {
          provide: NG_VALUE_ACCESSOR,
          useExisting: forwardRef(() => CustomTextboxComponent),
          multi: true,
        }
      ],
    })
    export class CustomTextboxComponent implements ControlValueAccessor {
      @Input() customValue: string = '';
    
      propagateChange = (_: any) => {};
      public onTouched: any = () => {};
    
      writeValue(obj: any): void {
        this.customValue = obj;
      }
    
      registerOnChange(fn: any): void {
        this.propagateChange = fn;
      }
    
      registerOnTouched(fn: any): void {
        this.onTouched = fn;
      }
    
      onModelChange(val: any) {
        this.propagateChange(val);
      }
    }
    
    

    Working Stackblitz