Angular ControlValueAccessor once for all

ControlValueAccessor implemented once with Typescript mixin.

Redin Gaetan
2 min readAug 15, 2021

In a project, I have chosen to create from scratch our atomic design system. As expected, It came the moment to work on form fields. I prefer to use the reactive forms, and consequently, we have to implement the ControlValueAccessor interface.

interface ControlValueAccessor {
writeValue(obj: any): void; // allow to update this value from control value
registerOnChange(fn: any): void; // register the method to call tu update the control value from this value
registerOnTouched(fn: any): void; // register the method to call when we need to mark as touched the control
setDisabledState(isDisabled: boolean)?: void; // optional, allow to handle the disabled state from control disabling state
}

It means to have an internal value, an internal onChange function, and an internal onTouched function. Often, when we have a value to handle, our directive/component also has a valueChange EventEmitter to notify the parent.

I find that very repetitive and not DRY to implement it in each component/directive which needs it… Maybe you know a better solution (, and I’m interested to know it :-)) but now I use a typescript mixin.

Here’s the official documentation. To sum up, it allows centralizing a piece of code that you can get in all classes you want. It’s also a good alternative for multiple inheritances, which is not officially possible.

Here we have the steps to follow:

And now, this is how to use it:

const baseInputMixin: CanControlValueAccessorCtor<any> = canControlValueAccessorMixin(class {});

@Directive({
...,
inputs: ['value'],
outputs: ['valueChange'],
})
export MyDirective extends baseInputMixin {
// Try into your favorite IDE to use this. and autocompletion, you will see all the CanControlValueAccessor interface's definition as accessible.
}

I choose the host.inputs and host.outputs metadata properties to simplify the code and not repeat it to use the decorators @Input() and @Output().

Well, I hope to help someone simplify his code, and I’m open-minded if you have a better solution/line of thinking.

Thanks for reading.

--

--