Curso
ANGULAR 4 + REDUX

www.devcode.la


¿QUIÉN SOY?

www.devcode.la

OBJETIVOS

www.devcode.la

1. Comprender los conceptos claves de Redux.
2. Aprender a implementar un arquitectura de datos escalable con Angular.
3. Implementar Redux + Angular.
4. Aprender a definir estados globales para la aplicación.
BENEFICIOS

www.devcode.la

1. Arquitectura escalable de datos.
2. Mayor control en el flujo de datos.
3. Programación Funcional.
4. Control de estados.
5. Estado global e immutable
MÓDULOS

www.devcode.la

Módulo 1: Introducción
Módulo 2: Ideas clave de Redux
Módulo 3: Redux + Angular
Módulo 4: Proyecto
REQUISITOS

www.devcode.la

- Conocimiento previo en Angular.
- VsCode
- Goolge Chrome
- Angular CLI
¡COMENCEMOS!

www.devcode.la

ARQUITECTURA DE DATOS

www.devcode.la

MODEL-VIEW-CONTROLLER

www.devcode.la


MODEL-VIEW-COLLECTION

www.devcode.la


VIEW-VIEWMODEL-MODEL

www.devcode.la


FLUX

www.devcode.la



www.devcode.la


¿POR QUÉ REDUX?

www.devcode.la

FRONTEND ACTUAL

www.devcode.la

Controlar el estado
- Respuestas del servidor
- Datos en cache
- Datos locales
- Estados de la UI
FRONTEND ACTUAL

www.devcode.la

"En cierto punto, ya no se entiende que esta pasando en la aplicación ya que perdiste control sobre el cuándo, el por qué y el cómo de su estado"
FRONTEND ACTUAL

www.devcode.la

Redux intenta hacer predecibles las mutaciones del estado
- Store = La única fuente de la verdad
- Actions = El estado es de solo lectura
- Reducers = Los cambios los realizan acciones puras
FRONTEND ACTUAL

www.devcode.la

¿REDUX JUNTO CON RX?

www.devcode.la

RX = manejar la complejidad de aplicaciones asíncronas
CONCEPTOS CLAVE DE REDUX

www.devcode.la

STATE

www.devcode.la

{
todos: [{
text: 'Aprender redux',
completed: true
}, {
text: 'Visitar nicobytes.com',
completed: false
}],
visibilityFilter: 'SHOW_COMPLETED'
}
ACTIONS

www.devcode.la

{ type: 'ADD_TODO', text: 'Ir a nadar a la piscina' }
{ type: 'TOGGLE_TODO', index: 1 }
{ type: 'SET_VISIBILITY_FILTER', filter: 'SHOW_ALL' }
REDUCERS

www.devcode.la

REDUCERS

www.devcode.la

function visibilityFilter(state = 'SHOW_ALL', action) {
if (action.type === 'SET_VISIBILITY_FILTER') {
return action.filter;
} else {
return state;
}
}
REDUCERS

www.devcode.la

function todos(state = [], action) {
switch (action.type) {
case 'ADD_TODO':
return state.concat([{ text: action.text, completed: false }]);
case 'TOGGLE_TODO':
return state.map((todo, index) =>
action.index === index ?
{ text: todo.text, completed: !todo.completed } :
todo
)
default:
return state;
}
}
LOS TRES PRINCIPIOS

www.devcode.la

Redux puede ser descrito en tres principios fundamentales
ÚNICA FUENTE DE LA VERDAD

www.devcode.la

El estado de toda tu aplicación esta almacenado en un árbol guardado en un único store.

www.devcode.la
https://css-tricks.com/learning-react-redux/

www.devcode.la

console.log(store.getState())
/* Imprime
{
visibilityFilter: 'SHOW_ALL',
todos: [
{
text: 'Considerar usar Redux',
completed: true
},
{
text: 'Mantener todo el estado en un solo árbol',
completed: false
}
]
}
*/
EL ESTADO ES DE SOLO LECTURA

www.devcode.la

La única forma de modificar el estado es emitiendo una acción, un objeto describiendo que ocurrió.

www.devcode.la

store.dispatch({
type: 'COMPLETE_TODO',
index: 1
});
store.dispatch({
type: 'SET_VISIBILITY_FILTER',
filter: 'SHOW_COMPLETED'
});
LOS CAMBIOS SE REALIZAN EN FUNCIONES PURAS

www.devcode.la

Para especificar como el árbol de estado es transformado por las acciones, se utilizan reducers puros.
REDUCERS

www.devcode.la

PROYECTO

www.devcode.la

- Sin Angular
- Sin ngrx
- Solo ts y redux

www.devcode.la


www.devcode.la

mkdir todos-redux

www.devcode.la

{
"name": "todos-redux",
"version": "1.0.0",
"description": "",
"author": "",
"license": "MIT",
"devDependencies": {
"ts-node": "3.3.0",
"typescript": "2.4.2"
},
"dependencies": {
"redux": "3.7.2"
}
}
package.json

www.devcode.la

{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"noEmitHelpers": true,
"sourceMap": true,
"lib": [
"es2016",
"dom"
]
},
"compileOnSave": false,
"buildOnSave": false
}
tsconfig.json

www.devcode.la

npm install

www.devcode.la

import { createStore } from 'redux';
console.log("Compile!!!")
index.ts

www.devcode.la

./node_modules/.bin/ts-node index.ts
run
¿QUÉ ES UN REDUCER?

www.devcode.la

PROYECTO

www.devcode.la


www.devcode.la


www.devcode.la

REDUCER
- Debe retornar el mismo tipo de data en el que es definido el reducer
- Por defecto debe retornar el estado original
- Debería recibir un estado inicial
- Crear un nuevo estado

www.devcode.la

interface Action{
type: string;
payload?: any;
}
ACTION

www.devcode.la

interface Reducer<T>{
(state: T, action: Action): T;
}
REDUCER
IMPLEMENTANDO ACTIONS

www.devcode.la


www.devcode.la

La única forma de modificar el estado es emitiendo una acción, un objeto describiendo que ocurrió

www.devcode.la

interface Action{
type: string;
payload?: any;
}

www.devcode.la

if(action.type === 'INCREMENT'){
return state + 1;
}
if(action.type === 'DECREMENT'){
return state - 1;
}

www.devcode.la

switch(action.type){
case 'INCREMENT':
return state + 1;
case 'DECREMENT':
return state - 1;
default:
return state;
}

www.devcode.la

switch(action.type){
case 'INCREMENT':
return state + 1;
case 'DECREMENT':
return state + 1;
case 'PLUS':
return state + action.payload;
default:
return state;
}

www.devcode.la
GUARDANDO NUESTRO ESTADO

www.devcode.la


www.devcode.la


www.devcode.la

{
todos: [{
text: 'Aprender redux',
completed: true
}, {
text: 'Visitar nicobytes.com',
completed: false
}],
visibilityFilter: 'SHOW_COMPLETED'
}

www.devcode.la

class Store<T>{
private state:T;
constructor(
private reducer: Reducer<T>,
initState: T
){
this.state = initState;
}
}

www.devcode.la

getState(): T{
return this.state;
}

www.devcode.la

dispatch(action: Action): void{
this.state = this.reducer(this.state, action);
}

www.devcode.la

let store = new Store<number>(reducer, 0);

www.devcode.la

store.dispatch({type: 'INCREMENT'});

www.devcode.la

console.log(store.getState());
IMPLEMENTANDO REDUX Y SUBSCRIBE

www.devcode.la


www.devcode.la

import {
Action,
Reducer,
createStore,
Store
} from 'redux';

www.devcode.la

interface PlusAction extends Action{
payload: number;
}

www.devcode.la

let reducer: Reducer<number> = (state: number = 0, action: Action)=>{
switch(action.type){
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state - 1
case 'PLUS':
return state + (<PlusAction>action).payload;
default:
return state;
}
}

www.devcode.la

let store: Store<number> =
createStore<number>(reducer);

www.devcode.la

console.log(store.getState());

www.devcode.la

store.dispatch(...)

www.devcode.la

store.subscribe(()=>{
console.log(store.getState());
})

www.devcode.la
¿QUÉ ES NGRX?

www.devcode.la

