Custom directive implementing controlValueAccessor does not trigger writeValue if the formControl has been updated by another controlValueAccessor
Which @angular/* package(s) are the source of the bug?
forms
Is this a regression?
No
Description
I've created a custom directive implementing controlValueAccessor and added it to two html input elements.
The writeValue function is not called if the input value is provided using [formControl] and the value is updated through the registerOnChange callback.
The writeValue is instead correctly called if the input value is provided by [(ngModel)] instead of [formControl]
Export class customDirective implements controlValueAccessor ...
With ngModel works fine
<input customDirective [(ngModel)]="value" />
<input customDirective [(ngModel)]="value" />
With formControl there's the issue
<input customDirective [formControl]="myFormControl" />
<input customDirective [formControl]="myFormControl" />
Please provide a link to a minimal reproduction of the bug
https://stackblitz.com/edit/angular-ivy-a2loir?file=src%2Fapp%2Fapp.component.html
Please provide the exception or error you saw
No response
Please provide the environment you discovered this bug in
No response
Anything else?
No response
I've just stumbled over the same bug.
The buggy behavior seems to be due to this code specifying emitModelToViewChange: false in its options:
https://github.com/angular/angular/blob/8ebc946c0e7bf80d26ec8268acb4ff0af9e5c34a/packages/forms/src/model.ts#L1471
This leads to the following if-statement evaluating to false. However it is only within this if-block, where all the other "writeValue()" instances are called (stored in the _onChange array).
https://github.com/angular/angular/blob/8ebc946c0e7bf80d26ec8268acb4ff0af9e5c34a/packages/forms/src/model.ts#L1338-L1341
This is still an issue on Angular 14 (didn't test on 17 yet)
FWIW this repros on ng17 and I assume ng18 since there's no meaningful change in the relevant code. You don't need a custom value accessor, it repros with the default value accessors:
<!-- With ngModel works fine -- >
<input [(ngModel)]="value" />
<input [(ngModel)]="value" />
<!-- With formControl there's the issue -->
<input [formControl]="myFormControl" />
<input [formControl]="myFormControl" />
The control value accessor pushes a new value to the model here: https://github.com/angular/angular/blob/8ebc946c0e7bf80d26ec8268acb4ff0af9e5c34a/packages/forms/src/directives/shared.ts#L223 This assumes that only a single view is bound to the model: the view sent us the value, it's already up to date, no need to write it back. As a consequence all other views except the one that triggered the change goes out of sync.
For [(ngModel)] the change detector pushes the value to all views, that's why it seems to work.