Event Binding

Avanzado
Angular
Angular
Actualizado: 24/09/2025

Eventos con paréntesis ()

El event binding en Angular nos permite responder a eventos del DOM de forma reactiva, capturando las interacciones del usuario y ejecutando lógica en nuestros componentes. La sintaxis utiliza paréntesis para envolver el nombre del evento, creando un enlace directo entre los eventos del navegador y los métodos de nuestro componente.

Sintaxis básica del event binding

La sintaxis fundamental del event binding sigue el patrón (evento)="método()", donde el evento va entre paréntesis y se asigna a una expresión TypeScript:

// contador.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-contador',
  template: `
    <div>
      <p>Contador: {{ valor }}</p>
      <button (click)="incrementar()">Incrementar</button>
      <button (click)="decrementar()">Decrementar</button>
      <button (click)="resetear()">Reset</button>
    </div>
  `
})
export class ContadorComponent {
  valor = 0;

  incrementar() {
    this.valor++;
  }

  decrementar() {
    this.valor--;
  }

  resetear() {
    this.valor = 0;
  }
}

Eventos DOM más comunes

Angular puede capturar cualquier evento DOM utilizando la misma sintaxis de paréntesis. Los eventos más utilizados incluyen:

Eventos de mouse:

@Component({
  selector: 'app-eventos-mouse',
  template: `
    <div class="zona-eventos">
      <button (click)="alHacerClick()">Click</button>
      <button (dblclick)="alDobleClick()">Doble Click</button>
      <div (mouseover)="alEntrarMouse()" 
           (mouseout)="alSalirMouse()"
           class="area-hover">
        Pasa el mouse aquí
      </div>
    </div>
    <p>{{ mensaje }}</p>
  `
})
export class EventosMouseComponent {
  mensaje = 'Esperando interacción...';

  alHacerClick() {
    this.mensaje = 'Botón clickeado';
  }

  alDobleClick() {
    this.mensaje = 'Doble click detectado';
  }

  alEntrarMouse() {
    this.mensaje = 'Mouse sobre el área';
  }

  alSalirMouse() {
    this.mensaje = 'Mouse fuera del área';
  }
}

Eventos de teclado:

@Component({
  selector: 'app-eventos-teclado',
  template: `
    <div>
      <input type="text" 
             (keyup)="alTeclaUp()" 
             (keydown)="alTeclaDown()"
             placeholder="Escribe algo...">
      <p>Última acción: {{ ultimaAccion }}</p>
      <p>Contenido: {{ contenido }}</p>
    </div>
  `
})
export class EventosTecladoComponent {
  ultimaAccion = '';
  contenido = '';

  alTeclaUp() {
    this.ultimaAccion = 'Tecla liberada';
  }

  alTeclaDown() {
    this.ultimaAccion = 'Tecla presionada';
  }
}

Acceso al objeto $event

Angular proporciona acceso al objeto de evento nativo a través de la variable especial $event, que contiene toda la información del evento DOM:

@Component({
  selector: 'app-evento-objeto',
  template: `
    <div>
      <input type="text" 
             (input)="alCambiarTexto($event)"
             placeholder="Escribe aquí...">
      
      <button (click)="mostrarDetalles($event)">
        Ver detalles del click
      </button>
      
      <p>Texto actual: {{ textoActual }}</p>
      <p>Información del evento: {{ infoEvento }}</p>
    </div>
  `
})
export class EventoObjetoComponent {
  textoActual = '';
  infoEvento = '';

  alCambiarTexto(evento: Event) {
    const input = evento.target as HTMLInputElement;
    this.textoActual = input.value;
  }

  mostrarDetalles(evento: MouseEvent) {
    this.infoEvento = `Click en X: ${evento.clientX}, Y: ${evento.clientY}`;
  }
}

Pasar parámetros personalizados

Además del objeto $event, podemos pasar parámetros personalizados a nuestros métodos de manejo de eventos:

@Component({
  selector: 'app-parametros-eventos',
  template: `
    <div>
      <h3>Colores disponibles</h3>
      <button (click)="cambiarColor('rojo')" class="btn-rojo">Rojo</button>
      <button (click)="cambiarColor('azul')" class="btn-azul">Azul</button>
      <button (click)="cambiarColor('verde')" class="btn-verde">Verde</button>
      
      <div class="resultado" [style.background-color]="colorSeleccionado">
        Color actual: {{ colorSeleccionado }}
      </div>

      <h3>Lista de tareas</h3>
      @for (tarea of tareas; track tarea.id) {
        <div class="tarea">
          <span>{{ tarea.nombre }}</span>
          <button (click)="eliminarTarea(tarea.id)">Eliminar</button>
        </div>
      }
    </div>
  `
})
export class ParametrosEventosComponent {
  colorSeleccionado = 'transparent';
  tareas = [
    { id: 1, nombre: 'Comprar comida' },
    { id: 2, nombre: 'Estudiar Angular' },
    { id: 3, nombre: 'Hacer ejercicio' }
  ];

