0

I want to create a custom form input component, that will work reactively.

I created a component (TS)

import { Component, forwardRef, Input, OnInit } from "@angular/core";
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from "@angular/forms";

@Component({
  selector: "app-new-input",
  templateUrl: "./new-input.component.html",
  styleUrls: ["./new-input.component.css"],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => NewInputComponent),
      multi: true
    }
  ]
})
export class NewInputComponent implements ControlValueAccessor, OnInit {
  @Input() label: string = "";
  @Input() placeholder: string = "";
  @Input() type: string = "text";

  propagateChange: any = () => {};
  onTouch: any = () => {};
  value: string = "";

  constructor() {}

  ngOnInit() {}

  onChange(event: any) {
    this.value = event.target.value;
    this.propagateChange(this.value);
  }

  writeValue(value: string): void {
    this.value = value ? value : "";
  }
  registerOnChange(fn: any): void {
    this.propagateChange = fn;
  }
  registerOnTouched(fn: any): void {
    this.propagateChange = fn;
  }
  setDisabledState?(isDisabled: boolean): void {}
}

and the HTML

{{label}}

<input matInput [type]="type" [value]="value" [placeholder]="placeholder" (change)="onChange($event)" (keyup)="onChange($event)">

in my app component I have

  searchForm: FormGroup = this.formBuilder.group({
    name: [""]
  });

  constructor(private formBuilder: FormBuilder) {}

and in app component HTML

<form [formGroup]="searchForm">
  <app-new-input formControlName="name"></app-new-input>
</form>

{{searchForm.value | json}}

All this can be found on STACKBLITZ

So, the problem I am having is that the changes from the component input do not propagate to the parent (app component) form

Could somebody tell me what am I doing wrong?

1 Answer 1

1

Your mistake is a simple one - you are setting propagateChange in both registerOnChange and registerOnTouched. Instead, assign to onTouch in registerOnTouched.

registerOnTouched(fn: any): void {
  this.onTouch = fn;
}

registerOnTouched is getting called after registerOnChange by the framework, so you are emitting touch events when you are intending to emit change events.

DEMO https://stackblitz.com/edit/angular-ve8sri

Sign up to request clarification or add additional context in comments.

2 Comments

Kurt, you save hour and hours of my headache :P Cheers.... I have another question.... I have 2 inputs now on the page... both using same formControlName... one is custom one is direct... check here... stackblitz.com/edit/… .... they are not updating syncronously, why?
I imagine because that's not how forms are meant to work. This doesn't work with 2 regular inputs either. stackblitz.com/edit/angular-muc59a

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.