21/11/2019 | Consejos tecnológicos,Desarrollo de software

Introducción a NgRx

La semana pasada escribimos un post sobre Redux y conocimos las diferentes partes que lo forman: Store, Actions y Reducers. En este post realizaremos una pequeña introducción a NgRx, un framework para introducir el flujo de desarrollo Flux a una aplicación Angular. Si quieres echarle un vistazo a las nociones básicas de Redux antes de lanzarte con esa introducción, pulsa aquí para ir al anterior post.

Ahora explicaremos paso a paso cómo hacer un ejemplo simple, pero práctico, para empezar a utilizar NgRx.

1 – Instalación

Empezaremos creando un proyecto de Angular nuevo e instalando el paquete @ngrx/store a través de npm

  • ng new ngrx-tri-training
  • npm install –save @ngrx/store


Si queremos cerciorarnos de que el proyecto se ha creado correctamente, siempre podemos ejecutarlo e iniciarlo en nuestro navegador

  • npm start -> localhost:4200


Si todo es correcto, deberíamos ver la landing page básica de Angular dependiendo de la versión de Angular-CLI que tengamos instalada.

2 – Crear Modelo

En este tutorial vamos a crear una aplicación para leer, crear y eliminar “links”. Así que empezaremos creando un modelo de “Link”, que estará formado por un nombre y una url.

src/app/models/link.model.ts


export interface Link {

name: string;

url: string;
}



3 – Crear Acción

Ahora crearemos las acciones. Como vimos en la introducción del blog anterior, las acciones son objetos que nos describen lo que está ocurriendo. En nuestro caso, para que sea más fácil identificarlos, crearemos una clase para cada acción y tener así un tipado más claro.

src/app/actions/link.actions.ts

import { Action } from "@ngrx/store";
import { Link } from "../models/link.model";

export const ADD_LINK = "[LINK] Add";
export const REMOVE_LINK = "[LINK] Remove";

export class AddLink implements Action {
  readonly type = ADD_LINK;
  constructor(public payload: Link) { }
}
export class RemoveLink implements Action {
  readonly type = REMOVE_LINK;
  constructor(public payload: number) { }
}

export type Actions = AddLink | RemoveLink

Hay que destacar que cada acción tiene un tipo “type” que describe la acción, y un “payload” con los datos de dicha acción. Recordad que el payload puede ser opcional dependiendo de la acción.

4 – Crear Reducers

Después, necesitaremos un reducer que trate la acción y nos devuelva el estado deseado tras dicha acción.

src/app/reducers/link.reducer.ts

import { Action } from "@ngrx/store";
import { Link } from "../models/link.model";
import * as LinkActions from "../actions/link.actions";

const initialState: Link = {
   name: "Google Link",
   url: "http://google.com"
}

export function reducer(state: Link[] = [initialState], action: LinkActions.Actions) {

   switch (action.type) {
       case LinkActions.ADD_LINK:
           return [...state, action.payload];
       default:
           return state;
   }
}

Como vemos en el código, dependiendo del tipo de la acción lanzada, el reducer decidirá cómo tratar los datos.

En este caso, y para mejor visualización de los datos en el ejemplo que veremos en un rato, se inicializa el “state” con un link predefinido. Esto no es necesario ya que normalmente inicializamos este estado a través de otros canales más convencionales; como podría ser un API.

5 – Crear AppState

src/app/app.state.ts

import { Link } from "./models/link.model";

export interface AppState {
   readonly links: Link[];
}

Es importante que tengamos en cuenta que hemos denominado “links”, a la clave que contendrá toda la información de los links, ya que lo utilizaremos a continuación.

6 – Actualizar AppModule

src/app/app.module.ts

// Other imports
import { StoreModule } from "@ngrx/store";
import { reducer } from "./reducers/link.reducer";

