Evento ngSubmit
Los formularios HTML tradicionales recargan la página completa cuando se envían, lo cual interrumpe la experiencia del usuario en aplicaciones Angular. El evento ngSubmit soluciona este problema proporcionando una forma nativa de manejar el envío de formularios sin recargas.
Diferencia entre submit y ngSubmit
Angular intercepta automáticamente el evento submit
del formulario y lo convierte en ngSubmit
cuando trabajamos con formularios template-driven. Esta transformación previene el comportamiento por defecto del navegador de recargar la página:
<!-- ❌ Evita usar el evento submit nativo -->
<form (submit)="onSubmit()">
<!-- Puede causar recarga de página -->
</form>
<!-- ✅ Usa ngSubmit en su lugar -->
<form (ngSubmit)="onSubmit()">
<!-- Previene automáticamente la recarga -->
</form>
Configuración básica del evento
Para manejar el envío del formulario, necesitamos combinar el evento ngSubmit
con una variable de plantilla que nos dé acceso al formulario:
<form #contactForm="ngForm" (ngSubmit)="onSubmit(contactForm)">
<div>
<label for="nombre">Nombre:</label>
<input
type="text"
id="nombre"
name="nombre"
[(ngModel)]="usuario.nombre"
required>
</div>
<div>
<label for="email">Email:</label>
<input
type="email"
id="email"
name="email"
[(ngModel)]="usuario.email"
required>
</div>
<button type="submit">Enviar</button>
</form>
La variable #contactForm="ngForm"
nos permite acceder al estado completo del formulario, incluyendo sus valores, validez y métodos de control.
Procesamiento de datos en el componente
En el componente TypeScript, el método que maneja el envío recibe toda la información del formulario:
export class ContactComponent {
usuario = {
nombre: '',
email: ''
};
mensaje = '';
mostrarResultado = false;
onSubmit(form: NgForm) {
if (form.valid) {
// Acceder a los valores del formulario
console.log('Valores del formulario:', form.value);
// Procesar los datos
this.procesarFormulario(form.value);
// Mostrar confirmación al usuario
this.mensaje = `¡Gracias ${form.value.nombre}! Tu mensaje ha sido enviado.`;
this.mostrarResultado = true;
} else {
this.mensaje = 'Por favor, completa todos los campos requeridos.';
this.mostrarResultado = true;
}
}
private procesarFormulario(datos: any) {
// Aquí harías el procesamiento real:
// - Enviar datos a un servicio
// - Hacer petición HTTP
// - Guardar en localStorage
console.log('Procesando datos:', datos);
}
}
Acceso a los valores del formulario
Angular ofrece múltiples formas de acceder a los datos del formulario dentro del método de envío:
onSubmit(form: NgForm) {
// Opción 1: Usar form.value (objeto con todos los valores)
const todosLosValores = form.value;
console.log('Todos los valores:', todosLosValores);
// Opción 2: Acceder a campos específicos
const nombreUsuario = form.value.nombre;
const emailUsuario = form.value.email;
// Opción 3: Usar las propiedades del componente (si usas ngModel)
console.log('Desde el componente:', this.usuario);
// Opción 4: Acceder a controles individuales
const controlNombre = form.controls['nombre'];
console.log('Estado del nombre:', controlNombre.valid);
}
Ejemplo completo con feedback visual
Aquí tienes un ejemplo más completo que incluye retroalimentación visual para el usuario:
<form #registroForm="ngForm" (ngSubmit)="onRegistro(registroForm)">
<div>
<label for="usuario">Usuario:</label>
<input
type="text"
id="usuario"
name="usuario"
[(ngModel)]="datosRegistro.usuario"
required
minlength="3">
</div>
<div>
<label for="password">Contraseña:</label>
<input
type="password"
id="password"
name="password"
[(ngModel)]="datosRegistro.password"
required
minlength="6">
</div>
<button
type="submit"
[disabled]="registroForm.invalid">
@if (procesando) {
Procesando...
} @else {
Registrarse
}
</button>
</form>
@if (mensajeExito) {
<div class="alert alert-success">
{{ mensajeExito }}
</div>
}
@if (mensajeError) {
<div class="alert alert-error">
{{ mensajeError }}
</div>
}
export class RegistroComponent {
datosRegistro = {
usuario: '',
password: ''
};
procesando = false;
mensajeExito = '';
mensajeError = '';
onRegistro(form: NgForm) {
if (form.invalid) {
this.mensajeError = 'Por favor, corrige los errores del formulario.';
return;
}
this.procesando = true;
this.mensajeError = '';
this.mensajeExito = '';
// Simular procesamiento asíncrono
setTimeout(() => {
try {
this.registrarUsuario(form.value);
this.mensajeExito = '¡Registro exitoso! Bienvenido ' + form.value.usuario;
} catch (error) {
this.mensajeError = 'Error en el registro. Inténtalo de nuevo.';
} finally {
this.procesando = false;
}
}, 2000);
}
private registrarUsuario(datos: any) {
// Lógica de registro aquí
console.log('Registrando usuario:', datos);
}
}
Ventajas del evento ngSubmit
El uso de ngSubmit
en lugar del evento submit
nativo proporciona estas ventajas clave:
- Prevención automática de la recarga de página
- Integración completa con el sistema de validación de Angular
- Acceso directo al estado y valores del formulario
- Compatibilidad con todas las funcionalidades de Angular Forms
- Mejor experiencia de usuario al mantener el estado de la aplicación
El evento ngSubmit
se ejecuta únicamente cuando el usuario hace clic en un botón de tipo submit
o presiona Enter en un campo del formulario, garantizando un control preciso sobre cuándo se procesa la información.
Estados de formulario
Los formularios en Angular mantienen un estado interno que nos permite controlar su comportamiento y mostrar información relevante al usuario. Estos estados se actualizan automáticamente cuando el usuario interactúa con los campos del formulario.
Estados básicos del formulario
Angular rastrea varios estados que nos ayudan a entender cómo ha interactuado el usuario con el formulario:
<form #miFormulario="ngForm" (ngSubmit)="onSubmit(miFormulario)">
<div>
<label for="nombre">Nombre:</label>
<input
type="text"
id="nombre"
name="nombre"
[(ngModel)]="usuario.nombre"
required>
</div>
<div>
<label for="email">Email:</label>
<input
type="email"
id="email"
name="email"
[(ngModel)]="usuario.email"
required>
</div>
<!-- Mostrar información de estados -->
<div class="form-debug">
<p>Valid: {{ miFormulario.valid }}</p>
<p>Invalid: {{ miFormulario.invalid }}</p>
<p>Pristine: {{ miFormulario.pristine }}</p>
<p>Dirty: {{ miFormulario.dirty }}</p>
<p>Touched: {{ miFormulario.touched }}</p>
<p>Untouched: {{ miFormulario.untouched }}</p>
</div>
<button type="submit" [disabled]="miFormulario.invalid">
Enviar
</button>
</form>
Estados de validez
Los estados de validez nos indican si el formulario cumple con todas las reglas de validación establecidas:
- valid:
true
cuando todos los campos cumplen sus validaciones - invalid:
true
cuando al menos un campo no cumple sus validaciones
export class FormularioComponent {
usuario = {
nombre: '',
email: ''
};
onSubmit(form: NgForm) {
console.log('¿Es válido el formulario?', form.valid);
console.log('¿Es inválido el formulario?', form.invalid);
if (form.valid) {
console.log('Formulario válido, procesando...');
this.procesarDatos(form.value);
} else {
console.log('Formulario inválido, mostrando errores...');
this.mostrarErrores(form);
}
}
}
Estados de interacción del usuario
Estos estados nos permiten saber cómo ha interactuado el usuario con el formulario:
- pristine:
true
cuando el formulario no ha sido modificado por el usuario - dirty:
true
cuando el usuario ha modificado algún campo - touched:
true
cuando el usuario ha hecho foco y luego lo ha perdido en algún campo - untouched:
true
cuando el usuario nunca ha hecho foco en ningún campo
<form #contactForm="ngForm">
<div>
<input
type="text"
name="mensaje"
[(ngModel)]="mensaje"
required>
@if (contactForm.dirty && contactForm.invalid) {
<div class="alert-warning">
Hay errores en el formulario que necesitas corregir
</div>
}
@if (contactForm.touched && contactForm.invalid) {
<div class="alert-error">
Por favor, revisa los campos marcados en rojo
</div>
}
</div>
</form>
Control del botón de envío
Una práctica común es deshabilitar el botón de envío cuando el formulario no es válido:
<form #pedidoForm="ngForm" (ngSubmit)="realizarPedido(pedidoForm)">
<div>
<label for="producto">Producto:</label>
<input
type="text"
id="producto"
name="producto"
[(ngModel)]="pedido.producto"
required>
</div>
<div>
<label for="cantidad">Cantidad:</label>
<input
type="number"
id="cantidad"
name="cantidad"
[(ngModel)]="pedido.cantidad"
required
min="1">
</div>
<button
type="submit"
[disabled]="pedidoForm.invalid"
[class.btn-disabled]="pedidoForm.invalid">
@if (pedidoForm.invalid) {
Completa todos los campos
} @else {
Realizar pedido
}
</button>
<button
type="button"
(click)="limpiarFormulario(pedidoForm)"
[disabled]="pedidoForm.pristine">
Limpiar formulario
</button>
</form>
Limpieza de formularios
Angular ofrece dos métodos principales para limpiar formularios, cada uno con un comportamiento diferente:
export class FormularioComponent {
usuario = {
nombre: '',
email: '',
telefono: ''
};
// Método que solo limpia los valores
limpiarValores(form: NgForm) {
form.reset();
// Los valores se establecen a vacío
// Los estados pristine/touched NO se restauran
console.log('Después de reset():');
console.log('Pristine:', form.pristine); // Puede seguir siendo false
console.log('Touched:', form.touched); // Puede seguir siendo true
}
// Método que restaura completamente el formulario
restaurarFormulario(form: NgForm) {
form.resetForm();
// Los valores se establecen a vacío
// Los estados pristine/touched SÍ se restauran
console.log('Después de resetForm():');
console.log('Pristine:', form.pristine); // Será true
console.log('Touched:', form.touched); // Será false
}
// También puedes restablecer a valores específicos
restaurarConValores(form: NgForm) {
form.resetForm({
nombre: 'Usuario por defecto',
email: '',
telefono: ''
});
}
}
Diferencias entre reset() y resetForm()
Es importante entender las diferencias clave entre estos métodos:
form.reset():
- Limpia únicamente los valores de los campos
- No restaura los estados de interacción (pristine, dirty, touched, untouched)
- No elimina las clases CSS de validación aplicadas
- Útil cuando solo quieres vaciar los datos pero mantener el estado visual
form.resetForm():
- Limpia los valores de los campos
- Restaura completamente todos los estados del formulario
- Elimina todas las clases CSS de validación
- Devuelve el formulario a su estado inicial como si nunca hubiera sido tocado
<form #demoForm="ngForm">
<input
type="text"
name="campo"
[(ngModel)]="valor"
required>
<div class="form-actions">
<button
type="button"
(click)="limpiarSoloValores(demoForm)">
Solo limpiar valores (reset)
</button>
<button
type="button"
(click)="limpiarTodo(demoForm)">
Limpiar todo (resetForm)
</button>
</div>
<div class="estados-info">
<p>Pristine: {{ demoForm.pristine ? 'Sí' : 'No' }}</p>
<p>Dirty: {{ demoForm.dirty ? 'Sí' : 'No' }}</p>
<p>Touched: {{ demoForm.touched ? 'Sí' : 'No' }}</p>
</div>
</form>
Ejemplo práctico completo
Aquí tienes un ejemplo que combina todos los conceptos de estados de formulario:
export class ContactoComponent {
contacto = {
nombre: '',
email: '',
mensaje: ''
};
enviado = false;
onEnviar(form: NgForm) {
if (form.valid) {
console.log('Enviando contacto:', form.value);
this.enviado = true;
// Después de enviar exitosamente, limpiar completamente
setTimeout(() => {
this.limpiarFormularioCompleto(form);
this.enviado = false;
}, 2000);
}
}
limpiarFormularioCompleto(form: NgForm) {
// Usar resetForm para restaurar completamente el estado
form.resetForm();
// También limpiar las propiedades del componente
this.contacto = {
nombre: '',
email: '',
mensaje: ''
};
}
hayErroresParaMostrar(form: NgForm): boolean {
// Solo mostrar errores si el usuario ha interactuado
return form.invalid && (form.dirty || form.touched);
}
}
Los estados de formulario son esenciales para crear una experiencia de usuario fluida, permitiendo mostrar mensajes de error en el momento adecuado y controlar cuándo los usuarios pueden enviar o limpiar sus formularios.
Fuentes y referencias
Documentación oficial y recursos externos para profundizar en Angular
Documentación oficial de Angular
Alan Sastre
Ingeniero de Software y formador, CEO en CertiDevs
Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, Angular es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.
Más tutoriales de Angular
Explora más contenido relacionado con Angular y continúa aprendiendo con nuestros tutoriales gratuitos.
Aprendizajes de esta lección
- Comprender la diferencia entre el evento nativo submit y el evento ngSubmit en Angular.
- Aprender a manejar el envío de formularios con ngSubmit y acceder a sus valores y estados.
- Conocer los estados internos de un formulario Angular (valid, invalid, pristine, dirty, touched, untouched) y su utilidad.
- Saber cómo controlar la habilitación del botón de envío según la validez del formulario.
- Diferenciar y aplicar correctamente los métodos reset() y resetForm() para limpiar formularios.