Formularios en Angluar

Cuando un componente incluye campos de formulario, WCF implementa la API ControlValueAccesor de Angular que permite incluir el componente completo como un campo más de un FormGroup.

ControlValueAccessor

ControlAvalueAccesor es una API estándar de Angular, que actúa como un puente entre la API de formularios de Angular y los elementos nativos que el DOM crea para los formularios. Es la API recomendada para permitir incluir un componente Angular en un formulario como si fuera un campo más, y es la que utiliza WCF para conseguir este objetivo, haciendo mucho más fácil la creación y manejo de formularios que contienen componentes.

Analicemos el siguiente formulario generado por Web Component Factory, con una captura tomada de la app-demo:

Formulario ejemplo
Formulario ejemplo

El código que incluye la app-demo, que abarca el formulario completo como un FormControl es el siguiente:

typescript:
formAppDemo = new FormGroup({
	formEjemploForm: new FormControl({
		inputTextCampo: "",
		inputCheckboxUno: false,
		inputCheckboxDos: false,
		selectDelDos: "",
		inputRadioButton: ""
	})
});

HTML en el template:
<div [formGroup]="formAppDemo">
	<comp-form-ejemplo formControlName="formEjemploForm"></comp-form-ejemplo>
</div>

La magia de Angular está lista: todos los campos están vinculados (binded) y las variables se van a actualizar con las interacciones. Por ejemplo, la propia app-demo despliega los valores incluyendo en la plantilla una sola línea de código:

<pre>{{ formAppDemo.controls.formEjemploForm.value | json }}</pre>

Leyendo y asignando valores al formulario y sus campos

Desde el componente que implementa el formulario, leer los valores es muy sencillo, dado que se comporta como cualquier formulario estándar de Angular. En el ejemplo el componente que implementa el formulario es app-demo y el FormGroup se llama formAppDemo, por lo que podemos acceder a sus valores de la siguiente forma:

Typescript:
console.log(this.formAppDemo.controls.formAnidadoForm.value);

Salida en la consola:
▶ Object { inputTextCampo: "Diego Armando", inputCheckboxUno: 
true,  inputCheckboxDos: false, selectDelDos: "2", inputRadioButton:
 "3"}

Asignando valores

Como en cualquier formulario, los valores del FormControl se pueden asignar con la granularidad que se desee. En definitiva es un objeto, y solo hay que generar el json acorde con la estructura.

Todas las asignaciones que se muestran funcionan sin problemas. Para asignar valores a los campos del formulario se utiliza el método estándar setValue. En nuestro ejemplo de la app-demo:

this.formAppDemo.controls.formEjemploForm.setValue( 
	{
		inputTextCampo: "Zinedine",
		inputCheckboxUno: false,
		inputCheckboxDos: true,
		selectDelDos: "3",
		inputRadioButton: "1"
	}
);

Cambia los valores, tal como era de esperar.

Formulario ejemplo después de asignar los valores
Formulario ejemplo después de asignar los valores

Asignando solo algunos valores

Como es razonable esperar, se pueden asignar solamente algunos campos del formulario, pero dado el carácter fuertemente tipeado de Typescript y la característica asincrónica de JavaScritp, es necesario tener algunas precauciones, porque de lo contrario pueden borrarse el resto de los valores.

El código recomendado, siempre para nuestro ejemplo, es el siguiente:

const currentValue = this.formAppDemo.controls.formEjemploForm.value;
queueMicrotask(() => {  	
	this.formAppDemo.controls.formEjemploForm.patchValue( 
		{
			...currentValue,
			inputCheckboxUno: false,
			inputRadioButton: "1"
		} as any
	);
});

Los detalles importantes:

  • currentValue: permite preservar completo el valor de la variable que contiene el formulario. Si ella, el patchValue funciona correctamente pero la variable pierde las propiedades que no fueron actualizadas.
    El motor de Angular las recupera automáticamente en la próxima operación sobre el formulario, pero es un comportamiento molesto, que es fácil evitar y vale la pena hacerlo.
  • queueMicrotask: es una función estándar de JavaScript que encola la función invocada al final de la cola de ejecución. El funcionamiento sigue siendo asincrónico, pero evita que funciones muy cortas terminen antes que funciones muy largas.
    En este caso, si el formulario es muy grande, la inicialización del formulario terminará antes que el patchValue, obteniendo el resultado no deseado de un formulario que tiene las propiedades de la segunda función asignadas y el resto vacías.
  • as any: como estamos asignado parcialmente, y a pesar del currentValue que se describe más arriba, los tipos no son exactamente iguales y obtenemos un error al compilar. Agregar as any al finalizar el objeto elimina este error.

Binding de eventos

Un formulario Angular no tiene valor alguno si no se puede vincular (bind) el manejo de eventos para reaccionar adecuadamente.

Esto se puede hacer agregando los tradicionales paréntesis curvos en el template html del formulario. Sin embargo, la recomendación para los reactiveForms de Angular es hacerlo con la directiva host: en el decorador del componente. Esto brinda control absoluto del comportamiento sin necesidad de alterar el HTML cada vez.

Siempre utilizando el ejemplo, asociemos los eventos click y focusout al formulario. Para ello es necesario en el decorador agregar el atributo hosts, y en el código que se exporta, agregar las funciones asociadas.

El decorador del componente
@Component({
	selector: "app-demo",
	standalone: true,
	imports: [FormEjemplo, ReactiveFormsModule, JsonPipe],
	templateUrl: "app-demo.component.html",
	host: {
		"(click)": "onClick($event)",
		"(focusout)": "onFocusout($event)"
	}
})

En el código exportable
onClick(event: MouseEvent) {
	console.log("click event", event);
}

onFocusout(event: FocusEvent) {
	console.log("focus event", event);
}

Utilizando el atributo hosts: se puede asociar (bind) cualquier evento asociable de Angular.

Un detalle no menor es que al asociarlo en el componente padre, la función asociada será invocada para todos los eventos del componente y no solo para los del formulario. Esto se puede resolver fácilmente extendiendo el componente del formulario para agregarle las funciones asociadas.


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

9 + 6 =