@NgModule({
 // Other code
 imports: [
   BrowserModule,
   StoreModule.forRoot({
     links: reducer
   })
 ],
 // Other code

Ahora introducimos el “StoreModule” en el módulo de la aplicación, y asociamos el reducer creado anteriormente a la clave “links” que hemos creado en el punto anterior, de ahí su importancia.

7 – Crear Componentes
  • ng g c components/read
  • ng g c components/create


Ahora crearemos dos nuevos componentes; read y create.

Dependiendo del Angular-cli que utilicéis, se nos añadirán directamente al archivo “app.module.ts”. En caso de que no se nos añadan, lo haremos manualmente.

8 – Leer de la store

Tras esto, vamos a modificar el componente “read” para mostrar los datos que tengamos en la store.

src/app/components/read/read.component.ts

import { Component, OnInit } from "@angular/core";
import { Observable } from "rxjs";
import { Store } from "@ngrx/store";
import { Link } from "./../../models/link.model";
import { AppState } from "./../../app.state";

@Component({
   selector: "app-read",
   templateUrl: "./read.component.html",
   styleUrls: ["./read.component.scss"]
})
export class ReadComponent implements OnInit {

   public links: Observable<Link[]>

   constructor(
       private store: Store<AppState>
   ) {
       this.links = store.select("links");
   }

   ngOnInit() {
   }

}

Nos crearemos una variables “links” que sera un observable de array de “Link”. En esta variable guardaremos los datos de los links guardados en la store a través de la funcion “store.select()”. Es importante también ver que a esta función le hemos pasado el literal “links”, que es el mismo nombre que le dimos a la clave en AppState y al objeto pasado al StoreModule en el archivo “app.module.ts”.

src/app/components/read/read.component.html


<div *ngIf="links">

<h3>Links</h3>

<ul>

<li *ngFor="let link of links | async">
           <a [href]="link.url" target="_blank">{{ link.name }}</a>
       </li>

   </ul>

</div>

Por otra parte, en el archivo .html del componente, creamos una lista simple de todos los links guardados. Lo más importante de este *ngFor es el pipe “| async”, que identificara los cambios en el state automáticamente y cambiara la lista de forma acorde.

src/app/app.component.html

<app-create></app-create>
<app-read></app-read>

Por ultimo, modificamos todo el archivo “app.component.html” e introducimos los tags de los nuevos componentes creados. Si todo funciona correctamente, deberíamos ver la lista inicializada de links.

9 – Escribir en la Store

src/app/components/create/create.component.ts

import { Component, OnInit } from "@angular/core";
import { Store } from "@ngrx/store";
import { AppState } from "./../../app.state";
import { Link } from "./../../models/link.model"
import * as LinkActions from "./../../actions/link.actions";
import { Observable } from "rxjs";

@Component({
 selector: "app-create",
 templateUrl: "./create.component.html",
 styleUrls: ["./create.component.scss"]
})
export class CreateComponent implements OnInit {

 constructor(
   private store: Store<AppState>
 ) { }

 ngOnInit() {
 }

 public addLink(name: string, url: string) {
   this.store.dispatch(new LinkActions.AddLink({ name: name, url: url }));
   // this.store.dispatch({ type: LinkActions.ADD_LINK, payload: { name: name, url: url } });
 }

}

src/app/components/create/create.component.html


<div>
   <input type="text" placeholder="name" #name>
   <input type="text" placeholder="url" #url>

   <button (click)="addLink(name.value, url.value)">Add a Link</button>
</div>

Ahora empezaremos a añadir links a la store. Para ello creamos dos input que tomen el nombre y la url del link, y pasará esos datos al componente con la función “addLink()”.

En el componente, con esos datos, disparamos la acción a través del método “store.dispatch()”. Como se ve en el código, a este distpatch le pasaremos la acción, que tiene un tipo inferido y el payload con los datos de el link. Más tarde el reducer reconocerá el tipo, y añadirá el nuevo link al array de la store.

10 – Borrar de la store

Por último vamos a añadir la posibilidad de borrar links de la lista. Para ellos crearemos una función que, a través del index de la lista, obtenga el link seleccionado y lo borre de esta.

src/app/components/read/read.component.html


<li (click)="deleteLink(i)" *ngFor="let link of links | async; let i = index">

src/app/components/read/read.component.ts

import * as LinkActions from "./../../actions/link.actions";

p
ublic deleteLink(index: number) {
       this.store.dispatch(new LinkActions.RemoveLink(index));
}


Al igual que en el caso de añadir libros, lanzaremos una acción, pero en este caso con el nuevo tipo y un payload diferente.

src/app/reducers/link.reducer.ts

       case LinkActions.REMOVE_LINK:
           state.splice(action.payload, 1);
           return state;


Hay que recordar añadir el nuevo tipo en el reducer, para que este pueda tratar los datos cuando se lanze la acción con el tipo “REMOVE_LINK”.

Si se ha realizado todo correctamente, una vez añadido un link, podremos eliminarlo de la lista tocando en cualquier parte de la columna en la que está escrito el link. Si clicamos en el link, este se abrirá en una pestaña nueva, así que recordad clicar a la derecha de este.

Conclusiones

Como hemos podido ver, se trata de un tutorial bastante sencillo en el que vemos una idea general de cómo funciona NgRx, este framework de Angular.

En un aplicación real deberíamos conectar este estado a una base de datos, y cargar todos los datos a través de una API. Os animo a que investiguéis más sobre esta librería en la documentación aquí.

Espero que este tutorial de NgRx os aclare algunas idea y os haya gustado. Si tenéis alguna consulta, no dudéis en preguntar.

¡Un saludo!


Compartir en:

Relacionados