diff --git a/README.md b/README.md index ea3df1e..04cb028 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,253 @@ -# Proyecto inicial - Curso de RXJS +# Observables -* Lo primero que debemos de hacer después de descargar el código es ejecutar el comando: +* Cómo estandar a los nombres de los observables se les añade un `$` al final, para poder identificarlos mejor: `const obs$` +* Creación de un observable de forma manual: +```javascript +const obs$ = new Observable( subscriber => { + subscriber.next(1); + subscriber.next(2); + subscriber.next(3); + subscriber.complete(); +}); ``` -npm install +* **Observer**: Pueden ser enviados directamente a un subscriptor, pueden contar con un **next**, un **error** y un **complete** +```javascript +const observer: Observer = { + next: value => console.log('[next]:', value), + error: error => console.warn('[error]:', error), + complete: () => console.info('[completed]') +} ``` -Ese comando descargará todos los módulos de node necesarios para ejecutar el proyecto. +* **Subscriber**: Los subscriptores que cuenten con más de un argumento, es decir next, error y/complete deben de ser enviados como un observer: `next: .. `- `error: ... ` +* **Unsubscribe**: Para poder ejecutar un desuscripción, deberiamos asignar la suscripción a una variable, y despues hacer referencia a esta usando `.unsubscribe()` +* Para ejecutar una linea de código en el momento de la desuscripción, en el observer debe de estar incluido un return: +```javascript +return { + clearInterval(intervalo); + console.log('Intervalo Destruido'); +} +``` +* **Add**: Observables en cadena - Usando por ejemplo: `subs1.add(subs2); subs1.add(subs3)` al momento de efectuar la desuscripción al subs1, nos desuscribiriamos también del `subs2` y del `subs3`. +* **Subject**: Un subject nos permite un casteo múltiple, es decir, la información de un observable va a ser retransmitida de forma exactamente igual para cualquier subscriptor, ejemplo un observable que nos podruce valores random para cada subscriptor, con subject nos devolveria la misma información a todos los suscriptores. +* Un **subject** también es un **Observer**, cuenta con su `next`, `error` y `complete`. Creación de un subject: +```javascript +const subject$ = new Subject(); +const subscription = intervalo$.subscribe(subject$); // El subject se suscribe al observable + +// Nos suscribimos al subject +const subs1 = subject$.subscribe(rnd => console.log('Subs 1:', rnd)); +const subs2 = subject$.subscribe(rnd => console.log('Subs 2:', rnd)); +``` +* **Cold Observable**: Cuando la data es producida por el observable en sí mismo. +* **Hot Observable**: Cuando la data es producida FUERA del observable. + +# Funciones para crear Observables + +* **of**: Convierte los argumentos a un observable, puede ser un *numero*, *string*, *objeto*, *array*, *promesa*, *booleano*, *function*, *lambda function* ... Trabaja de manera síncrona. Los valores deben de ser mandados con coma, si se mandara un arreglo se tomaria como un unico argumento, se podría usar el operador spread para mandar todos como valores individuales, produciendo lo mismo que sin **[ ]** +* **from**: Convierte a un observable desde una *array*, *objeto*, *promesa*, *iterable*, *función generadora*, o un *objeto observable*. +#### **of** & **from** comparativa +```javascript +const source$ = from([1,2,3,4,5]); // Output: 1, 2, 3, 4, 5 +const source$ = of([1,2,3,4,5]); // Output: [1, 2, 3, 4, 5] + +const source$ = from('Alejandro'); // Output: A, l, e, j, a, n, d, r, o +const source$ = of('Alejandro'); // Output: Alejandro +``` +* **fromEvent**: Crea un observable que emite un evento del tipo específico asignado: `fromEvent(document,'click')` +* **range**: Crea un observable que emite una secuencia de numeros dentro de un rango específico. Específicando solo un valor emite ese número de veces empezando desde el 0: +```javascript +range(2) // Emits 0, 1 +range(1,5) // Emits 1, 2, 3, 4, 5 +``` +* **interval**: Crea un observable que emite una secuencia de números cada intervalo especificado de manera infinita: `interval(1000)`. Si no se especifica ningun valor: `interval()` el intervalo se ejecutaría cada milisegundo. +* **timer**: Crea un observable que espera el tiempo asignado o **fecha** antes de ejecutarse. Se puede usar junto con *pipe* y *map* para que ejecute la función se quiera. +* **asyncScheduler**: Programa una tarea para que sea ejecutada en el tiempo específico, haciendo a esa tarea o función **asíncrona**: +### Parecido a un setTimeout usando asynScheduler +```javascript +// Esta función seria ejecutada al cabo de 3 segundos +const saludar = () => console.log('Hola Mundo'); +asyncScheduler.schedule(saludar,3000) +// Para enviar argumentos a la función, debe de ser como un ESTADO detrás del intervalo. +const saludar = (nombre) => console.log(`Hola ${nombre}`); +asyncScheduler.schedule(saludar,3000,'Alejandro') // Se puede enviar también un objeto: {nombre: 'Alejandro'} +``` +### Parecido a un setInterval usando asynScheduler +```javascript +// No puede ser una función de flecha, la razón es que para hacer un intervalo vamos a tener que usar el THIS, +// que solo esta disponible las funciones normales. Esta función tambien puede estar separada en otra parte del código +const subs = asyncScheduler.schedule(function (state){ + console.log('state',state); + + this.schedule(state + 1, 1000) +}, 3000, 0) + +// También podemos desuscribirnos usando un asyncScheduler en lugar de un setTimeout +asyncScheduler.schedule( () => subs.unsubscribe(), 6000 ) +``` + +# Operadores básicos + +* **map**: Nos permite transformar lo que emite el observable por lo que nososotros queramos, se puede usar para desestructurar el valor emitido. En el caso que *map* no pueda encontrar la propiedad de un objeto anidado se deberia usar de la siguiente manera: `map( event => event.target['value'] )` +* **mapTo**: Transforma las emisiones del observable a una salida específica: `mapTo('Hola Mundo')` +* **~~pluck~~**: *(Deprecated)* Permite extraer la propiedad de un objeto. `pluck('target')` -- `pluck('target', 'value')` +* **filter**: Nos permite filtrar las emisiones que emiten los observables con una condición especifica. `filter(value => value % 2 === 1` Solo llegarian a la salida los números impares. +* **tap**: Operador para ejecutar acciones secundarias. Ya sea mostrar en consola, como disparar otro operador para efectuar otra accion, etc… +* **reduce**: Aplica una función acumuladora a las emisiones producidas por el observable: +```javascript +// Recibe el valor acumulado y el actual. Retorna el valor acumulado sumado al actual. +// Pero sólo emitira cuando se completa el observable, retornando el valor total. +reduce( (acc, cur) => acc + cur, 0 ) +``` +* **scan**: Exactamente lo mismo al operador **reduce**, la única diferencia es que los valores son emitidos a la salida mostrando el valor acumulado. +* **Laboratorio progressBar**: Creación de un progressBar al hacer scroll que se va rellenando en función de cuanto scroll hagamos en la página, usando *fromEvent* para escuchar el evento *scroll*, *map* para enviar el evento a una función que en base a **scrollHeight**, **scrollTop** y **clientHeight**, calcula el porcentaje actual de scroll en la página y modifica el *style.width* con el porcentaje recibido. +# Operadores no tan comunes -* Cuando termine de instalar los node_modules, entonces podermos ejecutar el proyecto de con el siguiente comando +* **take**: El take deja pasar el número de emisiones entre el parentesis ej: `take(2)` y después se completa automáticamente. +* **first**: Deja pasar la primera emisión y después se completa. Puede ser empleado con una condición: `first(val => val >= 3)` En este caso se completaria cuando la emisión fuera igual o superior a 3. +* **takeWhile**: Deja pasar los elementos mientras no se cumpla la condición establecida, cuando algun valor hace que se cumpla la condición, el observable se completa. `takeWhile(({ y }) => y <= 150, true)` - El segundo argumento es el *inclusive*, si se manda un true, emitiría el valor que hizo que se completara el observable. +* **takeUntil**: Sigue emitiendo los valores hasta que el observable pasado como argumento recibe su primer valor: +```javascript +const timer$ = interval(1000); +const clickBtn$ = fromEvent(button,'click'); +timer$.pipe( + takeUntil(clickBtn$); +).subscribe(console.log) +// Tambien se puede usar en una sola línea de esta manera. +takeUntil(fromEvent(button,'click')); ``` -npm start +* **skip**: Omite el numero de emisiones que se le han pasado como argumento. `skip(2)`. En este caso las 2 primeras emisiones del observable seran ignoradas, y empezara a emitir a partir del tercer valor. +* **distinct**: Omite una emisión repetida por un observable. Ej: `of(1,2,3,4,1,1,1,5,5)` - De forma normal se recibirían todos los números, pero usando el `distinct()` solo se recibirían `1,2,3,4,5`. Para usar el `distinct()` con propiedades de objetos se debe especificar la propiedad que quiero que sea "única": **`distinct(p => p.nombre)`** +* **distinctUntilChanged**: Omite una emisión si la anterior es exactamente igual a la actual. Ej: `of(1,1,3,3,2,2,4,4,5,3,1)` - De forma normal se recibirían todos los números, pero usando el `distinctUntilChanged()` solo se recibirían `1,3,2,4,5,3,1`. Para usar el `distinctUntilChanged()` con propiedades de objetos se debe especificar una función comparativa: **`distinctUntilChanged( (anterior, actual) => anterior.nombre === actual.nombre))`** +* **distinctUntilKeyChanged**: Escucha la *propiedad* de un objeto y omite la emisión si la anterior es exactamente igual a la actual `distinctUntilKeyChanged('nombre')` + +# Operadores que funcionan con tiempo + +* **delay**: Retrasa la emisión del observable por la cantidad de tiempo pasado como argumento, o hasta la fecha asignada. `delay(1000)` -- `delay(Date)` +* **debounceTime**: Espera la cantidad pasada como argumento antes de emitir el último valor, si un nuevo valor es emitido antes de que haya transcurrido ese lapso de espera, este se reiniciaria volviendo a esperar el lapso asignado hasta que no haya mas emisiones en ese periodo de tiempo para poder emitir el valor. +* **throttleTime**: Emite el valor inmediatamente y espera la cantidad de tiempo asignada hasta volver a emitir el siguiente valor, si recibe algun valor durante ese "cooldown" serian ignorados, a menos que se especifique de la siguiente manera. +```javascript +throttleTime(1000,asyncScheduler, { + leading: true, // Emite el primer valor + trailing: true // Emite el último valor +}), +``` +* **sampleTime**: Nos permite obtener el último valor emitido dentro de un intervalo de tiempo. Es decir, si especificamos un `sampleTime(1000)` si el observable en ese lapso de 1 segundo emitiese 5 valores, al finalizar ese segundo, emitirá el último valor dentro de ese lapso. +* **sample**: Nos permite obtener una "muestra" de la última emisión de un observable al recibir un valor de *otro **observable***: +```javascript +// Cada vez que hicieramos click en el documento, recibiriamos la última emisión del observable. +const interval$ = interval(500); +const click$ = fromEvent(document,'click'); + +interval$.pipe( + sample(click$) +).subscribe(console.log); ``` -Para que eso funcione, recuerden que deben de ejecutar ese comando en el mismo directorio donde se encuentra el ```package.json``` +* **auditTime**: Espera la cantidad pasada como argumento antes de emitir el último valor. A diferencia del **`debounceTime()`** este no se reinicia al recibir un nuevo valor, simplemente emitira el último valor al finalizar la cantidad de tiempo asignada. Muy parecido a `sampleTime()`. La única diferencia con `sampleTime()`que tiene es que si el observable se completa antes, no se emitiría ningun último valor. -## Cambiar el puerto -Por defecto, el puerto que configuré para este proyecto es el ```8081```, pero si necesitan cambiarlo porque pueda que ese puerto lo use su computadora, pueden cambiarlo abriendo el ```package.json``` >> scripts. Ahí verán la instrucción que lanza el servidor de desarrollo +# AJAX +* **ajax**: Peticiones AJAX en RxJS `import { ajax } from 'rxjs/ajax';` +```javascript +ajax(url); +// Same as +ajax.get(url); + +// Headers +ajax(url, { + 'token': 'auth123' +}); +``` +* **getJSON**: Devuelve el body de la petición en formato JSON directamente, sin tener que pasarlo por el **map** o el **pluck**: `ajax.getJSON(url);` +* **catchError**: Un catchError debe retornar siempre un observable, se puede conseguir mediante un `return of(false)`, `of([])`, `of({})`... etc +* **AjaxError**: Tipado para el error de una petición *AJAX* +* **CRUD**: POST, PUT and DELETE. (Una petición DELETE devolvería un error en caso de incluirse el body tal y como está en el ejemplo) +```javascript +ajax.post(url, { + id: 1, + nombre: 'Alejandro' +}, { + 'mi-token': 'ABC123' +}).subscribe(console.log); ``` -"start": "webpack serve --mode development --open --port=8081" +* **CRUD**: Método reutilizable: (En el caso que fuera una petición DELETE no devolvería error, se ejecutaría correctamente) +```javascript +ajax({ + url, + method: 'DELETE', + headers: { + 'mi-token': 'ABC123' + }, + body: { + id: 1, + nombre: 'Alejandro' + } +}).subscribe(console.log); ``` -Simplemente cambian el puerto por el que ustedes necesiten y listo. (lógicamente graban los cambios antes de ejecutar el ```npm start``` nuevamente) +# Operadores de transformación o aplanamiento (Flattening Operators) +Los operadores de transformación o aplanamiento reciben un observable y se suscriben a este de manera interna. +Este tipo de operadores son necesarios cuando tenemos un observable dentro de otro observable, el cual necesitariamos suscribirnos dentro de la suscripción para poder recibir los valores, dicho proceso seria un poco dificil de leer y de mantener. + +* **mergeAll**: Este operador se suscribe a los observables emitidos por el source, y esta pendiente de ellos, nos devolveria las emisiones de todas las suscripciones internas en un única suscripción. No se completaria hasta que todas las suscripciones a las que esta suscrito se completen. Puede recibir un argumento que es el número de observables internos a los que se suscribira, por defecto es ***Infinito***. `mergeAll()` +* **mergeMap**: Se llama con un *callBack*: `mergeMap( () => )`. Este operador funciona de igual manera que el **mergeAll**, la diferencia es que este puede ***transformar*** los valores de las suscripciones internas, al igual que *switchMap*, *concatMap* y *exhaustMap* +```javascript +mergeMap(x => x * 5) +-------- +mergeMap(() => interval(1000)) +``` +* **switchMap**: Se llama con un *callBack*: `switchMap( () => )`. Es igual que el *mergeMap*, con la única diferencia que mientras que el *mergeMap* puede mantener infinitas suscripciones internas, el **switchMap** solamente estará suscrito al más reciente. Es decir si tenemos una suscripción interna a un *interval* y nos entra otro valor del source, ese intervalo se completaria y pasaría a emitir los valores del último *interval*. Muy útil para las peticiones *AJAX*. +* **concatMap**: Se llama con un *callBack*: `concatMap( () => )`. Los observables a los que se suscribe internamente pasan inmediatamente a la salida, pero si entra un nuevo observable al que debe suscribirse, este lo pondria en la **cola** hasta que el observable al que está suscrito se completase, en ese momento se suscribiria y empezaria a emitir esos valores. +* **exhaustMap**: Se llama con un *callBack*: `exhaustMap( () => )`. A diferencia del *concatMap*, si este recibe un nuevo observable al que suscribirse y ya tiene activa una suscripción lo va a ***ignorar*** completamente, para que este operador se suscriba internamente a otro observable, no tiene que tener ninguno activo en ese preciso momento. +# Operadores y métodos de combinación + +* **startWith**(Operador): Nos permite hacer una emisión o emisiones del argumento o argumentos pasado antes de que el observable EMPIECE a emitir. Pueden ser *strings*, *numeros*, *arrays*, *otro observable* etc.. Muy útil para cargar un loading mientras se efectua una petición HTTP `startWith('a','b','c')` +* **endWith**(Operador): Nos permite hacer una emisión o emisiones del argumento o argumentos pasado antes de que el observable se COMPLETE. Pueden ser *strings*, *numeros*, *arrays*, *otro observable* etc.. `endWith('a')` +* **concat**(Método): Método que nos permite encadenar observables, puede recibir tantos observables como queramos, hasta que el primer observable no se complete, no se ejecutaria el segundo. *concat* puede recibir también un array en lugar de un observable: `concat(of(1,2,3,4), of(1,2), [1,2,3])` +* **zip**: Combina multiples observables y retorna un observable cuyos valores son los valores en orden de los observables pasados como argumento. `zip(of(1),of(2))` devolveria un array de: [1, 2] +* **merge**(Método): Este método recibe uno o más observables y la salida va a ser el resultado de ambos observables combinados. +```javascript +merge( + keyup$.pipe(map(e => e.type)), + click$.pipe(map(e => e.type)) +).subscribe(console.log); +``` +* **combineLatest**(Método): Es un método con que nos permite mandar observables como argumentos(Deben de ser mandados como un *array*), combinarlos y emitir los valores de todos los observables internos simultáneamente. Este regresa un nuevo observable, el cual va a emitir valores hasta que todos los observables internos hayan emitido por lo menos un valor. Se deben completar todos los observables internos antes de que se complete el *combineLatest*. +```javascript +// Hasta pasados 2 segundos, no comenzaria a emitir nada y empezaria emitiendo un array de [1, 0] .. 1 +// Porque el primer observable ya era su segundo valor, y 0 por que el segundo intervalo acaba de comenzar a emitir. +combineLatest([ + interval(1000), + interval(2000) // Si este segundo intervalo se completase, el valor emitido por el primer intervalo seguiria + // siendo combinado con la última emisión de este intervalo: [2, 5], [3, 5], [4, 5] ... +]).subscribe(console.log) +``` +* **forkJoin**(Método): Recibe varios observables como argumentos(Deben de ser mandados como un *array* o un *objeto*). Estos observables deben de ser **FINITOS**, es decir, deben de completarse en algún momento, si no el *forkJoin* no emitiría ningún valor. Una vez completados todos los observables, retornaria el último valor de todos los observables como un *array* u *objeto*. Si uno de los observables da un error, todo el *forkJoin* fallaria, habria que pasar individualmente el observable con error por un *pipe* y un *catchError* para poder recibir los valores de los observables que no estan fallando. +```javascript +const numeros$ = of(1,2,3,4); +const intervalo$ = interval(1000).pipe(take(3)); +const letras$ = of('a','b','c').pipe(delay(3500)); + +forkJoin([ + numeros$, // 4 + intervalo$, // 2 + letras$ // c +]).subscribe(console.log); // [4, 2, c] + +forkJoin({ + num: numeros$, // 4 + int: intervalo$, // 2 + let: letras$ // c +}).subscribe(console.log); // {num: 4, int: 2, let: c} + +// La manera más usada del forkJoin es con peticiones AJAX +forkJoin({ + usuario: ajax.getJSON(`${GITHUB_API_URL}/${GITHUB_USER}`), + repos: ajax.getJSON(`${GITHUB_API_URL}/${GITHUB_USER}/repo123123s`).pipe(catchError(err => of([]))), + gists: ajax.getJSON(`${GITHUB_API_URL}/${GITHUB_USER}/gists`), +}).subscribe(console.log); +``` diff --git a/src/ajax/01-ajax-catchError.ts b/src/ajax/01-ajax-catchError.ts new file mode 100644 index 0000000..739d8fd --- /dev/null +++ b/src/ajax/01-ajax-catchError.ts @@ -0,0 +1,40 @@ +import { ajax, AjaxError } from 'rxjs/ajax' +import { map, pluck, catchError } from 'rxjs/operators'; +import { of } from 'rxjs'; + +const url = 'https://api.github.com/users?per_page=5'; + + +const atrapaError = (err: AjaxError) => { + console.warn('error en:', err) + return of(null) +} + +ajax(url).pipe( + map( resp => resp.response), + catchError(atrapaError) +).subscribe(users => console.log('usuarios:', users)); + +// const manejaErrores = (res: Response) => { +// if (!res.ok){ +// throw new Error(res.statusText); +// } else { +// return res; +// } +// } +// const fetchPromesa = fetch(url); + +// fetchPromesa.then( async(resp) => { +// if (!resp.ok) throw new Error(resp.statusText) + +// const data = await resp.json(); +// console.log(data) +// }).catch( err => console.warn('fuckedup',err) ) + + +// fetchPromesa +// .then(manejaErrores) +// .then(resp => resp.json()) +// .then(data => console.log('data:',data)) +// .catch(err => console.warn('error en usuarios', err)) + diff --git a/src/ajax/02-getJSON.ts b/src/ajax/02-getJSON.ts new file mode 100644 index 0000000..af3262a --- /dev/null +++ b/src/ajax/02-getJSON.ts @@ -0,0 +1,10 @@ +import { ajax } from 'rxjs/ajax'; + +const url = 'https://httpbin.org/delay/1'; + +const obs$ = ajax.getJSON(url, { + 'content-type': 'application/json', + 'mi-token': 'ABC123' +}); + +obs$.subscribe(data => console.log('data:', data)); \ No newline at end of file diff --git a/src/ajax/03-diff-ajax-getJson-catchError.ts b/src/ajax/03-diff-ajax-getJson-catchError.ts new file mode 100644 index 0000000..e57c4dd --- /dev/null +++ b/src/ajax/03-diff-ajax-getJson-catchError.ts @@ -0,0 +1,33 @@ +import { of } from 'rxjs'; +import { ajax, AjaxError } from 'rxjs/ajax'; +import { catchError } from 'rxjs/operators'; + +const url = 'https://httpbinxx.org/delay/1'; + +const manejaError = (response: AjaxError) => { + console.warn('error:', response.message); + return of({ + ok: false, + usuarios: [] + }) +} + +const obs$ = ajax.getJSON(url).pipe( + catchError(manejaError) +); +const obs2$ = ajax(url).pipe( + catchError(manejaError) +); + +obs$.subscribe(data => console.log('getJson:', data)) +obs2$.subscribe(data => console.log('ajax:', data)); + +// const obs$ = ajax.getJSON(url); + +// obs$.pipe( +// catchError(manejaError) +// ).subscribe({ +// next: val => console.log('next:', val), +// error: err => console.warn('error en subs:', err), +// complete: () => console.log('complete'), +// }) \ No newline at end of file diff --git a/src/ajax/04-put-post-delete.ts b/src/ajax/04-put-post-delete.ts new file mode 100644 index 0000000..1ef1a12 --- /dev/null +++ b/src/ajax/04-put-post-delete.ts @@ -0,0 +1,37 @@ +import { ajax } from 'rxjs/ajax'; + +const url = 'https://httpbin.org/delay/1'; + +ajax.get(url, { + 'token': 'auth1234' +}).subscribe(console.log); + +ajax.post(url, { + id: 1, + nombre: 'Alejandro' +}, { + 'mi-token': 'ABC123' +}).subscribe(console.log); + +ajax.put(url, { + id: 1, + nombre: 'Alejandro' +}, { + 'mi-token': 'ABC123' +}).subscribe(console.log); + +ajax.delete(url, { + 'mi-token': 'ABC123' +}).subscribe(console.log); + +ajax({ + url, + method: 'DELETE', + headers: { + 'mi-token': 'ABC123' + }, + body: { + id: 1, + nombre: 'Alejandro' + } +}).subscribe(console.log); diff --git a/src/ejercicios-terminados/01-capitalizar.ts b/src/ejercicios-terminados/01-capitalizar.ts new file mode 100644 index 0000000..7cf5ffe --- /dev/null +++ b/src/ejercicios-terminados/01-capitalizar.ts @@ -0,0 +1,30 @@ +import { from } from 'rxjs'; +import { map } from 'rxjs/operators'; +/** + * Ejercicio: + * El objetivo de es realizar la misma impresión, pero usando observables + * Nota: NO hay que usar el ciclo "FOR OF", usar un observable y llamar la función capitalizar + */ + +/** + * Salida esperada: + * Batman + * Joker + * Doble Cara + * Pingüino + * Hiedra Venenosa + */ +(() =>{ + + const nombres = ['batman', 'joker', 'doble cara', 'pingüino', 'hiedra venenosa']; + + const capitalizar = (nombre: string) => nombre.replace(/\w\S*/g, (txt) => txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase()); + + from(nombres).pipe( + map(capitalizar) // Utilizando la función de Fernando + // map(r => r[0].toUpperCase() + r.slice(1)) // Utilizando mi metodo. Funciona pero no capitaliza la segunda palabra + ).subscribe(console.log) + +})(); + + \ No newline at end of file diff --git a/src/ejercicios-terminados/02-reduce.ts b/src/ejercicios-terminados/02-reduce.ts new file mode 100644 index 0000000..b2c9324 --- /dev/null +++ b/src/ejercicios-terminados/02-reduce.ts @@ -0,0 +1,26 @@ +import { from } from 'rxjs'; +import { filter, reduce } from 'rxjs/operators'; + +/** + * Ejercicio: + * Sume todos los números del arreglo usando un reduce. + * Debe de filtrar para que sólo números sean procesados + * La salida debe de ser 32 + * + * Tip: + * isNan() es una función de JavaScript para determinar si es número + * Usar filter(...) para no tener problemas de tipado. + */ + +(() =>{ + + // isNaN('string' as any) + + const datos = [1, 2, 'foo', 3, 5, 6, 'bar', 7, 8]; + + from(datos).pipe( + filter( r => !isNaN(r) ), + reduce((acc, cur) => acc + cur) + ).subscribe( console.log ) // La salida debe de ser 32 + +})(); \ No newline at end of file diff --git a/src/ejercicios-terminados/03-random.ts b/src/ejercicios-terminados/03-random.ts new file mode 100644 index 0000000..f068f1f --- /dev/null +++ b/src/ejercicios-terminados/03-random.ts @@ -0,0 +1,30 @@ +import { interval, Subject } from 'rxjs'; +import { take, map } from 'rxjs/operators'; +/** + * Ejercicio: Realizar que los dos observables finales, + * emitan exactamente el mismo valor + * + * Tip: Hot Observable? subjects? + */ + +(() =>{ + + // == NO TOCAR este bloque ==================== + const reloj$ = interval(1000).pipe( + take(5), + map( val => Math.round(Math.random() * 100) ) + ); + // No tocar la creación del observable + // ============================================ + + + const subject$ = new Subject(); + reloj$.subscribe(subject$); + + // Estos dos observables deben de emitir exactamente los mismos valores + subject$.subscribe( val => console.log('obs1', val) ); + subject$.subscribe( val => console.log('obs2', val) ); + + + +})(); \ No newline at end of file diff --git a/src/ejercicios-terminados/04-cuenta-regresiva.ts b/src/ejercicios-terminados/04-cuenta-regresiva.ts new file mode 100644 index 0000000..9b31f6a --- /dev/null +++ b/src/ejercicios-terminados/04-cuenta-regresiva.ts @@ -0,0 +1,37 @@ +import { interval } from 'rxjs'; +import { map, take, mapTo } from 'rxjs/operators'; + +/** + * Ejercicio: Realizar una cuenta regresiva + * empezando de 7 + */ + +// Salida esperada === +// 7 +// 6 +// 5 +// 4 +// 3 +// 2 +// 1 +// 0 + +(() =>{ + + const inicio = 7; + const countdown$ = interval(700).pipe( + // Usar los operadores necesarios + // para realizar la cuenta regresiva + take(8), + map( i => inicio - i), + ); + + + // No tocar esta línea ================== + countdown$.subscribe( console.log ); // = + // ====================================== + + +})(); + + \ No newline at end of file diff --git a/src/ejercicios-terminados/05-combinar.ts b/src/ejercicios-terminados/05-combinar.ts new file mode 100644 index 0000000..073f8ea --- /dev/null +++ b/src/ejercicios-terminados/05-combinar.ts @@ -0,0 +1,52 @@ +import { interval, timer, combineLatest } from 'rxjs'; +import { map, take } from 'rxjs/operators'; +/** + * Ejercicio: Combinar ambos observables (letras$, numeros$) + * para que las emisiones sean la concatenación de los últimos + * valores emitidos + */ + +// Ejemplo de la tada esperada: +// a1 +// a2 +// b2 +// b3 +// c3 +// c4 +// d4 +// d5 +// e5 + +(() =>{ + + const letras = ['a','b','c','d','e']; + const numeros = [1,2,3,4,5]; + + // Emite letras cada segundo + const letras$ = interval(1000).pipe( + map( i => letras[i] ), + take( letras.length ) + ); + + // Emite numeros del 1 al 5 cada segundo, pero tiene un delay inicial + // de 500 milésimas + const numeros$ = timer(500,1000).pipe( + map( i => numeros[i] ), + take( numeros.length ) + ); + + // ======================================== + // Empezar a codificar aquí abajo + // Nota, el subscribe debe de ser así + // .subscribe( console.log ) + // Es decir, la salida en el subscribe debe + // de estar procesada en su totalidad + // ======================================== + + combineLatest([letras$, numeros$]).pipe( + // map(v => v[0] + v[1]) // Yo + map(([a,b]) => a + b) // Fernando + + ).subscribe(console.log) + +})(); \ No newline at end of file diff --git a/src/ejercicios-terminados/06-luke-skywalker.ts b/src/ejercicios-terminados/06-luke-skywalker.ts new file mode 100644 index 0000000..025241a --- /dev/null +++ b/src/ejercicios-terminados/06-luke-skywalker.ts @@ -0,0 +1,91 @@ +import { ajax } from 'rxjs/ajax'; +import { switchMap, map, tap, mergeAll, mergeMap, concatMap } from 'rxjs/operators'; +import { zip, of, from } from 'rxjs'; + +/** + * Ejercicio: + * Realizar 2 peticiones HTTP (ajax) una después de otra. + * + * La primera debe de obtener el personaje de Star Wars: + * Luke Skywalker, llamando el endpoint: /people/1/ --- Tiene que ser el 2, el 1 no retorna species + * + * La segunda petición, debe de ser utilizando el objeto + * de la petición anterior, y tomar la especie (species), + * que es un arreglo de URLs (array), dentro de ese arreglo, + * tomar la primera posición y realizar la llamada a ese URL, + * el cual debería de traer información sobre su especie (Human) + */ + +// Respuesta esperada: +// Información sobre los humanos en el universo de Star Wars +// Ejemplo de la data esperada +/* + { name: "Human", classification: "mammal", designation: "sentient", average_height: "180", skin_colors: "caucasian, black, asian, hispanic", …} +*/ + +// Respuesta esperada con Mayor dificultad +// Retornar el siguiente objeto con la información de ambas peticiones +// Recordando que se disparan una después de la otra, +// con el URL que viene dentro del arreglo de 'species' + +// Tip: investigar sobre la función zip: +// Que permite combinar observables en un arreglo de valores +// https://rxjs-dev.firebaseapp.com/api/index/function/zip + +// Ejemplo de la data esperada: +/* + especie: {name: "Human", classification: "mammal", designation: "sentient", average_height: "180", skin_colors: "caucasian, black, asian, hispanic", …} + personaje: {name: "Luke Skywalker", height: "172", mass: "77", hair_color: "blond", skin_color: "fair", …} +*/ + + +(() =>{ + + // No tocar ======================================================== + const SW_API = 'https://swapi.dev/api'; + const getRequest = ( url: string ) => ajax.getJSON(url); + // ================================================================== + + // Realizar el llamado al URL para obtener a Luke Skywalker + getRequest(`${SW_API}/people/2/`).pipe( + // Realizar los operadores respectivos aquí + switchMap( resp => { + return zip(of(resp), getRequest(resp.species[0])).pipe( + map(([personaje,especie]) => ({personaje, especie})) + ) + }) + + // Forma corta + // switchMap( resp => zip(of(resp), getRequest(resp.species[0])).pipe( + // map(([especie,personaje]) => ({especie, personaje})) + // )) + + // NO TOCAR el subscribe ni modificarlo == + ).subscribe( console.log ) // == + // ======================================= + +})(); + + +// Fernando + +(() =>{ + + // No tocar ======================================================== + const SW_API = 'https://swapi.dev/api'; + const getRequest = ( url: string ) => ajax.getJSON(url); + // ================================================================== + + // Realizar el llamado al URL para obtener a Luke Skywalker + getRequest(`${SW_API}/people/2/`).pipe( + // Realizar los operadores respectivos aquí + switchMap(resp => zip( of(resp), getRequest(resp.species[0]) ) ), + map(([personaje,especie]) => ({personaje, especie})) + + // NO TOCAR el subscribe ni modificarlo == + ).subscribe( console.log ) // == + // ======================================= + + + +})(); \ No newline at end of file diff --git a/src/ejercicios/01-capitalizar.ts b/src/ejercicios/01-capitalizar.ts new file mode 100644 index 0000000..e7a0b54 --- /dev/null +++ b/src/ejercicios/01-capitalizar.ts @@ -0,0 +1,35 @@ +/** + * Ejercicio: + * El objetivo de es realizar la misma impresión, pero usando observables + * Nota: NO hay que usar el ciclo "FOR OF", usar un observable y llamar la función capitalizar + */ + +/** + * Salida esperada: + * Batman + * Joker + * Doble Cara + * Pingüino + * Hiedra Venenosa + */ +(() =>{ + + + const nombres = ['batman', 'joker', 'doble cara', 'pingüino', 'hiedra venenosa']; + + const capitalizar = (nombre: string) => nombre.replace(/\w\S*/g, (txt) => txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase()); + + + // Cambiar este FOR OF, por un observable y capitalizar las emisiones + for( let nombre of nombres ) { + console.log( capitalizar(nombre) ) + } + + + + + + + +})(); + diff --git a/src/ejercicios/02-reduce.ts b/src/ejercicios/02-reduce.ts new file mode 100644 index 0000000..735e68f --- /dev/null +++ b/src/ejercicios/02-reduce.ts @@ -0,0 +1,29 @@ +import { from } from 'rxjs'; + +/** + * Ejercicio: + * Sume todos los números del arreglo usando un reduce. + * Debe de filtrar para que sólo números sean procesados + * La salida debe de ser 32 + * + * Tip: + * isNan() es una función de JavaScript para determinar si es número + * Usar filter(...) para no tener problemas de tipado. + */ + +(() =>{ + + + const datos = [1, 2, 'foo', 3, 5, 6, 'bar', 7, 8]; + + from(datos).pipe( + // Trabajar aquí + + + ).subscribe( console.log ) // La salida debe de ser 32 + + + +})(); + + \ No newline at end of file diff --git a/src/ejercicios/03-randoms.ts b/src/ejercicios/03-randoms.ts new file mode 100644 index 0000000..342c2b8 --- /dev/null +++ b/src/ejercicios/03-randoms.ts @@ -0,0 +1,33 @@ +import { interval } from 'rxjs'; +import { take, map } from 'rxjs/operators'; +/** + * Ejercicio: Realizar que los dos observables finales, + * emitan exactamente el mismo valor + * + * Tip: Hot Observable? subjects? + */ + +(() =>{ + + // == NO TOCAR este bloque ==================== + const reloj$ = interval(1000).pipe( + take(5), + map( val => Math.round(Math.random() * 100) ) + ); + // No tocar la creación del observable + // ============================================ + + + + + // Estos dos observables deben de emitir exactamente los mismos valores + reloj$.subscribe( val => console.log('obs1', val) ); + reloj$.subscribe( val => console.log('obs2', val) ); + + + + + +})(); + + \ No newline at end of file diff --git a/src/ejercicios/04-cuenta-regresiva.ts b/src/ejercicios/04-cuenta-regresiva.ts new file mode 100644 index 0000000..1008a6f --- /dev/null +++ b/src/ejercicios/04-cuenta-regresiva.ts @@ -0,0 +1,34 @@ +import { interval } from 'rxjs'; + +/** + * Ejercicio: Realizar una cuenta regresiva + * empezando de 7 + */ + +// Salida esperada === +// 7 +// 6 +// 5 +// 4 +// 3 +// 2 +// 1 +// 0 + +(() =>{ + + const inicio = 7; + const countdown$ = interval(700).pipe( + // Usar los operadores necesarios + // para realizar la cuenta regresiva + ); + + + // No tocar esta línea ================== + countdown$.subscribe( console.log ); // = + // ====================================== + + +})(); + + \ No newline at end of file diff --git a/src/ejercicios/05-combinar.ts b/src/ejercicios/05-combinar.ts new file mode 100644 index 0000000..eabd06c --- /dev/null +++ b/src/ejercicios/05-combinar.ts @@ -0,0 +1,56 @@ +import { interval, timer } from 'rxjs'; +import { map, take } from 'rxjs/operators'; +/** + * Ejercicio: Combinar ambos observables (letras$, numeros$) + * para que las emisiones sean la concatenación de los últimos + * valores emitidos + */ + +// Ejemplo de la tada esperada: +// a1 +// a2 +// b2 +// b3 +// c3 +// c4 +// d4 +// d5 +// e5 + +(() =>{ + + const letras = ['a','b','c','d','e']; + const numeros = [1,2,3,4,5]; + + // Emite letras cada segundo + const letras$ = interval(1000).pipe( + map( i => letras[i] ), + take( letras.length ) + ); + + // Emite numeros del 1 al 5 cada segundo, pero tiene un delay inicial + // de 500 milésimas + const numeros$ = timer(500,1000).pipe( + map( i => numeros[i] ), + take( numeros.length ) + ); + + // ======================================== + // Empezar a codificar aquí abajo + // Nota, el subscribe debe de ser así + // .subscribe( console.log ) + // Es decir, la salida en el subscribe debe + // de estar procesada en su totalidad + // ======================================== + + + + + + + + + +})(); + + \ No newline at end of file diff --git a/src/ejercicios/06-luke-skywalker.ts b/src/ejercicios/06-luke-skywalker.ts new file mode 100644 index 0000000..7fb2654 --- /dev/null +++ b/src/ejercicios/06-luke-skywalker.ts @@ -0,0 +1,65 @@ +import { ajax } from 'rxjs/ajax'; +import { switchMap, map } from 'rxjs/operators'; +import { zip, of } from 'rxjs'; + +/** + * Ejercicio: + * Realizar 2 peticiones HTTP (ajax) una después de otra. + * + * La primera debe de obtener el personaje de Star Wars: + * Luke Skywalker, llamando el endpoint: /people/1/ + * + * La segunda petición, debe de ser utilizando el objeto + * de la petición anterior, y tomar la especie (species), + * que es un arreglo de URLs (array), dentro de ese arreglo, + * tomar la primera posición y realizar la llamada a ese URL, + * el cual debería de traer información sobre su especie (Human) + */ + +// Respuesta esperada: +// Información sobre los humanos en el universo de Star Wars +// Ejemplo de la data esperada +/* + { name: "Human", classification: "mammal", designation: "sentient", average_height: "180", skin_colors: "caucasian, black, asian, hispanic", …} +*/ + +// Respuesta esperada con Mayor dificultad +// Retornar el siguiente objeto con la información de ambas peticiones +// Recordando que se disparan una después de la otra, +// con el URL que viene dentro del arreglo de 'species' + +// Tip: investigar sobre la función zip: +// Que permite combinar observables en un arreglo de valores +// https://rxjs-dev.firebaseapp.com/api/index/function/zip + +// Ejemplo de la data esperada: +/* + especie: {name: "Human", classification: "mammal", designation: "sentient", average_height: "180", skin_colors: "caucasian, black, asian, hispanic", …} + personaje: {name: "Luke Skywalker", height: "172", mass: "77", hair_color: "blond", skin_color: "fair", …} +*/ + + +(() =>{ + + // No tocar ======================================================== + const SW_API = 'https://swapi.dev/api'; + const getRequest = ( url: string ) => ajax.getJSON(url); + // ================================================================== + + // Realizar el llamado al URL para obtener a Luke Skywalker + getRequest(`Aquí va un URL`).pipe( + // Realizar los operadores respectivos aquí + + + + + + // NO TOCAR el subscribe ni modificarlo == + ).subscribe( console.log ) // == + // ======================================= + + + +})(); + + diff --git a/src/index.ts b/src/index.ts index e69de29..62406f7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -0,0 +1,3 @@ +import * as init from './operadores/04-lab-progressBar'; + +init \ No newline at end of file diff --git a/src/interfaces/github-users.interface.ts b/src/interfaces/github-users.interface.ts new file mode 100644 index 0000000..e58cb8b --- /dev/null +++ b/src/interfaces/github-users.interface.ts @@ -0,0 +1,29 @@ +// Generated by https://quicktype.io + +export interface GithubUsersResp { + total_count: number; + incomplete_results: boolean; + items: GithubUser[]; +} + +export interface GithubUser { + login: string; + id: number; + node_id: string; + avatar_url: string; + gravatar_id: string; + url: string; + html_url: string; + followers_url: string; + following_url: string; + gists_url: string; + starred_url: string; + subscriptions_url: string; + organizations_url: string; + repos_url: string; + events_url: string; + received_events_url: string; + type: string; + site_admin: boolean; + score: number; +} \ No newline at end of file diff --git a/src/observables/04-of.ts b/src/observables/04-of.ts new file mode 100644 index 0000000..e6a8a71 --- /dev/null +++ b/src/observables/04-of.ts @@ -0,0 +1,15 @@ +import { of } from 'rxjs'; + +// El of trabaja de manera síncrona -- Los valores deben de ser mandados con coma, si se mandara un arreglo se tomaria como un unico argumento, se podria usar el operador spread para mandar todos como valores individuales, produciendo lo mismo que sin [ ] +// const obs$ = of(1,2,3,4,5,6); +// const obs$ = of(...[1,2,3,4,5,6]); +const obs$ = of([1,2], {a: 1, b: 2}, function(){}, true, Promise.resolve(true)); + + +console.log('Inicio del Obs$'); +obs$.subscribe({ + next: (next) => console.log('next:',next), + error: null, + complete: () => console.log('Terminamos la secuencia') +}); +console.log('Fin del Obs$'); \ No newline at end of file diff --git a/src/observables/05-fromEvent.ts b/src/observables/05-fromEvent.ts new file mode 100644 index 0000000..bd46fa2 --- /dev/null +++ b/src/observables/05-fromEvent.ts @@ -0,0 +1,20 @@ +import { fromEvent } from 'rxjs'; + + +/** + * Eventos del DOM + */ +const src1$ = fromEvent(document,'click'); +const src2$ = fromEvent(document,'keyup'); + +const observer = { + next: (value) => console.log('next:', value) +}; + +src1$.subscribe(({x,y}) => { + console.log(x,y); +}); + +src2$.subscribe(evento => { + console.log(evento.key) +}); \ No newline at end of file diff --git a/src/observables/06-range.ts b/src/observables/06-range.ts new file mode 100644 index 0000000..4b446eb --- /dev/null +++ b/src/observables/06-range.ts @@ -0,0 +1,11 @@ +import { asyncScheduler, observeOn, of, range } from 'rxjs'; + +// const src$ = of(1,2,3,4,5); + +// Use of asyncScheduler on range is deprecated, must use the pipe with observeOn +// const src$ = range(1,5,asyncScheduler); +const src$ = range(1,5).pipe(observeOn(asyncScheduler)); + +console.log('inicio'); +src$.subscribe(console.log); +console.log('fin'); \ No newline at end of file diff --git a/src/observables/07-interval-timer.ts b/src/observables/07-interval-timer.ts new file mode 100644 index 0000000..978d745 --- /dev/null +++ b/src/observables/07-interval-timer.ts @@ -0,0 +1,21 @@ +import { timer, interval } from 'rxjs'; + +const observer = { + next: (value) => console.log('next:',value), + complete: () => console.log('complete') +} + + +// Date tambien puede ser mandado a un timer +const hoyEn5 = new Date(); // ahora +hoyEn5.setSeconds(hoyEn5.getSeconds() + 5); + + +const interval$ = interval(1000); +// const timer$ = timer(2000,1000); // Inicia un intervalo de 1 segundo cada emisión DESPUÉS de que hayan pasado 2 segundos +const timer$ = timer(hoyEn5); + +console.log('inicio'); +// interval$.subscribe(observer); +timer$.subscribe(observer); +console.log('fin'); \ No newline at end of file diff --git a/src/observables/08-asyncScheduler.ts b/src/observables/08-asyncScheduler.ts new file mode 100644 index 0000000..f3c3c49 --- /dev/null +++ b/src/observables/08-asyncScheduler.ts @@ -0,0 +1,36 @@ +import { asyncScheduler } from 'rxjs'; + +// setTimeout (() => {}, 3000); +// setInterval(() => {}, 3000); + + +/** + * setTimeout pasado a async + */ +const saludar = () => console.log('Hola Mundo'); +const saludar2 = (nombre) => console.log(`Hola ${nombre}`); +const saludar3 = ({nombre, apellido}) => console.log(`Hola ${nombre}`); + +// asyncScheduler.schedule(saludar, 2000); +// asyncScheduler.schedule(saludar2, 2000, 'Alejandro'); +asyncScheduler.schedule(saludar3, 2000, {nombre: 'Alejandro', apellido: 'Ortigosa'}); + + +/** + * setInterval pasado a async + */ + +// Para un intervalo, no puede ser una función de flecha +const subs = asyncScheduler.schedule(function(state){ + + console.log('state', state); + + this.schedule(state + 1, 1000) + +}, 3000, 0 ); + +// setTimeout(() => { +// subs.unsubscribe(); +// }, 6000); + +asyncScheduler.schedule( () => subs.unsubscribe(), 6000); \ No newline at end of file diff --git a/src/observables/09-from-avanzado.ts b/src/observables/09-from-avanzado.ts new file mode 100644 index 0000000..cd3c93a --- /dev/null +++ b/src/observables/09-from-avanzado.ts @@ -0,0 +1,41 @@ +import { of, from } from 'rxjs'; + +/** + * of = toma argumentos y genera una secuencia + * from = array, promise, iterable, observable + */ + +const observer = { + next: (value) => console.log('next:',value), + complete: () => console.log('complete') +} + +// const source$ = from([1,2,3,4,5]); // Output: 1, 2, 3, 4, 5 +// const source$ = of([1,2,3,4,5]); // Output: [1, 2, 3, 4, 5] + +// const source$ = from('Alejandro'); // Output: A, l, e, j, a, n, d, r, o +// const source$ = of('Alejandro'); // Output: Alejandro + +// const source$ = from(fetch('https://api.github.com/users/alesyt0h')); + +// source$.subscribe( async(resp) => { +// console.log(resp); + +// const data = await resp.json() +// console.log(data) +// }); + +const miGenerador = function*(){ + yield 1; + yield 2; + yield 3; + yield 4; + yield 5; +} + +const miIterable = miGenerador(); + +// for(let id of miIterable){ +// console.log(id); +// } +from(miIterable).subscribe(observer); \ No newline at end of file diff --git a/src/operadores-combinacion/01-startWith-endWith.ts b/src/operadores-combinacion/01-startWith-endWith.ts new file mode 100644 index 0000000..e72442a --- /dev/null +++ b/src/operadores-combinacion/01-startWith-endWith.ts @@ -0,0 +1,9 @@ +import { of } from 'rxjs'; +import { startWith, endWith} from 'rxjs/operators'; + +const numeros$ = of(1,2,3).pipe( + startWith('a','b','c'), + endWith('x','y','z') +); + +numeros$.subscribe(console.log); \ No newline at end of file diff --git a/src/operadores-combinacion/02-lab-startWith-loadingBackground.ts b/src/operadores-combinacion/02-lab-startWith-loadingBackground.ts new file mode 100644 index 0000000..1e23075 --- /dev/null +++ b/src/operadores-combinacion/02-lab-startWith-loadingBackground.ts @@ -0,0 +1,22 @@ +import { ajax } from 'rxjs/ajax'; +import { startWith } from 'rxjs/operators'; +// Referencias +const loadingDiv = document.createElement('div'); +loadingDiv.classList.add('loading'); +loadingDiv.innerHTML = 'Cargando'; + +const body = document.body; + +// Stream +ajax.getJSON('https://reqres.in/api/users/2?delay=3').pipe( + startWith(true) +).subscribe( resp => { + + if(resp === true){ + body.append(loadingDiv); + } else { + document.querySelector('.loading').remove(); + } + + console.log(resp) +}) diff --git a/src/operadores-combinacion/03-metodo-concat.ts b/src/operadores-combinacion/03-metodo-concat.ts new file mode 100644 index 0000000..0677954 --- /dev/null +++ b/src/operadores-combinacion/03-metodo-concat.ts @@ -0,0 +1,10 @@ +import { concat, interval } from 'rxjs'; +import { take } from 'rxjs/operators'; + +const interval$ = interval(1000); + +concat( + interval$.pipe(take(3)), + interval$.pipe(take(2)), + [1,2,3,4] +).subscribe(console.log) \ No newline at end of file diff --git a/src/operadores-combinacion/04-metodo-merge.ts b/src/operadores-combinacion/04-metodo-merge.ts new file mode 100644 index 0000000..c008b0e --- /dev/null +++ b/src/operadores-combinacion/04-metodo-merge.ts @@ -0,0 +1,10 @@ +import { fromEvent, merge } from 'rxjs'; +import { map } from 'rxjs/operators'; + +const keyup$ = fromEvent(document,'keyup'); +const click$ = fromEvent(document,'click'); + +merge( + keyup$.pipe(map(e => e.type)), + click$.pipe(map(e => e.type)) +).subscribe(console.log) \ No newline at end of file diff --git a/src/operadores-combinacion/05-metodo-combineLatest.ts b/src/operadores-combinacion/05-metodo-combineLatest.ts new file mode 100644 index 0000000..b745c48 --- /dev/null +++ b/src/operadores-combinacion/05-metodo-combineLatest.ts @@ -0,0 +1,25 @@ +import { combineLatest, fromEvent, merge } from 'rxjs'; +import { map } from 'rxjs/operators'; + +const input1 = document.createElement('input'); +const input2 = document.createElement('input'); + +input1.placeholder = 'email@gmail.com'; + +input2.placeholder = '*******'; +input2.type = 'password'; + +document.body.append(input1, input2); + +// Helper +const getInputStream = (elem: HTMLElement) => { + return fromEvent(elem, 'keyup').pipe( + map(e => e.target['value']) + ); +} + + +combineLatest([ + getInputStream(input1), + getInputStream(input2) +]).subscribe(console.log) \ No newline at end of file diff --git a/src/operadores-combinacion/06-metodo-forkJoin.ts b/src/operadores-combinacion/06-metodo-forkJoin.ts new file mode 100644 index 0000000..f9c6266 --- /dev/null +++ b/src/operadores-combinacion/06-metodo-forkJoin.ts @@ -0,0 +1,41 @@ +import { of, interval, forkJoin } from 'rxjs'; +import { delay, take } from 'rxjs/operators'; + +const numeros$ = of(1,2,3,4); +const intervalo$ = interval(1000).pipe(take(3)); +const letras$ = of('a','b','c').pipe(delay(3500)); + +// forkJoin([ +// numeros$, +// intervalo$, +// letras$ +// ]).subscribe(console.log); + + +// Dificil de leer +// +// forkJoin([ +// numeros$, +// intervalo$, +// letras$ +// ]).subscribe( resp => { +// console.log('numeros:', resp[0]) +// console.log('intervalo:', resp[1]) +// console.log('letras:', resp[2]) +// }); + +// forkJoin({ +// numeros$, +// intervalo$, +// letras$ +// }).subscribe( resp => { +// console.log(resp) +// }); + +forkJoin({ + num: numeros$, + int: intervalo$, + let: letras$ +}).subscribe( resp => { + console.log(resp) +}); diff --git a/src/operadores-combinacion/07-lab-forkJoin-peticiones.ts b/src/operadores-combinacion/07-lab-forkJoin-peticiones.ts new file mode 100644 index 0000000..9343683 --- /dev/null +++ b/src/operadores-combinacion/07-lab-forkJoin-peticiones.ts @@ -0,0 +1,14 @@ +import { forkJoin, of } from 'rxjs'; +import { ajax } from 'rxjs/ajax'; +import { catchError } from 'rxjs/operators'; + +const GITHUB_API_URL = 'https://api.github.com/users'; +const GITHUB_USER = 'alesyt0h'; + +forkJoin({ + usuario: ajax.getJSON(`${GITHUB_API_URL}/${GITHUB_USER}`), + repos: ajax.getJSON(`${GITHUB_API_URL}/${GITHUB_USER}/repo123123s`).pipe(catchError(err => of([]))), + gists: ajax.getJSON(`${GITHUB_API_URL}/${GITHUB_USER}/gists`), +}).pipe( + catchError( err => of(err.message)) +).subscribe(console.log); \ No newline at end of file diff --git a/src/operadores-tiempo/01-debounceTime.ts b/src/operadores-tiempo/01-debounceTime.ts new file mode 100644 index 0000000..2871413 --- /dev/null +++ b/src/operadores-tiempo/01-debounceTime.ts @@ -0,0 +1,21 @@ +import { debounceTime, fromEvent } from 'rxjs'; +import { map, pluck, distinctUntilChanged } from 'rxjs/operators'; + +const click$ = fromEvent(document,'click'); + +click$.pipe( + debounceTime(3000) +)//.subscribe(console.log) + +// Ejemplo 2 +const input = document.createElement('input'); +document.body.append(input); + +const input$ = fromEvent(input,'keyup'); + +input$.pipe( + // map(e => e.target.value), + debounceTime(1000), + pluck('target','value'), // No necesita tipado a diferencia del map + distinctUntilChanged() +).subscribe(console.log) \ No newline at end of file diff --git a/src/operadores-tiempo/02-throttleTime.ts b/src/operadores-tiempo/02-throttleTime.ts new file mode 100644 index 0000000..8a74bd6 --- /dev/null +++ b/src/operadores-tiempo/02-throttleTime.ts @@ -0,0 +1,23 @@ +import { fromEvent, asyncScheduler } from 'rxjs'; +import { throttleTime, pluck, distinctUntilChanged } from 'rxjs/operators'; + +const click$ = fromEvent(document,'click'); + +click$.pipe( + throttleTime(3000) +)//.subscribe(console.log) + +// Ejemplo 2 +const input = document.createElement('input'); +document.body.append(input); + +const input$ = fromEvent(input,'keyup'); + +input$.pipe( + throttleTime(400,asyncScheduler, { + leading: false, + trailing: true + }), + pluck('target','value'), // No necesita tipado a diferencia del map + distinctUntilChanged() +).subscribe(console.log) \ No newline at end of file diff --git a/src/operadores-tiempo/03-sampleTime.ts b/src/operadores-tiempo/03-sampleTime.ts new file mode 100644 index 0000000..e269a76 --- /dev/null +++ b/src/operadores-tiempo/03-sampleTime.ts @@ -0,0 +1,9 @@ +import { fromEvent } from 'rxjs'; +import { map, sampleTime } from 'rxjs/operators'; + +const click$ = fromEvent(document,'click'); + +click$.pipe( + sampleTime(2000), + map(({x, y}) => ({x,y})), +).subscribe(console.log); \ No newline at end of file diff --git a/src/operadores-tiempo/04-sample.ts b/src/operadores-tiempo/04-sample.ts new file mode 100644 index 0000000..0d9eb6d --- /dev/null +++ b/src/operadores-tiempo/04-sample.ts @@ -0,0 +1,8 @@ +import { interval, fromEvent, sample } from 'rxjs'; + +const interval$ = interval(500); +const click$ = fromEvent(document,'click'); + +interval$.pipe( + sample(click$) +).subscribe(console.log); \ No newline at end of file diff --git a/src/operadores-tiempo/05-auditTime.ts b/src/operadores-tiempo/05-auditTime.ts new file mode 100644 index 0000000..02d0b4a --- /dev/null +++ b/src/operadores-tiempo/05-auditTime.ts @@ -0,0 +1,10 @@ +import { fromEvent } from 'rxjs'; +import { tap, auditTime, map } from 'rxjs/operators'; + +const click$ = fromEvent(document,'click'); + +click$.pipe( + map(({x}) => ({x})), + tap(val => console.log('tap',val)), + auditTime(2000) +).subscribe(console.log); \ No newline at end of file diff --git a/src/operadores-transformacion/01-mergeAll.ts b/src/operadores-transformacion/01-mergeAll.ts new file mode 100644 index 0000000..9eef77d --- /dev/null +++ b/src/operadores-transformacion/01-mergeAll.ts @@ -0,0 +1,50 @@ +import { fromEvent, debounceTime, Observable } from 'rxjs'; +import { map, mergeAll } from 'rxjs/operators'; + +import { ajax } from 'rxjs/ajax'; + +import { GithubUser, GithubUsersResp } from '../interfaces/github-users.interface'; + +// Referencias +const body = document.body; +const textInput = document.createElement('input'); +const orderList = document.createElement('ol'); +body.append(textInput, orderList); + +// Helpers +const mostrarUsuarios = (usuarios: GithubUser[]) => { + + console.log(usuarios); + orderList.innerHTML = ''; + + for(const usuario of usuarios){ + const li = document.createElement('li'); + const img = document.createElement('img'); + img.src = usuario.avatar_url; + + const anchor = document.createElement('a'); + anchor.href = usuario.html_url; + anchor.text = 'Ver página'; + anchor.target = '_blank'; + + li.append(img); + li.append(usuario.login + ' '); + li.append(anchor); + + orderList.append(li); + } +} + +// Streams +const input$ = fromEvent(textInput,'keyup'); + +input$.pipe( + debounceTime(500), + map>(event => { + const texto = event.target['value']; + return ajax.getJSON(`https://api.github.com/search/users?q=${texto}`); + }), + mergeAll(), + map(v => v.items) +).subscribe( mostrarUsuarios); // console.log(users[0].url); + diff --git a/src/operadores-transformacion/02-mergeMap.ts b/src/operadores-transformacion/02-mergeMap.ts new file mode 100644 index 0000000..8d37c51 --- /dev/null +++ b/src/operadores-transformacion/02-mergeMap.ts @@ -0,0 +1,27 @@ +import { interval, of, fromEvent } from 'rxjs'; +import { mergeMap, take, map, takeUntil } from 'rxjs/operators'; + +const letras$ = of('a','b','c'); + +letras$.pipe( + mergeMap( (letra) => interval(1000).pipe( + map(i => letra + i), + take(3) + )) +)//.subscribe({ +// next: val => console.log('next:', val), +// complete: () => console.log('complete') +// }) + + +// Contar cuantos ¿?milisegundos?¿ esta presionado el raton (Left Button or Right Button) + +const mouseDown$ = fromEvent(document,'mousedown'); +const mouseUp$ = fromEvent(document,'mouseup'); +const interval$ = interval(); + +mouseDown$.pipe( + mergeMap( () => interval$.pipe( + takeUntil(mouseUp$) + )) +).subscribe(console.log) \ No newline at end of file diff --git a/src/operadores-transformacion/03-switchMap.ts b/src/operadores-transformacion/03-switchMap.ts new file mode 100644 index 0000000..65a9746 --- /dev/null +++ b/src/operadores-transformacion/03-switchMap.ts @@ -0,0 +1,55 @@ +import { fromEvent, debounceTime, Observable } from 'rxjs'; +import { map, mergeAll, mergeMap, pluck, switchMap } from 'rxjs/operators'; + +import { ajax } from 'rxjs/ajax'; + +import { GithubUser, GithubUsersResp } from '../interfaces/github-users.interface'; + +// Referencias +const body = document.body; +const textInput = document.createElement('input'); +const orderList = document.createElement('ol'); +body.append(textInput, orderList); + +// Helpers +const mostrarUsuarios = (usuarios: GithubUser[]) => { + + console.log(usuarios); + orderList.innerHTML = ''; + + for(const usuario of usuarios){ + const li = document.createElement('li'); + const img = document.createElement('img'); + img.src = usuario.avatar_url; + + const anchor = document.createElement('a'); + anchor.href = usuario.html_url; + anchor.text = 'Ver página'; + anchor.target = '_blank'; + + li.append(img); + li.append(usuario.login + ' '); + li.append(anchor); + + orderList.append(li); + } +} + +// Streams +const input$ = fromEvent(textInput,'keyup'); + +input$.pipe( + debounceTime(500), + mergeMap>(event => { + const texto = event.target['value']; + return ajax.getJSON(`https://api.github.com/search/users?q=${texto}`); + }), + map(v => v.items) +)//.subscribe( mostrarUsuarios); // console.log(users[0].url); + +const url = 'https://httpbin.org/delay/0?arg='; + +input$.pipe( + pluck('target','value'), + switchMap( texto => ajax.getJSON(url + texto)) +).subscribe(console.log); \ No newline at end of file diff --git a/src/operadores-transformacion/04-mergeMap-vs-switchMap.ts b/src/operadores-transformacion/04-mergeMap-vs-switchMap.ts new file mode 100644 index 0000000..eb03a66 --- /dev/null +++ b/src/operadores-transformacion/04-mergeMap-vs-switchMap.ts @@ -0,0 +1,10 @@ +import { fromEvent, interval } from 'rxjs'; +import { mergeMap, switchMap } from 'rxjs/operators'; + +const click$ = fromEvent(document, 'click'); +const interval$ = interval(1000); + +click$.pipe( + switchMap( () => interval$ ) + // mergeMap( () => interval$ ) +).subscribe(console.log); \ No newline at end of file diff --git a/src/operadores-transformacion/05-concatMap.ts b/src/operadores-transformacion/05-concatMap.ts new file mode 100644 index 0000000..fbff59a --- /dev/null +++ b/src/operadores-transformacion/05-concatMap.ts @@ -0,0 +1,9 @@ +import { interval, fromEvent } from 'rxjs'; +import { take, switchMap, concatMap } from 'rxjs/operators'; + +const interval$ = interval(500).pipe(take(3)); +const click$ = fromEvent(document,'click'); + +click$.pipe( + concatMap( () => interval$ ) +).subscribe(console.log); \ No newline at end of file diff --git a/src/operadores-transformacion/06-exhaustMap.ts b/src/operadores-transformacion/06-exhaustMap.ts new file mode 100644 index 0000000..12a1ab8 --- /dev/null +++ b/src/operadores-transformacion/06-exhaustMap.ts @@ -0,0 +1,9 @@ +import { interval, fromEvent } from 'rxjs'; +import { take, exhaustMap } from 'rxjs/operators'; + +const interval$ = interval(500).pipe(take(3)); +const click$ = fromEvent(document,'click'); + +click$.pipe( + exhaustMap( () => interval$ ) +).subscribe(console.log); \ No newline at end of file diff --git a/src/operadores-transformacion/07-lab-aplanamiento-diferencias.ts b/src/operadores-transformacion/07-lab-aplanamiento-diferencias.ts new file mode 100644 index 0000000..f739285 --- /dev/null +++ b/src/operadores-transformacion/07-lab-aplanamiento-diferencias.ts @@ -0,0 +1,46 @@ +import { fromEvent, of } from 'rxjs'; +import { tap, map, mergeMap, switchMap, exhaustMap, catchError } from 'rxjs/operators'; +import { ajax } from 'rxjs/ajax'; + +// Helper +const peticionHttpLogin = (userPass) => ajax.post('https://reqres.in/api/login?delay=1', userPass) +.pipe( + map(res => res.response['token']), + catchError(err => of(err.response.error)) +) + +// Creando un formulario +const form = document.createElement('form'); +const inputEmail = document.createElement('input'); +const inputPass = document.createElement('input'); +const submitBtn = document.createElement('button'); + +// Configuraciones +inputEmail.type = 'email'; +inputEmail.placeholder = 'Email'; +inputEmail.value = 'eve.holt@reqres.in'; + +inputPass.type = 'pass'; +inputPass.placeholder = 'Password'; +inputPass.value = 'cityslicka'; + +submitBtn.innerHTML = 'Submit'; + +form.append(inputEmail, inputPass, submitBtn); +document.body.append(form); + +// Streams +const submitForm$ = fromEvent(form, 'submit').pipe( + tap(ev => ev.preventDefault()), + map(ev => ({ + email: ev.target[0].value, + password: ev.target[1].value, + })), + // mergeMap(peticionHttpLogin) // Ejecutaria todas las peticiones HTTP mientras siguiera haciendo submit + // switchMap(peticionHttpLogin) // Cancelaria las peticiones anteriores si pulso de nuevo el submit + exhaustMap(peticionHttpLogin) // Ignoraria los demás intentos de submit si ya hay una petición HTTP en curso. +); + +submitForm$.subscribe(token => { + console.log(token); +}); \ No newline at end of file diff --git a/src/operadores/01-map-pluck-mapTo.ts b/src/operadores/01-map-pluck-mapTo.ts new file mode 100644 index 0000000..081e7a2 --- /dev/null +++ b/src/operadores/01-map-pluck-mapTo.ts @@ -0,0 +1,26 @@ +import { range, fromEvent } from 'rxjs'; +import { map, pluck, mapTo } from 'rxjs/operators'; + +// range(1,5).pipe( +// map( value => (value * 10).toString() ) +// ) +// .subscribe( console.log ) + +const keyup$ = fromEvent(document, 'keyup'); + +const keyupCode$ = keyup$.pipe( + map( event => event.code ) +); + +const keyupPluck$ = keyup$.pipe( + pluck('target','baseURI') +); + +const keyupMapTo$ = keyup$.pipe( + mapTo('Tecla presionada') +); + +keyup$.subscribe(console.log); +keyupCode$.subscribe( code => console.log('map', code)); +keyupPluck$.subscribe( code => console.log('pluck', code)); +keyupMapTo$.subscribe( code => console.log('mapTo', code)); \ No newline at end of file diff --git a/src/operadores/02-filter.ts b/src/operadores/02-filter.ts new file mode 100644 index 0000000..e875371 --- /dev/null +++ b/src/operadores/02-filter.ts @@ -0,0 +1,45 @@ +import { range, from, fromEvent } from 'rxjs'; +import { filter, map } from 'rxjs/operators'; + +// range(1,10).pipe( +// filter(val => val % 2 === 1 ) +// ).subscribe(console.log); + +range(20,30).pipe( + filter( (val, i) => { + console.log('index',i); + return val % 2 === 1; + }) +)//.subscribe(console.log); + +interface Personaje { + tipo: string; + nombre: string; +} + +const personajes: Personaje[] = [ + { + tipo: 'heroe', + nombre: 'Batman' + }, + { + tipo: 'heroe', + nombre: 'Robin' + }, + { + tipo: 'villano', + nombre: 'Joker' + } +]; + +from(personajes).pipe( + filter( val => val.tipo !== 'heroe') +).subscribe(console.log); + + +const keyup$ = fromEvent(document,'keyup').pipe( + map( event => event.code ), + filter( key => key === 'Enter' ), +); + +keyup$.subscribe(console.log); \ No newline at end of file diff --git a/src/operadores/03-tap.ts b/src/operadores/03-tap.ts new file mode 100644 index 0000000..8e3ae8f --- /dev/null +++ b/src/operadores/03-tap.ts @@ -0,0 +1,24 @@ +import { Observer, range } from 'rxjs'; +import { tap, map } from 'rxjs/operators'; + +const observer: Observer = { + next: value => console.log('siguiente [next]:', value), + error: error => console.warn('error [obs]:', error), + complete: () => console.info('completado [obs]') +} + +const numeros$ = range(1,5); + +numeros$.pipe( + // tap(observer) // El tap tambien acepta un observer + tap( x => { + console.log('antes', x); + return 100; // Dentro de un tap un return es ignorado + }), + map( val => val * 10), + tap({ + next: value => console.log('después', value), + complete: () => console.log('Se terminó todo') + }) +) +.subscribe( val => console.log('subs', val)); \ No newline at end of file diff --git a/src/operadores/04-lab-progressBar.ts b/src/operadores/04-lab-progressBar.ts new file mode 100644 index 0000000..3e83460 --- /dev/null +++ b/src/operadores/04-lab-progressBar.ts @@ -0,0 +1,53 @@ +import { fromEvent } from 'rxjs'; +import { map, tap } from 'rxjs/operators'; + +const texto = document.createElement('div'); +texto.innerHTML = ` +Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nisi maiores quas esse dicta, officia molestias in ipsa! Nihil, alias rerum excepturi cupiditate esse dolore vitae blanditiis provident facilis sed non!Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nisi maiores quas esse dicta, officia molestias in ipsa! Nihil, alias rerum excepturi cupiditate esse dolore vitae blanditiis provident facilis sed non!Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nisi maiores quas esse dicta, officia molestias in ipsa! Nihil, alias rerum excepturi cupiditate esse dolore vitae blanditiis provident facilis sed non!Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nisi maiores quas esse dicta, officia molestias in ipsa! Nihil, alias rerum excepturi cupiditate esse dolore vitae blanditiis provident facilis sed non!Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nisi maiores quas esse dicta, officia molestias in ipsa! Nihil, alias rerum excepturi cupiditate esse dolore vitae blanditiis provident facilis sed non! +

+Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nisi maiores quas esse dicta, officia molestias in ipsa! Nihil, alias rerum excepturi cupiditate esse dolore vitae blanditiis provident facilis sed non!Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nisi maiores quas esse dicta, officia molestias in ipsa! Nihil, alias rerum excepturi cupiditate esse dolore vitae blanditiis provident facilis sed non!Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nisi maiores quas esse dicta, officia molestias in ipsa! Nihil, alias rerum excepturi cupiditate esse dolore vitae blanditiis provident facilis sed non!Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nisi maiores quas esse dicta, officia molestias in ipsa! Nihil, alias rerum excepturi cupiditate esse dolore vitae blanditiis provident facilis sed non!Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nisi maiores quas esse dicta, officia molestias in ipsa! Nihil, alias rerum excepturi cupiditate esse dolore vitae blanditiis provident facilis sed non!Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nisi maiores quas esse dicta, officia molestias in ipsa! Nihil, alias rerum excepturi cupiditate esse dolore vitae blanditiis provident facilis sed non!Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nisi maiores quas esse dicta, officia molestias in ipsa! Nihil, alias rerum excepturi cupiditate esse dolore vitae blanditiis provident facilis sed non!Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nisi maiores quas esse dicta, officia molestias in ipsa! Nihil, alias rerum excepturi cupiditate esse dolore vitae blanditiis provident facilis sed non! +

+Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nisi maiores quas esse dicta, officia molestias in ipsa! Nihil, alias rerum excepturi cupiditate esse dolore vitae blanditiis provident facilis sed non!Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nisi maiores quas esse dicta, officia molestias in ipsa! Nihil, alias rerum excepturi cupiditate esse dolore vitae blanditiis provident facilis sed non!Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nisi maiores quas esse dicta, officia molestias in ipsa! Nihil, alias rerum excepturi cupiditate esse dolore vitae blanditiis provident facilis sed non!Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nisi maiores quas esse dicta, officia molestias in ipsa! Nihil, alias rerum excepturi cupiditate esse dolore vitae blanditiis provident facilis sed non!Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nisi maiores quas esse dicta, officia molestias in ipsa! Nihil, alias rerum excepturi cupiditate esse dolore vitae blanditiis provident facilis sed non!Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nisi maiores quas esse dicta, officia molestias in ipsa! Nihil, alias rerum excepturi cupiditate esse dolore vitae blanditiis provident facilis sed non!Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nisi maiores quas esse dicta, officia molestias in ipsa! Nihil, alias rerum excepturi cupiditate esse dolore vitae blanditiis provident facilis sed non! +

+Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nisi maiores quas esse dicta, officia molestias in ipsa! Nihil, alias rerum excepturi cupiditate esse dolore vitae blanditiis provident facilis sed non!Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nisi maiores quas esse dicta, officia molestias in ipsa! Nihil, alias rerum excepturi cupiditate esse dolore vitae blanditiis provident facilis sed non!Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nisi maiores quas esse dicta, officia molestias in ipsa! Nihil, alias rerum excepturi cupiditate esse dolore vitae blanditiis provident facilis sed non!Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nisi maiores quas esse dicta, officia molestias in ipsa! Nihil, alias rerum excepturi cupiditate esse dolore vitae blanditiis provident facilis sed non!Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nisi maiores quas esse dicta, officia molestias in ipsa! Nihil, alias rerum excepturi cupiditate esse dolore vitae blanditiis provident facilis sed non! +

+Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nisi maiores quas esse dicta, officia molestias in ipsa! Nihil, alias rerum excepturi cupiditate esse dolore vitae blanditiis provident facilis sed non!Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nisi maiores quas esse dicta, officia molestias in ipsa! Nihil, alias rerum excepturi cupiditate esse dolore vitae blanditiis provident facilis sed non!Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nisi maiores quas esse dicta, officia molestias in ipsa! Nihil, alias rerum excepturi cupiditate esse dolore vitae blanditiis provident facilis sed non!Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nisi maiores quas esse dicta, officia molestias in ipsa! Nihil, alias rerum excepturi cupiditate esse dolore vitae blanditiis provident facilis sed non!Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nisi maiores quas esse dicta, officia molestias in ipsa! Nihil, alias rerum excepturi cupiditate esse dolore vitae blanditiis provident facilis sed non!Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nisi maiores quas esse dicta, officia molestias in ipsa! Nihil, alias rerum excepturi cupiditate esse dolore vitae blanditiis provident facilis sed non!Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nisi maiores quas esse dicta, officia molestias in ipsa! Nihil, alias rerum excepturi cupiditate esse dolore vitae blanditiis provident facilis sed non!Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nisi maiores quas esse dicta, officia molestias in ipsa! Nihil, alias rerum excepturi cupiditate esse dolore vitae blanditiis provident facilis sed non! +

+Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nisi maiores quas esse dicta, officia molestias in ipsa! Nihil, alias rerum excepturi cupiditate esse dolore vitae blanditiis provident facilis sed non!Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nisi maiores quas esse dicta, officia molestias in ipsa! Nihil, alias rerum excepturi cupiditate esse dolore vitae blanditiis provident facilis sed non!Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nisi maiores quas esse dicta, officia molestias in ipsa! Nihil, alias rerum excepturi cupiditate esse dolore vitae blanditiis provident facilis sed non!Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nisi maiores quas esse dicta, officia molestias in ipsa! Nihil, alias rerum excepturi cupiditate esse dolore vitae blanditiis provident facilis sed non!Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nisi maiores quas esse dicta, officia molestias in ipsa! Nihil, alias rerum excepturi cupiditate esse dolore vitae blanditiis provident facilis sed non!Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nisi maiores quas esse dicta, officia molestias in ipsa! Nihil, alias rerum excepturi cupiditate esse dolore vitae blanditiis provident facilis sed non!Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nisi maiores quas esse dicta, officia molestias in ipsa! Nihil, alias rerum excepturi cupiditate esse dolore vitae blanditiis provident facilis sed non! +`; + +const body = document.body; +body.append(texto); + +const progressBar = document.createElement('div'); +// progressBar.setAttribute('class','progress-bar'); +progressBar.classList.add('progress-bar'); + +body.append(progressBar); + + +// Funcion que haga el calculo +const calcularPorcentajeScroll = (event) => { + // console.log(event.target.scrollingElement.scrollHeight); + // console.log(event.target.documentElement.scrollHeight); + + const { clientHeight, scrollHeight, scrollTop, } = event.target.documentElement + // console.log(clientHeight, scrollHeight, scrollTop) + + return ((scrollTop / (scrollHeight - clientHeight)) * 100) +} + +// Streams + +const scroll$ = fromEvent(document,'scroll'); +// scroll$.subscribe(console.log) + +const progress$ = scroll$.pipe( + map( calcularPorcentajeScroll ), + tap(console.log) +); + + +progress$.subscribe(porcentaje => { + progressBar.style.width = `${porcentaje}%`; +}); \ No newline at end of file diff --git a/src/operadores/05-reduce.ts b/src/operadores/05-reduce.ts new file mode 100644 index 0000000..d89e71f --- /dev/null +++ b/src/operadores/05-reduce.ts @@ -0,0 +1,22 @@ +import { interval } from 'rxjs'; +import { take, reduce, tap } from 'rxjs/operators'; + + +const numbers = [1,2,3,4,5]; + +const totalReducer = (acumulador: number, valorActual: number) => { + return acumulador + valorActual; +} + +const total = numbers.reduce(totalReducer, 0); +console.log('total arr', total); + +interval(500).pipe( + take(6), + tap(console.log), + reduce(totalReducer) +) +.subscribe({ + next: val => console.log('next:', val), + complete: () => console.log('complete') +}); \ No newline at end of file diff --git a/src/operadores/06-scan.ts b/src/operadores/06-scan.ts new file mode 100644 index 0000000..8366278 --- /dev/null +++ b/src/operadores/06-scan.ts @@ -0,0 +1,45 @@ +import { from } from 'rxjs'; +import { reduce,scan, map } from 'rxjs/operators'; + +const numeros = [1,2,3,4,5]; + +const totalAcumulador = (acc, cur) => acc + cur; + + +// Reduce +from(numeros).pipe( + reduce(totalAcumulador) +) +.subscribe(console.log); + +// Scan +from(numeros).pipe( + scan(totalAcumulador) +) +.subscribe(console.log); + +// Redux Alike +interface Usuario { + id?: string; + autenticado?: boolean; + token?: string; + edad?: number; + +} +const user: Usuario[] = [ + { id: 'alex', autenticado: false, token: null }, + { id: 'alex', autenticado: true, token: 'ABC' }, + { id: 'alex', autenticado: true, token: 'ABC123' }, +]; + +const state$ = from(user).pipe( + scan( (acc, cur) => { + return { ...acc, ...cur } + }, { edad: 33 }) +); + +const id$ = state$.pipe( + map(state => state) +); + +id$.subscribe(console.log) \ No newline at end of file diff --git a/src/operadores/07-take.ts b/src/operadores/07-take.ts new file mode 100644 index 0000000..d4569fd --- /dev/null +++ b/src/operadores/07-take.ts @@ -0,0 +1,14 @@ +import { of } from 'rxjs'; +import { take, tap } from 'rxjs/operators'; + +const numeros$ = of(1,2,3,4,5).pipe( + // tap(console.log) +); + +numeros$.pipe( + tap(console.log), + take(3) +).subscribe({ + next: val => console.log('next:', val), + complete: () => console.log('complete') +}) \ No newline at end of file diff --git a/src/operadores/08-first.ts b/src/operadores/08-first.ts new file mode 100644 index 0000000..35a7a81 --- /dev/null +++ b/src/operadores/08-first.ts @@ -0,0 +1,23 @@ +import { fromEvent } from 'rxjs'; +import { tap, first, map, pluck } from 'rxjs/operators'; + +const click$ = fromEvent(document,'click'); + +click$.pipe( + tap(console.log), + // map(event => ({ + // clientX: event.clientX, + // clientY: event.clientY + // })) + // Resumido => + map(({ clientX, clientY }) => ({ clientX, clientY })), + first(event => event.clientY >= 150), + // + // With Pluck + // + // pluck('clientY'), + // first(event => event >= 150) +).subscribe({ + next: val => console.log('next:', val), + complete: () => console.log('complete') +}); \ No newline at end of file diff --git a/src/operadores/09-takeWhile.ts b/src/operadores/09-takeWhile.ts new file mode 100644 index 0000000..89a93f8 --- /dev/null +++ b/src/operadores/09-takeWhile.ts @@ -0,0 +1,15 @@ +import { fromEvent } from 'rxjs'; +import { map, takeWhile } from 'rxjs/operators'; + +const click$ = fromEvent(document,'click'); + + + +click$.pipe( + map(({x,y}) => ({x,y})), + takeWhile(({ y }) => y <= 150, true) +) +.subscribe({ + next: (val) => console.log('next',val), + complete: () => console.log('complete') +}); \ No newline at end of file diff --git a/src/operadores/10-takeUntil-skip.ts b/src/operadores/10-takeUntil-skip.ts new file mode 100644 index 0000000..57165d8 --- /dev/null +++ b/src/operadores/10-takeUntil-skip.ts @@ -0,0 +1,25 @@ +import { interval, fromEvent } from 'rxjs'; +import { takeUntil, tap, skip } from 'rxjs/operators'; + +const boton = document.createElement('button'); +boton.innerHTML = 'Detener Timer'; +document.body.append(boton); + +const counter$ = interval(1000); +// const clickBtn$ = fromEvent(boton,'click'); +const clickBtn$ = fromEvent(boton,'click').pipe( + tap(() => console.log('tap antes del skip')), + skip(1), + tap(() => console.log('tap después del skip')), +); + +const sub = counter$.pipe( + // takeUntil(fromEvent(boton,'click')) + takeUntil(clickBtn$) +) +.subscribe({ + next: val => console.log('next:',val), + complete: () => console.log('completed') +}); + + diff --git a/src/operadores/11-distinct.ts b/src/operadores/11-distinct.ts new file mode 100644 index 0000000..8614f8a --- /dev/null +++ b/src/operadores/11-distinct.ts @@ -0,0 +1,27 @@ +import { of, from } from 'rxjs'; +import { distinct } from 'rxjs/operators'; + +const numeros$ = of(1,'1',1,3,3,2,2,4,4,5,3,1); + +numeros$.pipe( + distinct() // El distinct usa ===, se podria usar 1 como numero y 1 como string, por que no son iguales +) +.subscribe(console.log) + +interface Personaje { + nombre: string; +} + +const personajes: Personaje[] = [ + { nombre: 'Megaman' }, + { nombre: 'X' }, + { nombre: 'Zero' }, + { nombre: 'Dr. Willy' }, + { nombre: 'X' }, + { nombre: 'Megaman' }, + { nombre: 'Zero' }, +] + +from(personajes).pipe( + distinct(p => p.nombre) +).subscribe(console.log); \ No newline at end of file diff --git a/src/operadores/12-distinctUntilChanged.ts b/src/operadores/12-distinctUntilChanged.ts new file mode 100644 index 0000000..f825fea --- /dev/null +++ b/src/operadores/12-distinctUntilChanged.ts @@ -0,0 +1,27 @@ +import { of, from } from 'rxjs'; +import { distinctUntilChanged } from 'rxjs/operators'; + +const numeros$ = of(1,'1',1,3,3,2,2,4,4,5,3,1,'1'); + +numeros$.pipe( + distinctUntilChanged() // El distinct usa ===, se podria usar 1 como numero y 1 como string, por que no son iguales +) +.subscribe(console.log) + +interface Personaje { + nombre: string; +} + +const personajes: Personaje[] = [ + { nombre: 'Megaman' }, + { nombre: 'Megaman' }, + { nombre: 'Zero' }, + { nombre: 'Dr. Willy' }, + { nombre: 'X' }, + { nombre: 'X' }, + { nombre: 'Zero' }, +] + +from(personajes).pipe( + distinctUntilChanged( (ant, act) => ant.nombre === act.nombre ) +).subscribe(console.log); \ No newline at end of file diff --git a/src/operadores/13-distinctUntilKeyChanged.ts b/src/operadores/13-distinctUntilKeyChanged.ts new file mode 100644 index 0000000..0e724ba --- /dev/null +++ b/src/operadores/13-distinctUntilKeyChanged.ts @@ -0,0 +1,20 @@ +import { from } from 'rxjs'; +import { distinctUntilKeyChanged } from 'rxjs/operators'; + +interface Personaje { + nombre: string; +} + +const personajes: Personaje[] = [ + { nombre: 'Megaman' }, + { nombre: 'Megaman' }, + { nombre: 'Zero' }, + { nombre: 'Dr. Willy' }, + { nombre: 'X' }, + { nombre: 'X' }, + { nombre: 'Zero' }, +] + +from(personajes).pipe( + distinctUntilKeyChanged('nombre') +).subscribe(console.log); \ No newline at end of file