问题
I have a custom component (Angular 6) called ppo-currency-field
with the following template:
<span class="display" tabindex="-1">{{formattedValue()}}</span>
<input #input class="input" type="number" [name]="name"
[tabindex]="tabindex" [readonly]="!!readonly || readonly===''"
[disabled]="!!disabled || disabled===''" [ngModel]="value"
(ngModelChange)="writeValue($event)" [ngModelOptions]="ngModelOptions">
My component code contains the line:
@Input() ngModelOptions: Object;
I'm using my component with:
<ppo-currency-field [ngModel]="data.planningHours.rate"
(ngModelChange)="data.planningHours.rate = $event; recalc();"
[ngModelOptions]="{ updateOn: 'blur' }"
[disabled]="!data.containsPlanning ? '' : null"></ppo-currency-field>
As you can see, I'm attempting to "pass through" the ngModelOptions
from my custom component to the input
element. But this doesn't work, because the update option is not being set to "blur"; in fact, no update seems to be happening at all anymore.
However, when I set the option on the input
element directly, that works perfectly.
Why doesn't my pass-through work, and how could I implement it properly?
EDIT: the problem only occurs when I try to use ngModelOptions
as the attribute name. If I change it to e.g. options
, it works. This is ok I guess, but I was attempting to make my component behave as much as a native input field as possible. It makes intuitive sense that I shouldn't try to reuse an Angular directive, but in that case the ControlValueAccessor
interface should offer a hook to catch these options, as it does for ngModel
/ngModelChange
.
回答1:
The ngModelOptions
property at the custom component level applies to the ControlValueAccessor without having to bind the inner input element to a custom @Input() ngModelOptions
property, as you can see in this stackblitz.
In order for the ngModel
of the custom component to update correctly:
The inner input element should update on change (the default option), to make sure that the ControlValueAccessor can also update on change.
For the
{updateOn: 'blur'}
option to work, theonTouched
event callback of the ControlValueAccessor should be called when the input element loses focus:
<input ...
[ngModel]="value"
(ngModelChange)="writeValue($event)"
(blur)="onTouchedCallback()" />
where onTouchedCallback
is set in the custom component:
registerOnTouched(fn: any): void {
this.onTouchedCallback = fn;
}
The explanation for the need to call the onTouched
callback is given in a comment by kara in issue 20384 on Github:
The
{updateOn: 'blur'}
functionality on the top level depends on the control value accessor underneath it implementingregisterOnTouched
properly. It looks like your custom component saves the callback, but never actually calls it. Since it's not called, the value doesn't update properly. I'd recommend adding a(blur)="onTouchedCallback()"
to your ControlValueAccessor and it should work.
来源:https://stackoverflow.com/questions/53935844/passing-ngmodeloptions-through-from-a-custom-component-to-a-contained-native-e