Hoy vamos a aprender a introducir en el uso de REDUX gestión de estados en una aplicación web Angular con la biblioteca NGRX
El equivalente de REDUX para Angular es NGRX.
También le puede interesar: “Unimedia nombrada líder tecnológico por Clutch”
Además, nuestro equipo de desarrolladores es experto en crear software a medida según los requisitos del proyecto.
Veremos paso a paso cada uno de los componentes básicos de NGRX
Tienda
The Store es un contenedor de estado controlado diseñado para ayudar a escribir aplicaciones escalables y de alto rendimiento sobre Angular.
Efectos
Los Efectos utilizan los flujos para proporcionar nuevas fuentes de acciones. Modificar el estado en función de interacciones externas como solicitudes de red, mensajes de socket web y eventos temporales. Por lo tanto, son el método preferido para obtener datos y mantener parte de la lógica de la aplicación.
Selectores
Los selectores son funciones puras que se utilizan para obtener porciones del estado de la tienda. Así, seleccionan piezas de información del Almacén para que sean consumidas por otras partes de la lógica de la aplicación.
Reductores
Los Reductores en NgRx son responsables de manejar las transiciones de un estado al siguiente estado en tu aplicación. Las funciones reductoras gestionan estas transiciones determinando qué acciones gestionar en función del tipo de acción. En otras palabras, reciben acciones con una carga de datos y cambian el estado almacenando los nuevos datos según se desee.
Después de entender los bloques de construcción básicos de NGRX State Management, ahora vamos a construir una pequeña aplicación de chat de ejemplo con NGRX State Management.
Dado que, ya hemos creado una aplicación de chat antes En el post anterior aquí . Como resultado, vamos a introducir la gestión de estado NGRX en él para la actualización de la interfaz de usuario, mientras que el envío / recepción del mensaje.
1. Configurar el proyecto
/** Clone Repo */
git clone https://github.com/unimedia-technology/amplify-chat-angular.git
/** Enter into project directory */
cd amplify-chat-angular
/** Install the dependencies */
npm i
/** Create a new git branch */
git checkout ngrx
2. Instalar las dependencias de NGRX
ng add @ngrx/store @ngrx/effects @ngrx/component
3. Crear acciones
Aquí, vamos a crear 4 acciones que son necesarias para gestionar el estado de la aplicación de chat.
Archivo: store/actions/actions.ts
import { createAction, props } from '@ngrx/store';
export const loadMessages = createAction('[Chat] Load Messages', props<{ channelId: string }>());
export const loadMessagesSuccess = createAction('[Chat] Load Messages Success', props<{ messages: any[] }>());
export const sendMessage = createAction('[Chat] Send Message', props<{ message }>());
export const addMessageToList = createAction('[Chat] Add Message To List', props<{ message }>());
4. Crear efectos
Los efectos proporcionan una forma de interactuar con esos servicios y aislarlos de los componentes.
Utilización
Los efectos son útiles, Si desea manejar tareas como la obtención de datos, tareas de larga duración que producen múltiples eventos, y otras interacciones externas.
Nota: Para recordar, en la mayoría de los escenarios, enviará acción(es), pero no es obligatorio enviar siempre acción(es).
Para escribir efectos sin enviar la(s) acción(es), pase el segundo parámetro a las funciones createEffect()
con { dispatch: false }
Archivo: state/effects/effects.ts
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { Injectable } from '@angular/core';
import { map, switchMap } from 'rxjs/operators';
import * as chatActions from '../actions/actions';
import { from } from 'rxjs';
import { APIService } from '../../API.service';
@Injectable()
export class ChatEffects {
constructor(
private actions$: Actions,
private api: APIService,
) { }
/** Load the List of Messages */
loadMessages$ = createEffect(() => this.actions$.pipe(
ofType(chatActions.loadMessages),
switchMap(({ channelId }) => {
return from(this.api.MessagesByChannelId(channelId)).pipe(
map((res: any) => {
return chatActions.loadMessagesSuccess({ messages: res.items });
}),
);
})
));
/** Send message and call no actions */
sendMessage$ = createEffect(() => this.actions$.pipe(
ofType(chatActions.sendMessage),
switchMap(({ message }) => {
return from(this.api.CreateMessage(message));
})
), { dispatch: false });
}
5. Crear reductores
Cada función reductora toma el último Action
enviado, el estado actual, y determina si devuelve un estado recién modificado o el estado original. Ahora, en el siguiente ejemplo te guiaremos sobre cómo escribir funciones reductoras, registrarlas en tu Store
, y componer estados de funciones.
Archivo: store/reducers/reducers.ts
import { Action, createReducer, on } from '@ngrx/store';
import * as chatActions from '../actions/actions';
export interface IChatState {
messages: any[];
}
/** Initial State */
export const initialState: IChatState = {
messages: [],
};
export function chatReducer(state: IChatState | undefined, action: Action): IChatState {
return reducer(state, action);
}
const reducer = createReducer<IChatState>(
initialState,
/** Loaded Messafes */
on(chatActions.loadMessagesSuccess, (state, { messages }) => ({
...state,
messages
})),
/** Add message to the messages array */
on(chatActions.addMessageToList, (state, { message }) => ({
...state,
messages: [...state.messages, message]
})),
);
6. Crear selectores
El selector que hemos creado aquí devolverá el observable de todos los mensajes
Archivo: store/selectors/selectors.ts
import { createSelector } from '@ngrx/store';
export const selectChatState = (state) => state;
export const selectMessages = createSelector(
selectChatState,
(state) => state.chat.messages
);
7. Gestionar el Estado
En esta sección, vamos a ver cuales son todos los eventos donde necesitamos actualizar el estado:
- Cargar la aplicación
- Enviar un mensaje
- Recibir un mensaje
Archivo: app.component.ts
import { Component, OnInit } from '@angular/core';
import { NavigationEnd, Router, RouterEvent } from '@angular/router';
import { select, Store } from '@ngrx/store';
import { Observable } from 'rxjs';
import { delay } from 'rxjs/operators';
import { APIService } from './API.service';
import { addMessageToList, loadMessages, sendMessage } from './store/actions/actions';
import { IChatState } from './store/reducers/reducer';
import { selectMessages } from './store/selectors/selectors';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
title = 'amplify-chat-angular';
username: string;
messages: Observable<any[]>;
constructor(
private api: APIService,
private router: Router,
private store: Store<IChatState>
) { }
ngOnInit(): void {
this.router.events.subscribe((events: RouterEvent) => {
if (events instanceof NavigationEnd) {
const qParams = this.router.routerState.snapshot.root.queryParams;
if (qParams && qParams.user) {
this.username = qParams.user;
} else {
this.router.navigate(['/'], { queryParams: { user: 'Dave' } });
}
}
});
this.listMessages();
this.onCreateMessage();
}
send(event, inputElement: HTMLInputElement): void {
event.preventDefault();
event.stopPropagation();
const input = {
channelID: '2',
author: this.username.trim(),
body: inputElement.value.trim()
};
this.store.dispatch(sendMessage({ message: input }));
inputElement.value = '';
}
listMessages(): void {
this.store.dispatch(loadMessages({ channelId: '2' }));
this.messages = this.store.pipe(
select(selectMessages),
delay(10)
);
}
onCreateMessage(): void {
this.api.OnCreateMessageListener.subscribe(
{
next: (val: any) => {
console.log(val);
this.store.dispatch(addMessageToList({ message: val.value.data.onCreateMessage }));
}
}
);
}
}
Explicación
listMessages
envía la acciónloadMessages
conchannelId
para recuperar todos los mensajes.- Cuando el usuario envía un mensaje, se llama a la acción
sendMessage
, que envía el mensaje. - Cuando el usuario recibe un mensaje, se envía la acción
addMessageToList
y se añade el mensaje a la listamessages
.
8. Crear plantilla
En esta sección, vamos a utilizar, ngrxPush
tubería de @ngrx/component
.
El tubo ngrxPush
sirve como sustituto del tubo async
.
ngrxPush
contiene un manejo inteligente de la detección de cambios que nos permitirá funcionar tanto en modo de zona llena como de zona vacía sin ningún cambio en el código.
Uso:
La tubería ngrxPush
se suministra a través de ReactiveComponentModule
. Por lo tanto, para utilizarlo, añada el ReactiveComponentModule
a la dirección imports
de su NgModule.
Archivo: app.component.html
<div id="root">
<div class="container">
<div class="messages">
<div class="messages-scroller">
<ng-container *ngIf="messages$ | async as messages">
<ng-container *ngFor="let message of messages">
<div [ngClass]="message.author === username ? 'message me' : 'message'">
{{message.body}}
</div>
</ng-container>
</ng-container>
</div>
</div>
<div class="chat-bar">
<div class="form">
<input #messageInput type="text" name="messageBody" placeholder="Type your message here" value="
(keyup.enter)="send($event, messageInput)" />
</div>
</div>
</div>
</div>
Eso es todo, ahora estamos gestionando el estado utilizando NGRX.
¿Te ha gustado leer esto y te gustaría aprender más sobre angular y redux?, aquí tienes otro gran artículo, ¡échale un vistazo!
Unimedia Technology
Aquí en Unimedia Technology tenemos un equipo de desarrolladores Angular que pueden desarrollar sus más desafiantes Web Dashboards y Web apps.