  cambiarColor(color: string) {
    this.colorSeleccionado = color;
  }

  eliminarTarea(id: number) {
    this.tareas = this.tareas.filter(tarea => tarea.id !== id);
  }
}

Combinando $event con parámetros

Es posible combinar el objeto $event con parámetros personalizados para crear manejadores de eventos más sofisticados:

@Component({
  selector: 'app-evento-mixto',
  template: `
    <div>
      <h3>Formulario de usuario</h3>
      <input type="text" 
             (blur)="validarCampo('nombre', $event)"
             placeholder="Nombre">
      
      <input type="email" 
             (blur)="validarCampo('email', $event)"
             placeholder="Email">
      
      <input type="number" 
             (input)="actualizarRango('edad', $event)"
             min="1" max="100" 
             placeholder="Edad">

      @if (errores.length > 0) {
        <div class="errores">
          @for (error of errores; track error) {
            <p class="error">{{ error }}</p>
          }
        </div>
      }
      
      <p>Edad seleccionada: {{ edadActual }}</p>
    </div>
  `
})
export class EventoMixtoComponent {
  errores: string[] = [];
  edadActual = 0;

  validarCampo(campo: string, evento: Event) {
    const input = evento.target as HTMLInputElement;
    const valor = input.value.trim();
    
    // Limpiar errores previos de este campo
    this.errores = this.errores.filter(error => !error.includes(campo));
    
    if (!valor) {
      this.errores.push(`El campo ${campo} es obligatorio`);
    } else if (campo === 'email' && !valor.includes('@')) {
      this.errores.push('El email debe tener un formato válido');
    }
  }

  actualizarRango(campo: string, evento: Event) {
    const input = evento.target as HTMLInputElement;
    if (campo === 'edad') {
      this.edadActual = parseInt(input.value) || 0;
    }
  }
}

Eventos en elementos anidados

Los eventos se pueden aplicar a cualquier elemento HTML, no solo a botones e inputs. Esto permite crear interfaces interactivas complejas:

@Component({
  selector: 'app-elementos-anidados',
  template: `
    <div class="galeria">
      <h3>Galería de imágenes</h3>
      @for (imagen of imagenes; track imagen.id) {
        <div class="imagen-container" 
             (click)="seleccionarImagen(imagen)">
          <img [src]="imagen.url" [alt]="imagen.nombre">
          <p>{{ imagen.nombre }}</p>
          <button (click)="marcarFavorita(imagen.id, $event)"
                  class="btn-favorito">
            {{ imagen.favorita ? '❤️' : '🤍' }}
          </button>
        </div>
      }
      
      @if (imagenSeleccionada) {
        <div class="detalle">
          <h4>Imagen seleccionada: {{ imagenSeleccionada.nombre }}</h4>
          <p>Descripción: {{ imagenSeleccionada.descripcion }}</p>
        </div>
      }
    </div>
  `
})
export class ElementosAnidadosComponent {
  imagenSeleccionada: any = null;
  imagenes = [
    { 
      id: 1, 
      nombre: 'Paisaje', 
      url: '/assets/paisaje.jpg',
      descripcion: 'Hermoso paisaje montañoso',
      favorita: false 
    },
    { 
      id: 2, 
      nombre: 'Ciudad', 
      url: '/assets/ciudad.jpg',
      descripcion: 'Vista nocturna de la ciudad',
      favorita: true 
    }
  ];

  seleccionarImagen(imagen: any) {
    this.imagenSeleccionada = imagen;
  }

  marcarFavorita(id: number, evento: Event) {
    // Prevenir que el click se propague al contenedor padre
    evento.stopPropagation();
    
    const imagen = this.imagenes.find(img => img.id === id);
    if (imagen) {
      imagen.favorita = !imagen.favorita;
    }
  }
}

La sintaxis de paréntesis para event binding proporciona una forma intuitiva y declarativa de manejar la interactividad en nuestras aplicaciones Angular, permitiendo crear experiencias de usuario ricas y responsivas con un código limpio y mantenible.

