Angular Custom Control: change event isn't triggered in reactive form

238 Views Asked by At

This was working fine for some time until I found out it wasn't. The issue I face is the following.

I have an abstract value accessor class which is being extended by multiple custom form controls. It looks like this:

@Directive()
export abstract class AbstractInputComponent implements OnChanges {

  @Input() public isReadOnly = false;

  protected innerValue: unknown = null;
  public registerOnChange(fn: any) { this.propagateChange = fn; }
  public registerOnTouched(fn: any) { this.propagateTouch = fn; }
  public propagateChange = (_: any) => { };
  public propagateTouch = (_: any) => { };

  public ngOnChanges(): void {
    if ( this.isReadOnly ) {
        this.propagateChange(this.value);
    }
    this.cdr.detectChanges();
  }

  public get value(): any {
    return this.innerValue;
  };

  @Input() public set value(value: any) {
    if ( value !== undefined ) {
        this.writeValue(value);
        this.propagateChange(this.innerValue);
        this.propagateTouch(true);
    }
  }

  public writeValue(value: any): void {
    if (value !== this.innerValue) {
        this.innerValue = this.normalize(value);
    }
  }
}

One of the custom controls looks like this:

@Component({
    selector: 'ultim-checkbox',
    encapsulation: ViewEncapsulation.None,
    templateUrl: './checkbox-input.component.html',
    providers: [
        {
          provide: NG_VALUE_ACCESSOR,
          useExisting: forwardRef( ()=> CheckboxInputComponent ),
          multi: true
        },
        {
          provide: NG_VALIDATORS,
          useExisting: forwardRef( ()=> CheckboxInputComponent ),
          multi: true
        }
      ]
})
export class CheckboxInputComponent extends AbstractInputComponent implements ControlValueAccessor, Validator  {
   constructor ( protected cdr: ChangeDetectorRef ) {
       super(cdr);
   }
}

So, then in the big parent template I have this kind of markup:

<ultim-checkbox formControlName="isAdmin"
    (change)="setAdmin()"></ultim-checkbox>

And setAdmin() method never gets called. Any ideas why?

Angular 11
Typescript 5.1.6

1

There are 1 best solutions below

3
yashaswi k On

The problem lies in how you're binding the (change) event in the parent template. Since you're using Angular's reactive forms with formControlName, the correct event to listen for changes is the (ngModelChange) event, not the standard (change) event.

Here's how you should modify the parent template:

<ultim-checkbox formControlName="isAdmin" (ngModelChange)="setAdmin()"></ultim-checkbox>