Estoy usando un método SET para establecer mi valor límite en 'DEFAULT' si alguna vez intentamos establecerlo en una cadena vacía. La entrada de texto enlazado se comporta correctamente en todos los escenarios, EXCEPTO si el campo de texto ya dice 'PREDETERMINADO' y yo "selecciono todo + elimino" ese valor. El valor límite se vuelve a establecer correctamente en 'DEFAULT' inmediatamente, pero el campo de entrada permanece vacío.
Aquí hay un stackblitz que muestra el problema https://stackblitz.com/edit/angular-a9j8tv?file=src%2Fapp%2Fapp.component.html
Si tiene 'DEFAULT' y selecciona todo y elimina, puede ver que la entrada permanece vacía, pero el intervalo que está vinculado al mismo valor muestra correctamente el valor 'DEFAULT' recién restablecido.
¿Por qué pasó esto? ¿Hay un trabajo alrededor?
El problema proviene de ngModel
. Que desde el ''
valor manipulando a DEFAULT
, que es el último valor que ha ngModel
, ngModel
no se da cuenta del cambio de valor para activar un nuevo evento de cambio de valor.
Cómo solucionarlo;
He asignado el valor cambiado directamente a la variable en el setter
antes de la comparación. Luego, establezca un tiempo de espera mínimo para que ngModel
pueda actualizar su valor existente. Luego, cuando comparo el nuevo valor y lo vuelvo a cambiar a DEFAULT
en el caso de ''
, ngModel
se da cuenta del cambio de valor y activa el nuevo evento de cambio de valor. Esto actualiza ambas representaciones.
La directiva ngModel
usa @Input
y @Output
para obtener el valor y emitir el evento de cambio. Por lo tanto, vincular una función como setter o getter a una directiva como [ngModel]
con la estrategia de detección de cambios predeterminada activaría la función para cada detección de cambios.
Para replicar este error, conecte un console.log
en el getter, escriba algún valor y siga presionando la tecla de retroceso en <input>
y suéltelo. Podría ver que los mensajes de console.log
se siguen imprimiendo.
Error de replicación: Stackblitz
Como se dijo antes, la directiva ngModel
usa @Input
y, en consecuencia, el gancho ngOnChanges()
para obtener cualquier cambio en el valor de la etiqueta <input>
. Una cosa importante a tener en cuenta es que ngOnChanges()
no se activará si el valor no ha cambiado (1) .
Podríamos intentar dividir el enlace bidireccional [(ngModel)]
a la entrada [ngModel]
y la salida (ngModelChange)
.
export class AppComponent { public testString: string; onChange(value: string) { this.testString = value === '' ? 'DEFAULT' : value; } }
<input type="text" [ngModel]="testString" (ngModelChange)="onChange($event)" />
Todavía propenso a errores: Stackblitz
Aquí, Ctrl + a + Retroceso cualquier cadena que no sea 'DEFAULT' insertaría 'DEFAULT' como se esperaba. Pero Ctrl + a + Retroceso 'DEFAULT' daría como resultado un cuadro <input>
vacío debido al ngOnChanges()
discutido anteriormente.
Una forma de lograr el comportamiento esperado es no depender del ngModel
ngOnChanges()
de la directiva ngModel para establecer el valor, sino hacerlo nosotros mismos manualmente. Para eso, podríamos usar una variable de referencia de plantilla Angular en <input>
y enviarla al componente para su posterior manipulación.
export class AppComponent { public testString: string; public onChange(value: string, inputElem: HTMLInputElement) { this.testString = value === '' ? 'DEFAULT' : value; inputElem.value = this.testString; } }
<input type="text" #myInput ngModel (ngModelChange)="onChange($event, myInput)" />
Ejemplo de trabajo: Stackblitz
Aquí #myInput
es la variable de referencia de la plantilla. Si esto se siente extraño, debe saber que usar ngModel
para manipular <input>
se denomina en sí mismo formularios basados en plantillas . Y es bastante legal manipular la referencia de la plantilla directamente.
Dicho todo esto, en mi opinión, en los casos en que se necesite tal manipulación de entrada, le recomiendo que use los formularios reactivos angulares en su lugar. Proporciona un control más granular sobre la entrada.
(1) ngOnChanges()
no se activará si el valor previousValue
y el valor @Input
del objeto SimpleChange
de la variable currentValue
respectiva no han cambiado.