Manejo de eventos DOM

El manejo avanzado de eventos DOM en Angular va más allá de la simple captura de eventos, incluyendo el control de la propagación, la prevención de comportamientos predeterminados y la manipulación eficiente del DOM. Angular proporciona herramientas específicas para gestionar estos aspectos de forma elegante y performante.

Prevención del comportamiento predeterminado

Muchos elementos HTML tienen comportamientos predeterminados que pueden interferir con la lógica de nuestra aplicación. Angular permite prevenir estos comportamientos usando preventDefault() en el objeto evento:

@Component({
  selector: 'app-prevencion-default',
  template: `
    <div>
      <h3>Formulario personalizado</h3>
      <form (submit)="procesarFormulario($event)">
        <input type="text" [(ngModel)]="usuario" placeholder="Usuario" required>
        <input type="password" [(ngModel)]="password" placeholder="Contraseña" required>
        <button type="submit">Iniciar Sesión</button>
      </form>
      
      <p>Estado: {{ estadoFormulario }}</p>
      
      <h3>Enlaces personalizados</h3>
      <a href="https://ejemplo.com" 
         (click)="manejarEnlace($event)">
        Enlace interceptado
      </a>
      
      <div class="menu-contextual" 
           (contextmenu)="mostrarMenuContextual($event)">
        Click derecho aquí
      </div>
      
      @if (menuVisible) {
        <div class="menu" [style.left.px]="menuX" [style.top.px]="menuY">
          <button (click)="accionMenu('opcion1')">Opción 1</button>
          <button (click)="accionMenu('opcion2')">Opción 2</button>
        </div>
      }
    </div>
  `
})
export class PrevencionDefaultComponent {
  usuario = '';
  password = '';
  estadoFormulario = 'Esperando datos...';
  menuVisible = false;
  menuX = 0;
  menuY = 0;

  procesarFormulario(evento: Event) {
    // Prevenir el envío automático del formulario
    evento.preventDefault();
    
    if (this.usuario && this.password) {
      this.estadoFormulario = 'Procesando login...';
      // Aquí iría la lógica de autenticación
      setTimeout(() => {
        this.estadoFormulario = 'Login exitoso';
      }, 1000);
    } else {
      this.estadoFormulario = 'Faltan datos obligatorios';
    }
  }

  manejarEnlace(evento: Event) {
    // Prevenir la navegación del enlace
    evento.preventDefault();
    console.log('Enlace interceptado, navegación personalizada');
    // Aquí podríamos implementar navegación con Angular Router
  }

  mostrarMenuContextual(evento: MouseEvent) {
    // Prevenir el menú contextual del navegador
    evento.preventDefault();
    this.menuX = evento.clientX;
    this.menuY = evento.clientY;
    this.menuVisible = true;
  }

  accionMenu(opcion: string) {
    console.log(`Ejecutando ${opcion}`);
    this.menuVisible = false;
  }
}

Control de la propagación de eventos

El event bubbling es el proceso por el cual los eventos se propagan desde el elemento hijo hacia sus elementos padre. Angular permite controlar esta propagación usando stopPropagation():

@Component({
  selector: 'app-propagacion-eventos',
  template: `
    <div class="contenedor-padre" 
         (click)="clickPadre()" 
         style="padding: 20px; background: lightblue;">
      <p>Contenedor Padre</p>
      
      <div class="contenedor-hijo" 
           (click)="clickHijo($event)" 
           style="padding: 15px; background: lightgreen;">
        <p>Contenedor Hijo</p>
        
        <button (click)="clickBotonNormal($event)">
          Botón Normal
        </button>
        
        <button (click)="clickBotonSinPropagacion($event)">
          Botón Sin Propagación
        </button>
      </div>
      
      <div class="log">
        <h4>Log de eventos:</h4>
        @for (evento of logEventos; track $index) {
          <p>{{ evento }}</p>
        }
        <button (click)="limpiarLog($event)">Limpiar Log</button>
      </div>
    </div>
  `
})
export class PropagacionEventosComponent {
  logEventos: string[] = [];

  clickPadre() {
    this.agregarLog('Click en contenedor PADRE');
  }

  clickHijo(evento: Event) {
    this.agregarLog('Click en contenedor HIJO');
    // Si descomentamos la siguiente línea, evitamos que el evento llegue al padre
    // evento.stopPropagation();
  }

  clickBotonNormal(evento: Event) {
    this.agregarLog('Click en BOTÓN NORMAL - se propaga');
  }

