Существует множество курсов и учебных пособий по Angular и NgRx, которые мы можем пройти, но всем нам трудно найти пример проекта, который мы можем создать для отработки основ.
Итак, давайте попробуем создать приложение-счетчик с небольшим количеством дополнительных функций, где мы также можем устанавливать и сбрасывать число, используя последние версии Angular, NgRx и Material.
Сначала займемся всеми настройками…
- Прежде всего, у нас должна быть последняя версия node.js и редактор на ваш выбор, например. https://code.visualstudio.com/.
- Установите последнюю версию Angular CLI «npm install -g @angular/cli»
- Теперь вы можете создать новый проект «ng new counter-app». Он попросит вас выбрать, хотите ли вы использовать маршрутизацию по умолчанию или нет, и таблицу стилей, которую вы хотите использовать.
- После этого мы должны установить все зависимости. В нашем проекте нам понадобятся @angular/material, @ngrx/store и @ngrx/store-devtools.
- Для материала сейчас нам нужна всего одна команда, которая автоматизирует все шаги за вас. «Добавить @angular/material».
- Некоторые люди могут получить ошибку в отношении разрешения powershell. Если вы столкнулись с этой проблемой, сделайте следующее: «Удалите ng.ps1 из каталога C:\Users\%username%\AppData\Roaming\npm\, затем попробуйте очистить кэш npm в C:\Users\%username %\AppData\Roaming\npm-cache\”
- Даже для магазина теперь нам нужна только одна команда, чтобы выполнить все шаги «ng add @ngrx/store». После этогомы должны установить Redux devtools, который пригодится в процессе отладки «npm install @ngrx/store-devtools — сохранить»
- Затем удалите все из 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
Импорт ссылок
- Angular документы https://angular.io/
- Документы NgRx https://ngrx.io/docs
- Сайт материала https://material.angular.io/
- Репозиторий иконок https://material.io/resources/icons/?style=baseline
- Флексбокс CSS https://css-tricks.com/snippets/css/a-guide-to-flexbox/
Продолжайте писать код и вносить свой вклад в этот замечательный фреймворк Angular Central и @ngrx.