Saltar al contenido

Angular ngModelChange llega tarde al actualizar NgModel

Puede darse el caso de que halles alguna incompatibilidad en tu código o proyecto, recuerda probar siempre en un entorno de testing antes subir el código al trabajo final.

Solución:

TLDR

StackBlitz.

mi-directiva.directiva.ts

/* ... */

ngOnInit () 
  const initialOnChange = (this.ngControl.valueAccessor as any).onChange;

  (this.ngControl.valueAccessor as any).onChange = (value) => initialOnChange(this.processInput(value));


/* ... */

@HostListener('ngModelChange', ['$event'])
ngModelChange(value: any) 
  this.ngControl.valueAccessor.writeValue(this.processInput(value));


Respuesta detallada

Veamos por qué no funcionó inicialmente.

Angular tiene descriptores de acceso de valor predeterminado para ciertos elementos, como por ejemplo para input type='text', input type='checkbox' etc…

A ControlValueAccessor es el intermediario entre la capa VISTA y la capa MODELO. Cuando un usuario escribe en una entrada, VIEW notifica al ControlValueAccessorque tiene el trabajo de informar al MODELO.

Por ejemplo, cuando el input ocurre el evento, el onChange metodo de la ControlValueAccessor sera llamado. Así es cómo onChange parece para cadaControlValueAccessor:

function setUpViewChangePipeline(control: FormControl, dir: NgControl): void 
  dir.valueAccessor!.registerOnChange((newValue: any) => 
    control._pendingValue = newValue;
    control._pendingChange = true;
    control._pendingDirty = true;

    if (control.updateOn === 'change') updateControl(control, dir);
  );

La magia sucede en updateControl:

function updateControl(control: FormControl, dir: NgControl): void 
  if (control._pendingDirty) control.markAsDirty();
  control.setValue(control._pendingValue, emitModelToViewChange: false);
 
  // !
  dir.viewToModelUpdate(control._pendingValue);
  control._pendingChange = false;

dir.viewToModelUpdate(control._pendingValue); es lo que invoca el ngModelChange evento en la directiva personalizada. Lo que esto significa es que el valor del modelo es el valor de la entrada (en minúsculas). Y porqué ControlValueAccessor.writeValuesolamente escribe el valor en la VISTA, habrá un demora entre el valor de VISTA y el valor de MODELO.

Vale la pena mencionar que FormControl.setValue(val) escribirá val a ambas cosas capas, VISTA y MODELO, pero si usáramos esto, habría un Bucle infinitoya que setValue() llamadas internas viewToModelUpdate(porque hay que actualizar el MODELO), y viewToModelUpdate llamadas setValue().

Veamos una posible solución:

ngOnInit () 
  const initialOnChange = (this.ngControl.valueAccessor as any).onChange;

  (this.ngControl.valueAccessor as any).onChange = (value) => initialOnChange(this.processInput(value));

Con este enfoque, está modificando sus datos en la capa VISTA, antes de que se envíen a la ControlValueAccessor.

Y podemos estar seguros de que onChange existe en cada incorporado ControlValueAccessor.

Resultados de la búsqueda

Si va a crear uno personalizado, solo asegúrese de que tenga un onChange propiedad. TypeScript puede ayudarte con eso.

Si desea leer más acerca de las partes internas de @angular/formsrecomendaría echar un vistazo a Una exploración exhaustiva de las formas angulares.

puedes conseguirlo usando

  @HostListener('input', ['$event'])
  ngModelChange(event: any) 
    const item = event.target
    const value = item.value;
    const pos = item.selectionStart;
    this.control.control.setValue(this.processInput(value),  emit: false );
    item.selectionStart = item.selectionEnd = pos
  

Vea que usamos la entrada @HostListener para obtener el elemento, no solo el valor. Esto nos permite posicionar el cursor en su posición después de cambiar el valor

NOTA: Para hacer una mayúscula simple, es mejor usar css text-transform:uppercase y, cuando queramos obtener el valor, use toUpperCase()

NOTA 2: sobre la máscara, consulte este SO

Reseñas y puntuaciones del post

Si te animas, puedes dejar un post acerca de qué le añadirías a esta crónica.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)


Tags : /

Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *