Sebastian Gomez
Implementando filter como operador en Observables
En este post vamos a explorar cómo aumentar el número de operadores en un Observable añadiendo el operador filter.
Recordemos del post anterior, donde implementamos el operador map en Observables desde cero, de manera similar a como lo hace RxJS. Ahora vamos a incrementar la cantidad de operadores que podemos usar en un Observable.
Primero, echemos un vistazo a la definición más pura de un Observable con una función generadora de observables a partir de eventos:
// Define una clase llamada Observable
class Observable {
// El constructor de la clase recibe una función de suscripción
constructor(subscribe) {
// Almacena la función de suscripción en una propiedad privada
this._subscribe = subscribe;
}
// Método para suscribir un observador al observable
subscribe(observer) {
// Ejecuta la función de suscripción con el observador proporcionado
return this._subscribe(observer);
}
// Método estático que crea un observable a partir de un evento del DOM
static fromEvent(domElement, eventName) {
// Retorna una nueva instancia de Observable
return new Observable(function subscribe(observer) {
// Define un manejador de eventos que invoca el método next del observador con el evento
const handler = (ev) => {
observer.next(ev);
};
// Añade el manejador de eventos al elemento del DOM para el nombre del evento dado
domElement.addEventListener(eventName, handler);
// Retorna un objeto con un método para cancelar la suscripción al evento
return {
unsubscribe() {
// Elimina el manejador de eventos del elemento del DOM
domElement.removeEventListener(eventName, handler);
},
};
});
}
}Con esto, puedes crear Observables que devuelven información del evento de la siguiente manera:
// Obtiene una referencia al botón con ID "button" del documento HTML
const button = document.getElementById("button");
// Crea un observable llamado "clicks" utilizando el método estático "fromEvent" de la clase Observable,
// suscribiendo al botón al evento "click"
const clicks = Observable.fromEvent(button, "click");
// Suscribe un objeto observador al observable "clicks" y almacena la suscripción en "clicksSubscription"
const clicksSubscription = clicks.subscribe({
// Define el método "next" del objeto observador para manejar eventos de clic en el botón
next(e) {
// Registra en la consola un mensaje indicando que se hizo clic en el botón y muestra el objeto del evento "e"
console.log("Click in the button", e); // "e" contiene toda la información del evento
}
});
// Cancela la suscripción a eventos de clic en el botón utilizando el método "unsubscribe" del objeto de suscripción
clicksSubscription.unsubscribe(); // Así te puedes desuscribirPero a veces necesitamos realizar más operaciones sobre la cadena de datos. Por ejemplo, si queremos obtener sólo algunos datos del evento, como la posición del mouse, o si sólo queremos escuchar los clicks que se hagan en la parte izquierda del botón. Para ello, vamos a implementar la función filter sobre Observables.
Ejemplo de filter en Arrays
Veamos cómo funciona filter en Arrays de JavaScript antes de implementarlo en Observables:
[1, 2, 3, 4].filter((x) => x > 3);
// retorna [4]filter, al igual que map, recibe una función como parámetro. Esta función es de validación y retorna true o false. El operador filter devuelve el elemento para el cual la función de validación es true.
Implementando filter en Observables
Recuerda que el método map lo definimos en el post anterior, así que aquí solo agregamos el método filter a la misma clase Observable:
// Define un método "filter" que toma una función de validación como argumento
filter(validationFunction) {
// Almacena una referencia al objeto actual en la variable "self"
const self = this;
// Retorna un nuevo observable
return new Observable(function subscribe(observer) {
// Suscribe un objeto observador al observable actual ("self") y almacena la suscripción en "subscription"
const subscription = self.subscribe({
// Define el método "next" del objeto observador para manejar los valores emitidos por el observable
next(v) {
// Si la función de validación retorna verdadero para el valor "v", pasa el valor al método "next" del observador
if (validationFunction(v)) {
observer.next(v);
}
},
// Define el método "error" del objeto observador para manejar errores emitidos por el observable
error(e) {
// Pasa el error "e" al método "error" del observador
observer.error(e);
},
// Define el método "complete" del objeto observador para manejar la finalización del observable
complete() {
// Llama al método "complete" del observador
observer.complete();
}
});
// Retorna la suscripción
return subscription;
});
}Ahora veamos cómo usar el operador filter en Observables para filtrar los clicks que se hacen en la parte izquierda del botón. Como filter y map retornan un nuevo Observable, podemos encadenarlos directamente:
// Obtiene una referencia al botón con ID "button" del documento HTML
const button = document.getElementById("button");
// Crea un observable llamado "clicks" utilizando el método estático "fromEvent" de la clase Observable,
// suscribiendo al botón al evento "click"
const clicks = Observable.fromEvent(button, "click");
// Filtra y transforma los eventos de clic antes de suscribir un objeto observador
const clicksSubscription = clicks
// Aplica el método "filter" para permitir solo eventos en los que la posición X del mouse sea menor a 40
.filter((ev) => ev.offsetX < 40)
// Aplica el método "map" (implementado en el post anterior) para transformar el evento en su posición X del mouse (offsetX)
.map((ev) => ev.offsetX)
// Suscribe un objeto observador a los eventos filtrados y transformados
.subscribe({
// Define el método "next" del objeto observador para manejar eventos de clic en el botón
next(offsetX) {
// Registra en la consola un mensaje indicando la posición X del mouse en el momento del clic
console.log("La posición del mouse respecto a X " + offsetX); // Solo imprime si haces click a la izquierda de "Me"
},
});Aquí tienes el ejemplo completo con la implementación de filter y map:
https://codepen.io/seagomezar/pen/WdVzxB
Nota: verifica que el enlace de CodePen siga activo antes de publicar; los IDs cortos antiguos a veces dejan de existir.
Conclusiones
- Hemos aprendido a implementar el operador
filteren Observables para filtrar y procesar los eventos que cumplan con ciertos criterios. - Los operadores en Observables, como
filterymap, permiten manipular y realizar operaciones sobre la cadena de datos de manera más eficiente. - Los operadores sobre Observables siguen una lógica similar y, en su mayoría, deben retornar un nuevo Observable. Las operaciones o filtros sobre los valores se realizan en la función
next.
Ejercicios para practicar
- Crea un Observable que filtre y muestre solo los clicks realizados con el botón derecho del mouse.
- Implementa un Observable que filtre y muestre solo los eventos de teclado con teclas de flechas (arriba, abajo, izquierda y derecha).
- Combina los operadores
filterymappara crear un Observable que filtre eventos de teclado y transforme la salida en un objeto que contenga información relevante sobre la tecla presionada (por ejemplo, el nombre de la tecla y si fue presionada con alguna tecla modificadora como Shift, Ctrl, etc.).
Resumen en 3 puntos
- Aprendimos a implementar y utilizar el operador
filteren Observables. - Entendimos cómo funcionan los operadores en Observables, como
filterymap, para manipular eventos. - Vimos ejemplos de cómo aplicar
filterymapen casos de uso reales, como filtrar clicks y eventos de teclado.
Ideas adicionales para aplicar lo aprendido
- Implementa un Observable que filtre y muestre los eventos de
scrolldel usuario en una página web, pero solo cuando el desplazamiento es hacia arriba. - Crea un Observable que detecte y muestre eventos de cambio (
change) en un formulario, pero solo cuando el valor ingresado cumpla con ciertas condiciones, como una longitud mínima o un formato específico. - Combina los operadores
filterymappara crear un Observable que detecte eventos demousemovey transforme la salida en un objeto que contenga información relevante sobre la posición del mouse en porcentajes respecto al ancho y alto de la ventana. - Utiliza la función
filteren Observables para implementar un sistema de autocompletado que muestre sugerencias en función de la entrada del usuario en un campo de texto, pero solo cuando el usuario haya escrito al menos tres caracteres. - Implementa un Observable que filtre y muestre eventos de cambio en el tamaño de la ventana (
resize), pero solo cuando la diferencia entre el tamaño anterior y el nuevo tamaño sea mayor a un valor específico.
Estas ideas adicionales te ayudarán a aplicar y consolidar lo aprendido sobre la implementación de filter en Observables. No olvides que puedes combinar diferentes operadores para lograr resultados más complejos y adaptados a tus necesidades.
Eso es todo, espero que este post te sea de utilidad y lo puedas aplicar a algún proyecto que tengas en mente. Déjame un comentario si te sirvió o si tienes alguna duda, y si te gustó, compártelo usando los links a las redes sociales aquí abajo. ¡Buena suerte en tus proyectos y sigue explorando las posibilidades que ofrecen los Observables!
Sebastian Gomez
Creador de contenido principalmente acerca de tecnología.