  clickBotonSinPropagacion(evento: Event) {
    // Detenemos la propagación del evento
    evento.stopPropagation();
    this.agregarLog('Click en BOTÓN SIN PROPAGACIÓN - no se propaga');
  }

  limpiarLog(evento: Event) {
    evento.stopPropagation();
    this.logEventos = [];
  }

  private agregarLog(mensaje: string) {
    this.logEventos.unshift(`${new Date().toLocaleTimeString()}: ${mensaje}`);
    if (this.logEventos.length > 10) {
      this.logEventos.pop();
    }
  }
}

Eventos de teclado avanzados

Angular facilita el manejo específico de teclas mediante pseudo-eventos que simplifican la captura de combinaciones de teclas:

@Component({
  selector: 'app-eventos-teclado-avanzados',
  template: `
    <div class="editor">
      <h3>Editor con atajos de teclado</h3>
      <textarea 
        (keydown.enter)="nuevaLinea($event)"
        (keydown.ctrl.s)="guardar($event)"
        (keydown.ctrl.z)="deshacer($event)"
        (keydown.escape)="cancelar($event)"
        (keydown.tab)="sangria($event)"
        [(ngModel)]="contenido"
        placeholder="Escribe aquí... (Ctrl+S para guardar, Ctrl+Z para deshacer, Tab para sangría)"
        rows="10" cols="50">
      </textarea>
      
      <div class="estado">
        <p>Estado: {{ estadoEditor }}</p>
        <p>Líneas: {{ contarLineas() }}</p>
        <p>Caracteres: {{ contenido.length }}</p>
      </div>

      <div class="historial">
        <h4>Historial de acciones:</h4>
        @for (accion of historialAcciones; track $index) {
          <p>{{ accion }}</p>
        }
      </div>
    </div>
  `
})
export class EventosTecladoAvanzadosComponent {
  contenido = '';
  estadoEditor = 'Listo';
  historialAcciones: string[] = [];
  historialContenido: string[] = [];

  nuevaLinea(evento: KeyboardEvent) {
    // Solo agregar nueva línea si no hay modificadores
    if (!evento.shiftKey) {
      this.agregarAccion('Nueva línea agregada');
    }
  }

  guardar(evento: KeyboardEvent) {
    evento.preventDefault(); // Prevenir el comportamiento del navegador
    this.estadoEditor = 'Guardando...';
    this.agregarAccion('Documento guardado');
    
    // Simular guardado
    setTimeout(() => {
      this.estadoEditor = 'Guardado exitosamente';
      setTimeout(() => this.estadoEditor = 'Listo', 2000);
    }, 500);
  }

  deshacer(evento: KeyboardEvent) {
    evento.preventDefault();
    if (this.historialContenido.length > 0) {
      this.contenido = this.historialContenido.pop() || '';
      this.agregarAccion('Acción deshecha');
    } else {
      this.agregarAccion('No hay acciones para deshacer');
    }
  }

  cancelar(evento: KeyboardEvent) {
    this.estadoEditor = 'Operación cancelada';
    this.agregarAccion('Escape presionado - operación cancelada');
  }

  sangria(evento: KeyboardEvent) {
    evento.preventDefault();
    const textarea = evento.target as HTMLTextAreaElement;
    const inicio = textarea.selectionStart;
    const fin = textarea.selectionEnd;
    
    // Guardar estado para deshacer
    this.historialContenido.push(this.contenido);
    
    // Agregar sangría
    this.contenido = this.contenido.substring(0, inicio) + 
                    '    ' + 
                    this.contenido.substring(fin);
    
    this.agregarAccion('Sangría aplicada');
    
    // Restaurar posición del cursor
    setTimeout(() => {
      textarea.selectionStart = textarea.selectionEnd = inicio + 4;
    });
  }

  contarLineas(): number {
    return this.contenido.split('\n').length;
  }

  private agregarAccion(accion: string) {
    this.historialAcciones.unshift(`${new Date().toLocaleTimeString()}: ${accion}`);
    if (this.historialAcciones.length > 5) {
      this.historialAcciones.pop();
    }
  }
}

Eventos de formulario especializados

Los eventos de formulario requieren un manejo cuidadoso para crear experiencias de usuario fluidas:

@Component({
  selector: 'app-eventos-formulario',
  template: `
    <form class="formulario-avanzado" (submit)="enviarFormulario($event)">
      <h3>Formulario con validación en tiempo real</h3>
      
      <div class="campo">
        <label>Email:</label>
        <input type="email" 
               (input)="validarEmail($event)"
               (focus)="campoEnFoco('email')"
               (blur)="campoFueraDeFoco('email', $event)"
               [class.valido]="validacionEmail.valido"
               [class.invalido]="validacionEmail.invalido && validacionEmail.tocado">
        @if (validacionEmail.invalido && validacionEmail.tocado) {
          <span class="error">{{ validacionEmail.mensaje }}</span>
        }
      </div>

      <div class="campo">
        <label>Teléfono:</label>
        <input type="tel" 
               (input)="formatearTelefono($event)"
               (paste)="manejarPegado($event)"
               placeholder="(xxx) xxx-xxxx">
      </div>

      <div class="campo">
        <label>Archivo:</label>
        <input type="file" 
               (change)="manejarArchivo($event)"
               accept=".pdf,.doc,.docx">
        @if (archivoSeleccionado) {
          <p>Archivo: {{ archivoSeleccionado.name }} 
             ({{ formatearTamaño(archivoSeleccionado.size) }})</p>
        }
      </div>

      <div class="campo">
        <label>Comentarios:</label>
        <textarea 
          (input)="contarCaracteres($event)"
          maxlength="500"
          rows="4">
        </textarea>
        <small>{{ caracteresRestantes }}/500 caracteres restantes</small>
      </div>

      <button type="submit" [disabled]="!formularioValido()">
        Enviar Formulario
      </button>
      
      <div class="estado-formulario">
        <p>Campo activo: {{ campoActivo }}</p>
        <p>Estado: {{ estadoFormulario }}</p>
      </div>
    </form>
  `
})
export class EventosFormularioComponent {
  validacionEmail = { valido: false, invalido: false, tocado: false, mensaje: '' };
  archivoSeleccionado: File | null = null;
  caracteresRestantes = 500;
  campoActivo = 'ninguno';
  estadoFormulario = 'Pendiente';

  validarEmail(evento: Event) {
    const input = evento.target as HTMLInputElement;
    const email = input.value;
    const regexEmail = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    
    this.validacionEmail.valido = regexEmail.test(email);
    this.validacionEmail.invalido = !this.validacionEmail.valido && email.length > 0;
    this.validacionEmail.mensaje = this.validacionEmail.invalido ? 
      'Por favor ingresa un email válido' : '';
  }

  campoEnFoco(campo: string) {
    this.campoActivo = campo;
  }

  campoFueraDeFoco(campo: string, evento: Event) {
    if (campo === 'email') {
      this.validacionEmail.tocado = true;
    }
    this.campoActivo = 'ninguno';
  }

  formatearTelefono(evento: Event) {
    const input = evento.target as HTMLInputElement;
    let valor = input.value.replace(/\D/g, ''); // Solo números
    
    if (valor.length >= 6) {
      valor = `(${valor.slice(0, 3)}) ${valor.slice(3, 6)}-${valor.slice(6, 10)}`;
    } else if (valor.length >= 3) {
      valor = `(${valor.slice(0, 3)}) ${valor.slice(3)}`;
    }
    
    input.value = valor;
  }

  manejarPegado(evento: ClipboardEvent) {
    evento.preventDefault();
    const datosPegados = evento.clipboardData?.getData('text') || '';
    const soloNumeros = datosPegados.replace(/\D/g, '');
    
    if (soloNumeros.length === 10) {
      const input = evento.target as HTMLInputElement;
      input.value = `(${soloNumeros.slice(0, 3)}) ${soloNumeros.slice(3, 6)}-${soloNumeros.slice(6)}`;
    }
  }

  manejarArchivo(evento: Event) {
    const input = evento.target as HTMLInputElement;
    const archivo = input.files?.[0];
    
    if (archivo) {
      // Validar tamaño (máximo 5MB)
      if (archivo.size > 5 * 1024 * 1024) {
        alert('El archivo es demasiado grande. Máximo 5MB.');
        input.value = '';
        return;
      }
      
      this.archivoSeleccionado = archivo;
    } else {
      this.archivoSeleccionado = null;
    }
  }

  contarCaracteres(evento: Event) {
    const textarea = evento.target as HTMLTextAreaElement;
    this.caracteresRestantes = 500 - textarea.value.length;
  }

  enviarFormulario(evento: Event) {
    evento.preventDefault();
    
    if (this.formularioValido()) {
      this.estadoFormulario = 'Enviando...';
      // Simular envío
      setTimeout(() => {
        this.estadoFormulario = 'Enviado exitosamente';
      }, 1000);
    }
  }

