I'm working with Reactive Forms in Angular and I'm trying to create a custom form element so I can use buttons with my form. I'm trying to follow a tutorial from Alligator.io, but it isn't clear how to integrate this into my component. Currently I'm getting the following error.
ERROR Error: No value accessor for form control with name: 'coverage_for_domestic_partners'
Below is my code any help would be appreciated.
// HTML
<div class="button-group" [formGroup]="group">
<button
*ngFor="let option of config.options"
class="button button--toggle"
[ngClass]='{"is-active": option.selected}'
(click)="select($event, option)"
[formControlName]="config.key">
<div class="button-content">{{option.key}}</div>
</button>
</div>
// Component
import { Component, OnInit, Input, forwardRef, HostBinding } from '@angular/core';
import { FormGroup, ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
@Component({
selector: 'z-toggle-group',
templateUrl: './z-toggle-group.component.html',
styleUrls: ['./z-toggle-group.component.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => ZToggleGroupComponent),
multi: true
}
]
})
export class ZToggleGroupComponent implements OnInit, ControlValueAccessor {
@Input() config;
@Input() group: FormGroup;
@Input() disabled = false;
@HostBinding('style.opacity')
get opacity() {
return this.disabled ? 0.25 : 1;
}
clicked;
onChange = (rating: number) => {};
onTouched = () => {};
constructor() { }
ngOnInit() {
// console.log('form', this.group)
this.group.get(this.config.key).valueChanges.subscribe(value => {
console.log('formValue', value);
})
}
writeValue(obj: any): void {
throw new Error("Method not implemented.");
}
registerOnChange(fn: any): void {
throw new Error("Method not implemented.");
}
registerOnTouched(fn: any): void {
throw new Error("Method not implemented.");
}
setDisabledState?(isDisabled: boolean): void {
throw new Error("Method not implemented.");
}
select($event, option) {
this.clicked = $event;
this.group.get(this.config.key).patchValue(option.value, {onlySelf: true});
this.config.options.forEach(option => {
option.selected = false;
})
option.selected = true;
}
}
You need to implement ControlValueAccessor interface, to do this, you will need to implement this methods:
interface ControlValueAccessor {
writeValue(obj: any): void
registerOnChange(fn: any): void
registerOnTouched(fn: any): void
setDisabledState(isDisabled: boolean)?: void
}
You can see my example implementation right here.
When your component's value changes, you have to call OnChange
method, that you registered in your ts file.
You can see my call example here.
After that, you can use your component by its tag selector (in your case z-toggle-group
) inside a form, and you will be able to use formControlName
.
<div class="button-group" [formGroup]="group">
<button
*ngFor="let option of config.options"
class="button button--toggle"
[ngClass]='{"is-active": option.selected}'
(click)="select($event, option)">
<z-toggle-group [formControlName]="config.key"></z-toggle-group>
<div class="button-content">{{option.key}}</div>
</button>
</div>
Here is your code working.