I made a custom form field control in Angular Material following this guide.
Then I add this control in my FormGroup
. But the problem I have here is the FormGroup
is not able to get the correct value of the custom control. It always gets undefined
. I did check if the correct values are being seeded to the value
property in the custom control from the input and it does.
What could be the problem here?
My Custom Control: The Component
import { Component, OnDestroy, HostBinding, Input, Optional, Self, ElementRef } from '@angular/core';
import { MatFormFieldControl } from '@angular/material/form-field';
import { Subject } from 'rxjs/internal/Subject';
import { NgControl, ControlValueAccessor, FormBuilder, FormGroup } from '@angular/forms';
import { FocusMonitor } from '@angular/cdk/a11y';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
@Component({
selector: 'app-test-input',
templateUrl: './test-input.html',
styleUrls: ['./test-input.scss'],
providers: [{ provide: MatFormFieldControl, useExisting: MyTestInput }]
})
export class MyTestInput implements MatFormFieldControl<string>, OnDestroy, ControlValueAccessor {
static nextId = 0;
FormGrp: FormGroup;
stateChanges = new Subject<void>();
private val: string;
private ph: string;
private req = false;
private dis = false;
onChange: () => void;
onTouched: () => void;
public get value(): string {
return this.val;
}
public set value(val: string) {
this.val = val;
this.stateChanges.next();
}
controlType = 'my-test-input';
@HostBinding() id = `${this.controlType}-${MyTestInput.nextId++}`;
@Input()
get placeholder() {
return this.ph;
}
set placeholder(plh) {
this.ph = plh;
this.stateChanges.next();
}
focused = false;
get empty() {
return false;
}
@HostBinding('class.floating')
get shouldLabelFloat() {
return this.focused || !this.empty;
}
@Input()
get required() {
return this.req;
}
set required(req) {
this.req = coerceBooleanProperty(req);
this.stateChanges.next();
}
@Input()
get disabled(): boolean { return this.dis; }
set disabled(value: boolean) {
this.dis = coerceBooleanProperty(value);
this.stateChanges.next();
}
errorState = this.FormGrp == null ? false : this.FormGrp.invalid;
@HostBinding('attr.aria-describedby') describedBy = '';
setDescribedByIds(ids: string[]) {
this.describedBy = ids.join(' ');
}
onContainerClick(event: MouseEvent) {
if ((event.target as Element).tagName.toLowerCase() !== 'input') {
this.elRef.nativeElement.querySelector('input').focus();
}
this.onTouched();
}
ngOnDestroy() {
this.stateChanges.complete();
this.fm.stopMonitoring(this.elRef.nativeElement);
}
constructor(
@Optional() @Self() public ngControl: NgControl,
fb: FormBuilder, private fm: FocusMonitor, private elRef: ElementRef<HTMLElement>) {
this.FormGrp = fb.group({
data: ['', this.required]
});
if (ngControl != null) {
ngControl.valueAccessor = this;
}
fm.monitor(elRef.nativeElement, true).subscribe(origin => {
this.focused = !!origin;
this.stateChanges.next();
});
}
writeValue(value: any): void {
this.FormGrp.get('data').setValue(value);
}
registerOnChange(fn: any): void {
this.onChange = fn;
}
registerOnTouched(fn: any): void {
this.onTouched = fn;
}
setDisabledState(isDisabled: boolean): void {
isDisabled ? this.FormGrp.get('data').disable() : this.FormGrp.get('data').enable();
}
input() {
this.value = this.FormGrp.get('data').value;
this.onChange();
}
}
The Template:
<div [formGroup]="FormGrp">
<input formControlName="data" (input)="input()">
</div>
My Calling form:
<form [formGroup]="Form">
<mat-form-field>
<app-test-input formControlName="testControl"></app-test-input>
</mat-form-field>
<button>Submit</button>
</form>
<p *ngIf="Form">
{{Form.value | json}}
</p>
My Calling form definition:
this.Form = fb.group({
testControl: ['', [Validators.required]]
});
You forgot to pass updated value to onChange
method which is part of ControlValueAccessor implementation:
test-input.component.ts
export class TestInputComponent ... {
onChange = (_: any) => {};
...
input() {
this.value = this.FormGrp.get('data').value;
this.onChange(this.value);
\/
pass newValue
}
Stackblitz Example
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With