  formularioValido(): boolean {
    return this.validacionEmail.valido && this.archivoSeleccionado !== null;
  }

  formatearTamaño(bytes: number): string {
    if (bytes === 0) return '0 Bytes';
    const k = 1024;
    const tamaños = ['Bytes', 'KB', 'MB'];
    const i = Math.floor(Math.log(bytes) / Math.log(k));
    return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + tamaños[i];
  }
}

Gestión eficiente de listeners

Para aplicaciones complejas, es importante gestionar eficientemente los event listeners para evitar memory leaks y mejorar el rendimiento:

@Component({
  selector: 'app-gestion-listeners',
  template: `
    <div class="gestion-eventos">
      <h3>Gestión avanzada de eventos</h3>
      
      <div class="zona-scroll" 
           #zonaScroll
           (scroll)="manejarScrollOptimizado($event)"
           style="height: 200px; overflow-y: auto; border: 1px solid #ccc;">
        @for (item of items; track item) {
          <div class="item">{{ item }}</div>
        }
      </div>
      
      <p>Posición scroll: {{ posicionScroll }}px</p>
      
      <div class="zona-resize">
        <p>Tamaño ventana: {{ tamañoVentana.width }}x{{ tamañoVentana.height }}</p>
        <p>Eventos de resize: {{ contadorResize }}</p>
      </div>
      
      <button (click)="toggleListeners()">
        {{ listenersActivos ? 'Desactivar' : 'Activar' }} Listeners
      </button>
    </div>
  `
})
export class GestionListenersComponent {
  items = Array.from({ length: 50 }, (_, i) => `Item ${i + 1}`);
  posicionScroll = 0;
  tamañoVentana = { width: 0, height: 0 };
  contadorResize = 0;
  listenersActivos = true;
  
  private timeoutScroll: any;
  private resizeListener?: () => void;

  ngOnInit() {
    this.inicializarListeners();
  }

  ngOnDestroy() {
    this.limpiarListeners();
  }

  manejarScrollOptimizado(evento: Event) {
    // Debouncing para optimizar el rendimiento
    clearTimeout(this.timeoutScroll);
    this.timeoutScroll = setTimeout(() => {
      const elemento = evento.target as Element;
      this.posicionScroll = elemento.scrollTop;
    }, 16); // ~60fps
  }

  private inicializarListeners() {
    // Listener de resize optimizado
    this.resizeListener = this.throttle(() => {
      this.tamañoVentana = {
        width: window.innerWidth,
        height: window.innerHeight
      };
      this.contadorResize++;
    }, 250);

    if (this.listenersActivos) {
      window.addEventListener('resize', this.resizeListener);
      this.tamañoVentana = {
        width: window.innerWidth,
        height: window.innerHeight
      };
    }
  }

  private limpiarListeners() {
    if (this.resizeListener) {
      window.removeEventListener('resize', this.resizeListener);
    }
    clearTimeout(this.timeoutScroll);
  }

  toggleListeners() {
    this.listenersActivos = !this.listenersActivos;
    
    if (this.listenersActivos) {
      this.inicializarListeners();
    } else {
      this.limpiarListeners();
    }
  }

  // Función throttle para optimizar eventos frecuentes
  private throttle(func: Function, delay: number) {
    let timeoutId: any;
    let lastExecTime = 0;
    
    return (...args: any[]) => {
      const currentTime = Date.now();
      
      if (currentTime - lastExecTime > delay) {
        func.apply(this, args);
        lastExecTime = currentTime;
      } else {
        clearTimeout(timeoutId);
        timeoutId = setTimeout(() => {
          func.apply(this, args);
          lastExecTime = Date.now();
        }, delay - (currentTime - lastExecTime));
      }
    };
  }
}

El manejo avanzado de eventos DOM en Angular proporciona control granular sobre la interactividad, permitiendo crear aplicaciones web sofisticadas que responden de manera eficiente a las acciones del usuario mientras mantienen un rendimiento óptimo.

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 sintaxis básica del event binding en Angular usando paréntesis.
  • Aprender a capturar eventos comunes del DOM como clicks, movimientos del ratón y eventos de teclado.
  • Utilizar el objeto $event para acceder a detalles del evento y combinarlo con parámetros personalizados.
  • Gestionar la propagación y prevención de comportamientos predeterminados de eventos.
  • Implementar técnicas avanzadas para optimizar el manejo de listeners y eventos en formularios y elementos anidados.