After attempting to run a migration for the new input API (with --insert-todos enabled) my custom Text Input component looks like this: text-input.component.ts
export class TextInputComponent implements ControlValueAccessor {
private ngControl = inject(NgControl, { self: true, optional: true });
// TODO: Skipped for migration because:
// Your application code writes to the input. This prevents migration.
@Input() disabled: boolean = false;
...
setDisabledState?(isDisabled: boolean): void {
this.disabled = isDisabled;
}
...
}
text-input.component.html
<input
class="{{ cn( 'rounded focus:outline-primary-400 focus:outline border px-3
bg-background-400 py-1 leading-tight', tw() ) }}"
[type]="type()"
[(ngModel)]="value"
[disabled]="disabled"
(blur)="onTouch()"
/>
I've omitted the code I think migrated successfully (as I was not writing to the other inputs) or unrelated implementations of the interface. I know I don't NEED to migrate to the new API but it seems to me the change implies that I was perhaps using the Input directive incorrectly anyway. In that, if a value needs to be changed with methods other than through its binding should it really be an "Input"?
What might be the best practice to implementing cases like in setDisabledState keeping in mind the new input APIs and the restrictions entailed.
EDIT:
In this case, using a one-way property binding seems to work for me but I am uncertain if this is a good way to do this.
text-input.component.ts
export class TextInputComponent implements ControlValueAccessor {
...
private _disabled = false;
get disabled() {
return this._disabled;
}
set disabled(value: boolean) {
this._disabled = value;
}
...
setDisabledState?(isDisabled: boolean): void {
this._disabled = isDisabled;
}
...
}
As the comments states, the migration would have replaced @Input
with input
signal. But input
signals are read only and we are setting the value of disabled in the function setDisabledState
. So it is unable to migrate it.
The disabled field should not be an input, it should be done programatically using:
this.form.get('customControl').disable();
You can update the code to below, where the disabled
field is not an input
property.
export class TextInputComponent implements ControlValueAccessor {
private ngControl = inject(NgControl, { self: true, optional: true });
disabled: WritableSignal<boolean> = signal(false);
...
setDisabledState?(isDisabled: boolean): void {
this.disabled.set(isDisabled);
}
...
}
<input
class="{{ cn( 'rounded focus:outline-primary-400 focus:outline border px-3
bg-background-400 py-1 leading-tight', tw() ) }}"
[type]="type()"
[(ngModel)]="value"
[disabled]="disabled()"
(blur)="onTouch()"
/>
So the implementation of disabled
was the problem.