Submit y limpieza de formularios

Intermedio
Angular
Angular
Actualizado: 24/09/2025

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 - Autor del tutorial

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.