This is an old question, but I was struggling with the same problem. In case anybody stumbles across this, here's how I interpret what is being asked, and my solution.
My component needs to be able to receive a [value]
and emit some (output)
, while ALSO being able to simply receive a formControlName
. I too needed to solve this problem. Use the following example:
<my-button-toggle formControlName="toggleValue"></my-button-toggle>
OR
<my-button-toggle
value="A"
(toggleChange)="handleChange($event)"
></my-button-toggle>
I fought with this for a while, but after looking at this repo it finally clicked for me.
Here's the html structure of my wrapped button-toggle component
<mat-button-toggle-group
[disabled]="disabled"
[multiple]="multiple"
[vertical]="vertical"
[value]="value"
(change)="doChange($event)">
...
And in the ts, we allow for formControlName as well as value setting:
export interface ToggleItem {
disabled?: boolean
value?: any
label: string
}
@Component({
selector: 'my-button-toggle',
templateUrl: './button-toggle.component.html',
styleUrls: ['./button-toggle.component.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: ButtonToggleComponent,
multi: true,
},
],
})
export class ButtonToggleComponent implements ControlValueAccessor {
@Input() value: any = ''
@Input() disabled = false
@Input() multiple = false
@Input() vertical = false
@Input() toggles: ToggleItem[] = []
@Output() toggleChange = new EventEmitter<any>()
onChange = (value: any) => {}
onTouched = () => {}
writeValue(value: string): void {
this.value = value
}
registerOnChange(fn: any): void {
this.onChange = fn
}
registerOnTouched(fn: any): void {
this.onTouched = fn
}
setDisabledState?(isDisabled: boolean): void {
this.disabled = isDisabled
}
doChange = (x: MatButtonToggleChange) => {
this.value = x.value
this.onChange(x.value)
this.toggleChange.emit(x.value)
}
}
So, what's happening is that when the button-toggle's (change)
event is fired, it calls the this.onChange
internally, which is handled by the ControlValueAccessor, but then I also tell my custom output to emit the value, so that any other type of non-reactive-form use case can still talk to the component.