Reactive Extensions for Angular

www.devcode.la


www.devcode.la


ngrx/platform

www.devcode.la

Crear proyecto
ng new counter-app-ngrx

www.devcode.la

Instalar ngrx/store
npm install @ngrx/store --save
DEFINIR LAS ACCIONES

www.devcode.la


www.devcode.la


www.devcode.la

let reducer: Reducer<number> = (state: number = 0, action: Action) =>{
if(action === null) return state;
switch(action.type){
case 'INCREMENT':
return state + 1;
case 'DECREMENT':
return state - 1;
case 'PLUS':
return state + (<PlusAction>action).payload;
default:
return state;
}
}

www.devcode.la

import { Action } from '@ngrx/store';
export const INCREMENT = '[Counter] increment';
export const DECREMENT = '[Counter] decrement';
export const PLUS = '[Counter] plus';
export const RESET = '[Counter] reset';
export interface PlusAction extends Action{
payload: number;
}
DEFINIR EL REDUCER

www.devcode.la


www.devcode.la

let reducer: Reducer<number> = (state: number = 0, action: Action) =>{
if(action === null) return state;
switch(action.type){
case 'INCREMENT':
return state + 1;
case 'DECREMENT':
return state - 1;
case 'PLUS':
return state + (<PlusAction>action).payload;
default:
return state;
}
}

www.devcode.la

counter.reducer.ts
- El estado
- Su reducer
DEFINIR EL ESTADO DE LA APP

www.devcode.la


www.devcode.la

export interface AppState{
counter: number;
}

www.devcode.la

app.reducer.ts
- El estado
- Su reducer

www.devcode.la

StoreModule.forRoot(rootReducer),
Implementar el estado en la app

www.devcode.la

Leer estado desde un componente

www.devcode.la

Memoization
this.store.select()

www.devcode.la

Enviar acciones desde un componente
IMPLEMENTADO EN COMPONENTES

www.devcode.la

Redux DevTools Extension

www.devcode.la


www.devcode.la


Actions Creator

www.devcode.la

PROYECTO

www.devcode.la

www.devcode.la
PROYECTO

www.devcode.la
CREANDO LA ESTRUCTURA REDUX

www.devcode.la

www.devcode.la
ng new todo-app-ngrx --sytle scss

www.devcode.la
npm install @ngrx/store --save
npm install @ngrx/store-devtools --save

www.devcode.la


www.devcode.la

CREANDO COMPONENTES

www.devcode.la

www.devcode.la
- TodoComponent
- TodoListComponent
- NewTodoComponent
- FooterComponent
AGREGAR UNA TAREA

www.devcode.la
LISTAR TAREAS

www.devcode.la
ELIMINAR UNA TAREA

www.devcode.la
TOGGLE DE UNA TAREA

www.devcode.la
EDITAR DE UNA TAREA

www.devcode.la
AGREGANDO ESTILOS DE TODOMVC

www.devcode.la

www.devcode.la
npm install todomvc-common --save
npm install todomvc-app-css --save

www.devcode.la
@import '~todomvc-common/base.css';
@import '~todomvc-app-css/index.css';
FOOTER COMPONENT

www.devcode.la
SELECTORS

www.devcode.la
FILTERS

www.devcode.la
CREANDO RUTAS

www.devcode.la
SELECTOR PARA TAREAS

www.devcode.la
DEPLOY

www.devcode.la

www.devcode.la

CREAR PROYECTO

www.devcode.la
INSTALAR FIREBASE CLI

www.devcode.la

www.devcode.la
firebase init
ng build --target=production --base-href /
firebase deploy
COSAS POR HACER

www.devcode.la

www.devcode.la
https://github.com/DevcodeInc/Curso-de-Angular-4-y-Redux

www.devcode.la
- Limpiar todas las tareas hechas
- Toggle pero de todas las tareas
- Persistencia

www.devcode.la
https://github.com/nicobytes/todo-app-ngrx/

www.devcode.la
Un largo camino

www.devcode.la
GRACIAS...
Curso de Angular 4 y Redux
By devcodela
Curso de Angular 4 y Redux
Curso de Angular 4 y Redux
- 1,806