Нужно ли нам управлять состоянием в каждом приложении Angular? Может быть, не всегда, так как же элегантно реализовать управление состоянием для приложений, в которых оно нам действительно нужно? NgRx - одна из библиотек, используемых для управления состоянием приложения. Это реализация Redux для Angular.
Сначала давайте посмотрим на проблему, которую мы пытаемся решить, а затем разберемся с концепциями, лежащими в основе NgRx, и, наконец, перейдем к кодированию.
Хотите прочитать эту историю позже? Сохраните в Журнале.
Состояние приложения
Что такое состояние приложения? Теоретически это вся память приложения, но, как правило, это данные, полученные через вызовы API, пользовательские вводы, состояние пользовательского интерфейса презентации, настройки приложения и т. Д. Проще говоря, это данные, которые могут различать два экземпляра то же приложение. Простым конкретным примером состояния приложения может быть список клиентов, поддерживаемый в приложении.
Проблема, которую мы пытаемся решить
Для простоты предположим, что у нас есть список клиентов в приложении, и это состояние, которым мы пытаемся управлять. Некоторые вызовы API и вводимые пользователем данные могут изменять состояние (т.е. список), добавляя или удаляя клиентов. Изменение состояния должно быть отражено в пользовательском интерфейсе и других зависимых компонентах. Конечно, в этом конкретном случае мы можем иметь глобальную переменную для хранения списка, а затем добавлять / удалять клиентов из / в него, а затем писать код для обновления пользовательского интерфейса и зависимостей. Но в этом дизайне есть много подводных камней, которые не являются предметом рассмотрения в этой статье. NgRx - отличный дизайн для большинства требований государственного управления. В самом деле, он немного шаблонен по сравнению с другими конкурентами, такими как NGXS, Akita или простой RxJS.
Управление состоянием приложения NgRx
Давайте посмотрим на реализацию NgRx - нужно понять несколько компонентов.
- Магазин. Магазин - это то, что хранит состояние приложения.
- Действие: уникальное событие, отправляемое компонентами и службами, которое описывает, как следует изменить состояние. Например, «Добавить клиента» может быть действием, которое изменит состояние (т. Е. Добавит нового клиента в список).
- Редуктор: все изменения состояния происходят внутри редуктора; он реагирует на действие и на основе этого действия создает новое неизменяемое состояние и возвращает его в хранилище.
- Селектор: Селектор - это функция, используемая для получения части состояния из магазина.
- Эффект: механизм, который отслеживает отправленные действия в наблюдаемом потоке, обрабатывает ответ сервера и немедленно или асинхронно возвращает редуктору новые действия для изменения состояния. Обратите внимание, что мы не используем эффект в этом примере приложения.
Это взаимодействие между этими компонентами в NgRx.
Пользовательский интерфейс и другие компоненты отправляют действия. Действия могут иметь полезную нагрузку, которая должна изменять состояние. Редуктор создает новое состояние, описанное указанным действием, и возвращает его в хранилище. Как только магазин обновится с новым состоянием, он уведомит пользовательский интерфейс и все зависимые компоненты. Каждый пользовательский интерфейс реагирует на изменение состояния, и его представление обновляется, чтобы отразить изменения.
Это представление нашего примера состояния, списка клиентов.
Элемент управления пользовательского интерфейса «Добавить нового клиента» отправляет действие AddCustomer с новым клиентом в качестве полезной нагрузки в этом действии. Редуктор выполняет действие AddCustomer, а новый клиент приходит в качестве полезной нагрузки и создает новый список с существующими клиентами. Затем он обновит магазин, добавив в него новый список клиентов. Наконец, он уведомит пользовательский интерфейс и отобразит новый список.
Наконец, кодирование
Предпосылки:
Убедитесь, что установлены Node.js и Angular CLI. Вы можете запустить ng --version
, чтобы узнать, какие версии установлены на вашем компьютере.
- Nodejs: https://nodejs.org/
- Angular CLI: https://cli.angular.io/
1. Создайте приложение Angular с помощью Angular CLI.
ng new angular-state-management
Выберите «Нет» и «CSS».
? Would you like to add Angular routing? No ? Which stylesheet format would you like to use? CSS
Он создаст все необходимые файлы и установит зависимости. Это займет несколько минут.
2. Загрузите проект в IDE (я использую IntelliJ IDEA).
3. Запустите приложение.
Давайте запустим приложение, созданное с помощью интерфейса командной строки, чтобы убедиться, что все создано правильно.
npm start
Убедитесь, что приложение работает на http: // localhost: 4200 /
4. Установите NgRx.
Давайте установим NgRx сейчас (вы можете использовать новое окно терминала или выйти из того, в котором вы находитесь, нажав клавиши ctrl + C)
npm install @ngrx/store --save
Кроме того, вы можете запустить ng add @ngrx/store
, если у вас версия CLI 6+.
Обратите внимание, что @ngrx/store
был добавлен в файл package.json.
5. Создайте модель клиента.
Теперь мы начинаем добавлять код. Во-первых, давайте создадим customer
файл с помощью интерфейса командной строки.
ng g class models/customer
Как еще один вариант, вы можете добавить его с помощью редактора.
Файл customer.ts был создан в папке src \ app \ models. Добавьте к нему свойство name
.
export class Customer { public name: String = ''; }
6. Добавить действия
Теперь мы собираемся добавить код, связанный с NgRx. Как показывает наша диаграмма выше, состояние, которым мы собираемся управлять, - это набор клиентов. Мы можем изменить сбор данных о состоянии клиента с помощью действий. В этом конкретном случае у нас есть два действия, которые могут изменить состояние:
AddCustomer
RemoveCustomer
Создайте файл TypeScript customer.actions.ts в папке src / app для действий клиентов с помощью редактора.
Добавьте следующий код в файл customer.actions.ts:
import {Action} from '@ngrx/store'; export enum CustomerActionTypes { Add = '[Customer Component] Add', Remove = '[Customer Component] Remove' } export class ActionEx implements Action { readonly type; payload: any; } export class CustomerAdd implements ActionEx { readonly type = CustomerActionTypes.Add; constructor(public payload: any) { } } export class CustomerRemove implements ActionEx { readonly type = CustomerActionTypes.Remove; constructor(public payload: any) { } }
7. Добавьте клиентского редуктора
Добавим редуктор; все изменения состояния происходят внутри редуктора на основе выбранного «действия». Если состояние изменяется, редуктор создаст нового клиента, а не изменяет существующий список клиентов. В случае изменения состояния редуктор всегда будет возвращать вновь созданный объект списка клиентов.
Создайте файл TypeScript customer.reducer.ts в папке src / app для CustomerReducer с помощью редактора.
import {ActionEx, CustomerActionTypes} from './customer.actions'; export const initialState = []; export function CustomerReducer(state = initialState, action: ActionEx) { switch (action.type) { case CustomerActionTypes.Add: return [...state, action.payload]; case CustomerActionTypes.Remove: return [ ...state.slice(0, action.payload), ...state.slice(action.payload + 1) ]; default: return state; } }
8. Добавьте магазин NgRx в приложение.
Давайте добавим в приложение модуль магазина.
Добавьте импорт в app.module.ts:
import { StoreModule } from '@ngrx/store'; import { CustomerReducer } from './customer.reducer';
И модуль магазина
StoreModule.forRoot({ customers: CustomerReducer })
Теперь app.module.ts должен выглядеть так.
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { AppComponent } from './app.component'; import { StoreModule } from '@ngrx/store'; import { CustomerReducer } from './customer.reducer'; @NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule, StoreModule.forRoot({ customers: CustomerReducer }) ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
9. Добавьте компонент пользовательского интерфейса для просмотра клиентов.
ng g c CustomersView
Добавьте код в файл customers-view.compoment.ts .
Объявите клиентов, которые наблюдаются на вершине класса
customers: Observable<Customer[]>;
И изменим конструктор:
constructor(private store: Store<{ customers: Customer[] }>) { this.customers = store.pipe(select('customers')); }
Импортируйте необходимые зависимости вверху:
import {Customer} from '../models/customer'; import {select, Store} from '@ngrx/store'; import {Observable} from 'rxjs';
Теперь файл customers-view.compoment.ts должен выглядеть так:
import {Component} from '@angular/core'; import {Customer} from '../models/customer'; import {Observable} from 'rxjs'; import {select, Store} from '@ngrx/store'; @Component({ selector: 'app-customers-view', templateUrl: './customers-view.component.html', styleUrls: ['./customers-view.component.css'] }) export class CustomersViewComponent { customers: Observable<Customer[]>; constructor(private store: Store<{ customers: Customer[] }>) { this.customers = store.pipe(select('customers')); } }
Добавьте следующий код в файл customers-view.compoment.html,
<h4>List of Customers</h4> <ul class="customers"> <li *ngFor="let customer of customers | async; let i=index"> <span >{{i+1}}.</span> {{customer.name}} </li> </ul>
И чтобы сделать список более приятным, добавьте код CSS в файл customers-view.compoment.css.
.customers { margin: 0 0 2em 0; list-style-type: none; padding: 0; width: 15em; } .customers li { background-color: steelblue; color: white; border-radius: 4px; padding: 4px; margin: 2px; }
10. Добавьте элементы управления пользовательского интерфейса, чтобы добавить новых клиентов.
ng g c CustomerAdd
customer-add.component.ts
import {Component} from '@angular/core'; import {select, Store} from '@ngrx/store'; import {Customer} from '../models/customer'; import {Observable} from 'rxjs'; import {CustomerAdd} from '../customer.actions'; @Component({ selector: 'app-customer-add', templateUrl: './customer-add.component.html', styleUrls: ['./customer-add.component.css'] }) export class CustomerAddComponent { customers: Observable<Customer[]>; constructor(private store: Store<{ customers: Customer[] }>) { this.customers = store.pipe(select('customers')); } AddCustomer(customerName: string): void { const customer = new Customer(); customer.name = customerName; this.store.dispatch(new CustomerAdd(customer)); } }
И файл c ustomer-add.component.html.
<h4>Add New Customer</h4> <input #box ><button (click)="AddCustomer(box.value)">Add</button>
11. Обновите компонент приложения с помощью компонентов CustomerView и CustomerAdd.
Теперь обновите файл app.component.html, удалив содержимое по умолчанию и вставив компоненты app-customers-view и app-customer-add.
Файл app.component.html должен выглядеть следующим образом:
<div style="text-align:center"> <h1> Welcome to {{ title }}! </h1> </div> <app-customers-view></app-customers-view> <app-customer-add></app-customer-add>
12. Запустите приложение.
Наш код готов! Давайте снова запустим приложение (если оно еще не запущено).
npm start
13. Подключите действие «Удалить клиента».
Мы собираемся добавить кнопку справа от ярлыка клиента и подключить отправку действия CutomerRemove
.
Добавьте в наш файл customers-view.compoment.ts следующую строку:
removeCustomer(customerIndex): void { this.store.dispatch(new CustomerRemove(customerIndex)); }
Теперь файл customers-view.compoment.ts выглядит следующим образом:
import {Component} from '@angular/core'; import {Customer} from '../models/customer'; import {Observable} from 'rxjs'; import {select, Store} from '@ngrx/store'; import {CustomerRemove} from '../customer.actions'; @Component({ selector: 'app-customers-view', templateUrl: './customers-view.component.html', styleUrls: ['./customers-view.component.css'] }) export class CustomersViewComponent { customers: Observable<Customer[]>; constructor(private store: Store<{ customers: Customer[] }>) { this.customers = store.pipe(select('customers')); } removeCustomer(customerIndex) { this.store.dispatch(new CustomerRemove(customerIndex)); } }
И в файл customers-view.compoment.html добавьте следующее:
<button style="float: right" (click)="removeCustomer(i)">Remove</button>
customers-view.compoment.html теперь выглядит так:
<h4>List of Customers</h4> <ul class="customers"> <li *ngFor="let customer of customers | async; let i=index"> <span >{{i+1}}.</span> {{customer.name}} <button style="float: right" (click)="removeCustomer(i)">Remove</button> </li> </ul>
Вот финальная версия приложения.
Надеюсь, эта статья поможет вам лучше понять NgRx и использовать его с приложением Angular.
Давай аплодируем, если тебе это действительно нравится.
📝 Сохраните эту историю в Журнале.
👩💻 Просыпайтесь каждое воскресное утро и слушайте самые интересные истории из области технологий, ожидающие вас в вашем почтовом ящике. Прочтите информационный бюллетень« Примечательно в технологиях .