Существует множество курсов и учебных пособий по Angular и NgRx, которые мы можем пройти, но всем нам трудно найти пример проекта, который мы можем создать для отработки основ.

Итак, давайте попробуем создать приложение-счетчик с небольшим количеством дополнительных функций, где мы также можем устанавливать и сбрасывать число, используя последние версии Angular, NgRx и Material.

Сначала займемся всеми настройками…

  1. Прежде всего, у нас должна быть последняя версия node.js и редактор на ваш выбор, например. https://code.visualstudio.com/.
  2. Установите последнюю версию Angular CLI «npm install -g @angular/cli»
  3. Теперь вы можете создать новый проект «ng new counter-app». Он попросит вас выбрать, хотите ли вы использовать маршрутизацию по умолчанию или нет, и таблицу стилей, которую вы хотите использовать.
  4. После этого мы должны установить все зависимости. В нашем проекте нам понадобятся @angular/material, @ngrx/store и @ngrx/store-devtools.
  5. Для материала сейчас нам нужна всего одна команда, которая автоматизирует все шаги за вас. «Добавить @angular/material».
  6. Некоторые люди могут получить ошибку в отношении разрешения powershell. Если вы столкнулись с этой проблемой, сделайте следующее: «Удалите ng.ps1 из каталога C:\Users\%username%\AppData\Roaming\npm\, затем попробуйте очистить кэш npm в C:\Users\%username %\AppData\Roaming\npm-cache\”
  7. Даже для магазина теперь нам нужна только одна команда, чтобы выполнить все шаги «ng add @ngrx/store». После этогомы должны установить Redux devtools, который пригодится в процессе отладки «npm install @ngrx/store-devtools — сохранить»
  8. Затем удалите все из app.component и создайте компонент «ng g c counter».

Теперь мы готовы заняться собственно кодированием…

Сначала начнем с действия "counter.actions.ts". Итак, здесь нам понадобятся 4 действия: увеличение, уменьшение, установка и сброс.

import { createAction, props } from '@ngrx/store';
export enum CounterActionTypes {
  Increment = '[Counter] increment a number',
  Decrement = '[Counter] decrement a number',
  Set = '[Counter] set a number',
  Reset = '[Counter] reset a number',
}
export const increment = createAction(CounterActionTypes.Increment);
export const decrement = createAction(CounterActionTypes.Decrement);
export const set = createAction(
  CounterActionTypes.Set,
  props<{payload: number}>(),
);
export const reset = createAction(CounterActionTypes.Reset);

Теперь нам нужно создать редукторы «counter.reducers.ts».

import { on, createReducer, Action } from '@ngrx/store';
import { increment, decrement, set, reset } from './counter.actions'
export interface State {
  count: number;
}
export const initialState: State = {
  count: 0
}
const _counterReducer = createReducer(initialState,
  on(increment, state => ({...state, count: state.count + 1})),
  on(decrement, state => ({...state, count: state.count - 1})),
  on(set, (state, action) => ({...state, count: action.payload})),
  on(reset, state => ({...state, count: 0})),
);
export function counterReducer(state: State, action: Action) {
  return _counterReducer(state, action);
}

Для этого небольшого проекта вам не нужно создавать глобальный файл app.reducers.ts, но это хорошая практика.

import * as fromCounter from './counter/counter.reducers';
export interface State {
  count: fromCounter.State
}
export let rootReducers = {
count: fromCounter.counterReducer
}

Теперь нам нужно добавить редьюсеры в модуль приложения, а также добавим store-devtools.

imports: [ ...
  StoreModule.forRoot(rootReducers),
  StoreDevtoolsModule.instrument({
    maxAge: 25, // Retains last 25 states
    logOnly: environment.production, 
    }),
]

После этого нам нужно создать селекторы counter.selectors.ts для получения фрагмента данных, на который мы можем подписаться в компоненте.

import { createSelector } from '@ngrx/store';
import { State } from '../app.reducers';
import * as fromCount from './counter.reducers';
export const getCount = (state: State) => { return state.count};
export const getCountNumber = createSelector(
  getCount,
  (state: fromCount.State) => state.count
);

Поскольку мы сейчас закончили с вещами, связанными с магазином. Давайте поработаем над компонентом и бизнес-логикой.

Мы будем использовать карточку материала, кнопку, иконку, ввод и форму. А также мы будем использовать реактивную форму. Итак, давайте сначала добавим все эти импорты в app.module.

imports: [...
  MatCardModule,
  MatButtonModule,
  MatIconModule,
  MatInputModule,
  MatFormFieldModule,
  ReactiveFormsModule,
]

Теперь давайте сразу перейдем к шаблону. Вы можете проверить здесь все доступные компоненты материала и как их использовать https://material.angular.io/.

<mat-card class="counter-card">
  <mat-card-header class="title">
    <mat-card-title>Counter app</mat-card-title>
  </mat-card-header>
  <mat-card-content class="content">
    <mat-form-field appearance="outline">
      <mat-label>Count:</mat-label>
      <input matInput placeholder="Count" type="number" 
       [formControl]="countForm">
    </mat-form-field>
  </mat-card-content>
  <mat-card-actions class="action">
    <button mat-icon-button color="primary" (click)="onIncrement()">
      <mat-icon>add_circle</mat-icon>
    </button>
    <button mat-icon-button color="primary" (click)="onDecrement()">
      <mat-icon>remove_circle</mat-icon>
    </button>
    <button mat-icon-button color="primary" (click)="onSet()">
      <mat-icon>check_circle</mat-icon>
    </button>
    <button mat-icon-button color="primary" (click)="onReset()">
      <mat-icon>restore</mat-icon>
    </button>
  </mat-card-actions>
</mat-card>

Есть также некоторые стили, чтобы сделать его красивым.

:host {
  position: absolute;
  top: 30vh;
  left: calc(100vh - 200px);
}
.counter-card {
  width: 400px;
}
.content, .title {
  display: flex;
  flex-direction: row;
  justify-content: center;
}
.action {
  display: flex;
  flex-direction: row;
  justify-content: space-evenly;
}
.count {
  background-color: #9a9ca6
}

Теперь наступает важная часть бизнес-логики. Итак, здесь у нас будет один formControl. А затем мы должны внедрить магазин и подписаться на селектор, чтобы получить счет для каждого обновления (Не забудьте отписаться от всех подписок на ngOnDestroy, позже вы меня поблагодарите ;)). И у нас будут функции для каждой кнопки, где мы должны отправить действие.

import { Component, OnInit, OnDestroy } from '@angular/core';
import { Store, select } from '@ngrx/store';
import { State } from '../app.reducers';
import { Subscription } from 'rxjs';
import { getCountNumber } from './counter.selectors';
import { increment, decrement, reset, set } from './counter.actions';
import { FormControl } from '@angular/forms';
@Component({
  selector: 'app-counter',
  templateUrl: './counter.component.html',
  styleUrls: ['./counter.component.scss']
})
export class CounterComponent implements OnInit, OnDestroy {
  public countForm: FormControl;
  private countSubscription: Subscription;
  constructor(private store$: Store<State>) {
    this.countForm = new FormControl('');
  }
  ngOnInit(): void {
  this.countSubscription = this.store$.pipe(select(getCountNumber)).subscribe(count => {
    this.countForm.setValue(count);
  })
  }
  ngOnDestroy(): void{
    this.countSubscription.unsubscribe();
  }
  onIncrement() {
    this.store$.dispatch(increment());
  }
  onDecrement() {
    this.store$.dispatch(decrement());
  }
  onReset() {
    this.store$.dispatch(reset());
  }
  onSet() {
    this.store$.dispatch(set({payload: Number(this.countForm.value)}));
  }
}

Итак, мы закончили с кодированием.

Если вы помните, мы также установили store-devtool, так что он делает? Это довольно полезный инструмент, который позволяет вам отлаживать все, что связано с управлением состоянием. Для этого вам нужно просто установить его расширение "Redux DevTools" в Chrome. Вы можете видеть свое состояние после каждой отправки действия и его разницу. И самое главное, вы можете путешествовать во времени, я имел в виду, что вы можете прыгать/пропускать любое действие.

Вы можете найти весь код здесь https://github.com/iepratiyush/personal-project

Импорт ссылок

  1. Angular документы https://angular.io/
  2. Документы NgRx https://ngrx.io/docs
  3. Сайт материала https://material.angular.io/
  4. Репозиторий иконок https://material.io/resources/icons/?style=baseline
  5. Флексбокс CSS https://css-tricks.com/snippets/css/a-guide-to-flexbox/

Продолжайте писать код и вносить свой вклад в этот замечательный фреймворк Angular Central и @ngrx.