
आज तक,
राज्य प्रबंधन के बिना एक भी बड़ा
एसपीए आवेदन पूरा नहीं हुआ
है । इस क्षेत्र में
कोणीय के लिए कई समाधान हैं। इनमें से सबसे लोकप्रिय
NgRx है । यह
RxJs लाइब्रेरी का उपयोग करके एक
Redux पैटर्न लागू करता है और इसमें अच्छे उपकरण होते हैं।
इस लेख में, हम संक्षेप में मुख्य
NgRx मॉड्यूल के माध्यम से
जाएंगे और
कोणीय-नेग्रेक्स-डेटा लाइब्रेरी पर अधिक विस्तार से ध्यान केंद्रित
करेंगे , जो आपको पांच मिनट में
राज्य प्रबंधन के साथ पूर्ण
सीआरयूडी बनाने की अनुमति देता है।
NgRx समीक्षा
आप निम्नलिखित लेखों में
NgRx के बारे में अधिक पढ़ सकते हैं:
-
कोणीय / NGRX पर प्रतिक्रियाशील अनुप्रयोग। भाग 1. परिचय-
कोणीय / NGRX पर प्रतिक्रियाशील अनुप्रयोग। भाग 2. भंडार-
कोणीय / NGRX पर प्रतिक्रियाशील अनुप्रयोग। भाग 3. प्रभावसंक्षेप में
NgRx के मुख्य मॉड्यूल, इसके पेशेवरों और विपक्षों पर विचार करें।
NgRx / स्टोर - एक Redux पैटर्न लागू करता है।
सरल स्टोर कार्यान्वयनcounter.actions.ts
export const INCREMENT = 'INCREMENT'; export const DECREMENT = 'DECREMENT'; export const RESET = 'RESET';
counter.reducer.ts
import { Action } from '@ngrx/store'; const initialState = 0; export function counterReducer(state: number = initialState, action: Action) { switch (action.type) { case INCREMENT: return state + 1; case DECREMENT: return state - 1; case RESET: return 0; default: return state; } }
।
मॉड्यूल से कनेक्शन
import { NgModule } from '@angular/core'; import { StoreModule } from '@ngrx/store'; import { counterReducer } from './counter'; @NgModule({ imports: [StoreModule.forRoot({ count: counterReducer })], }) export class AppModule {}
घटक में उपयोग करें
import { Component } from '@angular/core'; import { Store, select } from '@ngrx/store'; import { Observable } from 'rxjs'; import { INCREMENT, DECREMENT, RESET } from './counter'; interface AppState { count: number; } @Component({ selector: 'app-my-counter', template: ` <button (click)="increment()">Increment</button> <div>Current Count: {{ count$ | async }}</div> <button (click)="decrement()">Decrement</button> <button (click)="reset()">Reset Counter</button> `, }) export class MyCounterComponent { count$: Observable<number>; constructor(private store: Store<AppState>) { this.count$ = store.pipe(select('count')); } increment() { this.store.dispatch({ type: INCREMENT }); } decrement() { this.store.dispatch({ type: DECREMENT }); } reset() { this.store.dispatch({ type: RESET }); } }
NgRx / store-devtools - आपको
redux-devtools के माध्यम से एप्लिकेशन में परिवर्तनों को ट्रैक करने की अनुमति देता है।
कनेक्शन उदाहरण import { StoreDevtoolsModule } from '@ngrx/store-devtools'; @NgModule({ imports: [ StoreModule.forRoot(reducers), // StoreModule StoreDevtoolsModule.instrument({ maxAge: 25, // 25 }), ], }) export class AppModule {}
NgRx / प्रभाव - आपको आवेदन में आने वाले डेटा को HTTP अनुरोधों जैसे रिपॉजिटरी में जोड़ने की अनुमति देता है।
उदाहरण./effects/auth.effects.ts
import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Action } from '@ngrx/store'; import { Actions, Effect, ofType } from '@ngrx/effects'; import { Observable, of } from 'rxjs'; import { catchError, map, mergeMap } from 'rxjs/operators'; @Injectable() export class AuthEffects { // Listen for the 'LOGIN' action @Effect() login$: Observable<Action> = this.actions$.pipe( ofType('LOGIN'), mergeMap(action => this.http.post('/auth', action.payload).pipe( // If successful, dispatch success action with result map(data => ({ type: 'LOGIN_SUCCESS', payload: data })), // If request fails, dispatch failed action catchError(() => of({ type: 'LOGIN_FAILED' })) ) ) ); constructor(private http: HttpClient, private actions$: Actions) {} }
प्रभाव को मॉड्यूल से जोड़ना
import { EffectsModule } from '@ngrx/effects'; import { AuthEffects } from './effects/auth.effects'; @NgModule({ imports: [EffectsModule.forRoot([AuthEffects])], }) export class AppModule {}
NgRx / एंटिटी - डेटा सरणियों के साथ काम करने की क्षमता प्रदान करता है।
उदाहरणuser.model.ts
export interface User { id: string; name: string; }
user.actions.ts
import { Action } from '@ngrx/store'; import { Update } from '@ngrx/entity'; import { User } from './user.model'; export enum UserActionTypes { LOAD_USERS = '[User] Load Users', ADD_USER = '[User] Add User', UPSERT_USER = '[User] Upsert User', ADD_USERS = '[User] Add Users', UPSERT_USERS = '[User] Upsert Users', UPDATE_USER = '[User] Update User', UPDATE_USERS = '[User] Update Users', DELETE_USER = '[User] Delete User', DELETE_USERS = '[User] Delete Users', CLEAR_USERS = '[User] Clear Users', } export class LoadUsers implements Action { readonly type = UserActionTypes.LOAD_USERS; constructor(public payload: { users: User[] }) {} } export class AddUser implements Action { readonly type = UserActionTypes.ADD_USER; constructor(public payload: { user: User }) {} } export class UpsertUser implements Action { readonly type = UserActionTypes.UPSERT_USER; constructor(public payload: { user: User }) {} } export class AddUsers implements Action { readonly type = UserActionTypes.ADD_USERS; constructor(public payload: { users: User[] }) {} } export class UpsertUsers implements Action { readonly type = UserActionTypes.UPSERT_USERS; constructor(public payload: { users: User[] }) {} } export class UpdateUser implements Action { readonly type = UserActionTypes.UPDATE_USER; constructor(public payload: { user: Update<User> }) {} } export class UpdateUsers implements Action { readonly type = UserActionTypes.UPDATE_USERS; constructor(public payload: { users: Update<User>[] }) {} } export class DeleteUser implements Action { readonly type = UserActionTypes.DELETE_USER; constructor(public payload: { id: string }) {} } export class DeleteUsers implements Action { readonly type = UserActionTypes.DELETE_USERS; constructor(public payload: { ids: string[] }) {} } export class ClearUsers implements Action { readonly type = UserActionTypes.CLEAR_USERS; } export type UserActionsUnion = | LoadUsers | AddUser | UpsertUser | AddUsers | UpsertUsers | UpdateUser | UpdateUsers | DeleteUser | DeleteUsers | ClearUsers;
user.reducer.ts
import { EntityState, EntityAdapter, createEntityAdapter } from '@ngrx/entity'; import { User } from './user.model'; import { UserActionsUnion, UserActionTypes } from './user.actions'; export interface State extends EntityState<User> { // additional entities state properties selectedUserId: number | null; } export const adapter: EntityAdapter<User> = createEntityAdapter<User>(); export const initialState: State = adapter.getInitialState({ // additional entity state properties selectedUserId: null, }); export function reducer(state = initialState, action: UserActionsUnion): State { switch (action.type) { case UserActionTypes.ADD_USER: { return adapter.addOne(action.payload.user, state); } case UserActionTypes.UPSERT_USER: { return adapter.upsertOne(action.payload.user, state); } case UserActionTypes.ADD_USERS: { return adapter.addMany(action.payload.users, state); } case UserActionTypes.UPSERT_USERS: { return adapter.upsertMany(action.payload.users, state); } case UserActionTypes.UPDATE_USER: { return adapter.updateOne(action.payload.user, state); } case UserActionTypes.UPDATE_USERS: { return adapter.updateMany(action.payload.users, state); } case UserActionTypes.DELETE_USER: { return adapter.removeOne(action.payload.id, state); } case UserActionTypes.DELETE_USERS: { return adapter.removeMany(action.payload.ids, state); } case UserActionTypes.LOAD_USERS: { return adapter.addAll(action.payload.users, state); } case UserActionTypes.CLEAR_USERS: { return adapter.removeAll({ ...state, selectedUserId: null }); } default: { return state; } } } export const getSelectedUserId = (state: State) => state.selectedUserId; // get the selectors const { selectIds, selectEntities, selectAll, selectTotal } = adapter.getSelectors(); // select the array of user ids export const selectUserIds = selectIds; // select the dictionary of user entities export const selectUserEntities = selectEntities; // select the array of users export const selectAllUsers = selectAll; // select the total user count export const selectUserTotal = selectTotal;
reducers / index.ts
import { createSelector, createFeatureSelector, ActionReducerMap, } from '@ngrx/store'; import * as fromUser from './user.reducer'; export interface State { users: fromUser.State; } export const reducers: ActionReducerMap<State> = { users: fromUser.reducer, }; export const selectUserState = createFeatureSelector<fromUser.State>('users'); export const selectUserIds = createSelector( selectUserState, fromUser.selectUserIds ); export const selectUserEntities = createSelector( selectUserState, fromUser.selectUserEntities ); export const selectAllUsers = createSelector( selectUserState, fromUser.selectAllUsers ); export const selectUserTotal = createSelector( selectUserState, fromUser.selectUserTotal ); export const selectCurrentUserId = createSelector( selectUserState, fromUser.getSelectedUserId ); export const selectCurrentUser = createSelector( selectUserEntities, selectCurrentUserId, (userEntities, userId) => userEntities[userId] );
परिणाम क्या है?
हम लाभ का एक समूह के साथ पूर्ण
राज्य प्रबंधन प्राप्त करते हैं:
- आवेदन के लिए एक एकल डेटा स्रोत,
- राज्य आवेदन से अलग संग्रहीत है,
- परियोजना में सभी डेवलपर्स के लिए एक एकल लेखन शैली,
-
changeDetectionStrategy.Onush आवेदन के सभी घटकों में,
-
Redux-devtools के माध्यम से सुविधाजनक डिबगिंग,
- परीक्षण में आसानी, के रूप में
reducers शुद्ध कार्य हैं।
लेकिन इसके नुकसान भी हैं:- बड़ी संख्या में प्रतीत होने योग्य अयोग्य मॉड्यूल,
- एक ही प्रकार का बहुत से कोड जिसे आपने बिना किसी दुःख के नहीं देखा,
- उपरोक्त सभी की वजह से महारत हासिल करने में कठिनाई।
CRUD
एक नियम के रूप में, एप्लिकेशन का एक महत्वपूर्ण हिस्सा ऑब्जेक्ट्स (निर्माण, पढ़ना, अपडेट करना, हटाना) के साथ काम कर रहा है, इसलिए, काम की सुविधा के लिए,
CRUD अवधारणा बनाई गई थी (बनाएं, पढ़ें, अपडेट, हटाएं)। इस प्रकार, सभी प्रकार की वस्तुओं के साथ काम करने के लिए बुनियादी संचालन मानकीकृत हैं। यह लंबे समय से बैकेंड पर फलफूल रहा है। कई पुस्तकालय इस कार्यक्षमता को लागू करने और नियमित काम से छुटकारा पाने में मदद करते हैं।
NgRx में ,
इकाई मॉड्यूल
CRUD के लिए जिम्मेदार है, और यदि आप इसके कार्यान्वयन के एक उदाहरण को देखते हैं, तो आप तुरंत देख सकते हैं कि यह
NgRx का सबसे बड़ा और सबसे जटिल हिस्सा है। यही कारण है कि
जॉन पापा और
वार्ड बेल ने
कोणीय- ngrx-data बनाया ।
कोणीय ngrx-डेटा
कोणीय- ngrx- डेटा एक
NgRx ऐड-इन लाइब्रेरी है जो आपको अतिरिक्त कोड लिखे बिना डेटा सरणियों के साथ काम करने की अनुमति देता है।
एक पूर्ण
राज्य प्रबंधन बनाने के अलावा, वह सर्वर से बातचीत करने के लिए
http के साथ सेवाओं के निर्माण का कार्य करता है।
एक उदाहरण पर विचार करें
स्थापना npm install --save @ngrx/store @ngrx/effects @ngrx/entity @ngrx/store-devtools ngrx-data
कोणीय- ngrx- डेटा मॉड्यूल import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { EntityMetadataMap, NgrxDataModule, DefaultDataServiceConfig } from 'ngrx-data'; const defaultDataServiceConfig: DefaultDataServiceConfig = { root: 'crud' }; export const entityMetadata: EntityMetadataMap = { Hero: {}, User:{} }; export const pluralNames = { Hero: 'heroes' }; @NgModule({ imports: [ CommonModule, NgrxDataModule.forRoot({ entityMetadata, pluralNames }) ], declarations: [], providers: [ { provide: DefaultDataServiceConfig, useValue: defaultDataServiceConfig } ] }) export class EntityStoreModule {}
ऐप से कनेक्ट करें @NgModule({ imports: [ BrowserModule, HttpClientModule, StoreModule.forRoot({}), EffectsModule.forRoot([]), EntityStoreModule, StoreDevtoolsModule.instrument({ maxAge: 25, }), ], declarations: [ AppComponent ], providers: [], bootstrap: [AppComponent] }) export class AppModule {}
हमें बस एक ही प्रभाव, रिड्यूसर और एक्शन और चयनकर्ता को लिखे बिना , NgRx के साथ बैक-एंड और एपीआई के एकीकरण के साथ काम करने के लिए उत्पन्न एपीआई मिली ।आइए अधिक विस्तार से जांच करें कि यहां क्या हो रहा है।
DefaultDataServiceConfig स्थिरांक हमारे API के लिए विन्यास सेट करता है और
प्रदाताओं मॉड्यूल से जोड़ता है।
मूल संपत्ति इंगित करती है कि अनुरोधों के लिए कहां जाना है। यदि इसे सेट नहीं किया जाता है, तो डिफ़ॉल्ट "एपीआई" होगा।
const defaultDataServiceConfig: DefaultDataServiceConfig = { root: 'crud' };
UnitMetadata स्थिरांक NgrxDataModule.forRoot से कनेक्ट होने पर बनाए जाने वाले
स्टोर के नामों को परिभाषित करता
है ।
export const entityMetadata: EntityMetadataMap = { Hero: {}, User:{} }; ... NgrxDataModule.forRoot({ entityMetadata, pluralNames })
API के पथ में आधार पथ (हमारे मामले में "क्रूड") और स्टोर का नाम शामिल है।
उदाहरण के लिए, एक उपयोगकर्ता को एक निश्चित संख्या के साथ पाने के लिए, पथ "crud / user / {userId}" होगा।
उपयोगकर्ताओं की पूरी सूची प्राप्त करने के लिए, "s" - "crud / user
s " अक्षर को डिफ़ॉल्ट रूप से स्टोर नाम के अंत में जोड़ा जाता है।
यदि आपको पूरी सूची (उदाहरण के लिए, "हीरो" और "हेरोस") प्राप्त करने के लिए एक अलग मार्ग की आवश्यकता है, तो आप इसे
बहुवचन सेट
करके और
NgrxDataModule.forRoot से कनेक्ट करके बदल सकते हैं।
export const pluralNames = { Hero: 'heroes' }; ... NgrxDataModule.forRoot({ entityMetadata, pluralNames })
घटक में कनेक्शन
घटक में कनेक्ट करने के लिए, आवश्यक है कि निर्माण के लिए
UnitServices निर्माता को पास किया
जाए और आवश्यक भंडारण
का चयन
करने के लिए
getEntityCollectionService विधि
का उपयोग किया
जाए। import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core'; import { Observable } from 'rxjs'; import { Hero } from '@appModels/hero'; import { EntityServices, EntityCollectionService } from 'ngrx-data'; @Component({ selector: 'app-heroes', templateUrl: './heroes.component.html', styleUrls: ['./heroes.component.css'], changeDetection: ChangeDetectionStrategy.OnPush }) export class HeroesComponent implements OnInit { heroes$: Observable<Hero[]>; heroesService: EntityCollectionService<Hero>; constructor(entityServices: EntityServices) { this.heroesService = entityServices.getEntityCollectionService('Hero'); } ... }
घटक को सूची को बाँधने के लिए,
संस्थाओं को सेवा से
$ संपत्ति लेना पर्याप्त है, और सर्वर से डेटा प्राप्त करने के लिए
getAll () कॉल करें।
ngOnInit() { this.heroes$ = this.heroesService.entities$; this.heroesService.getAll(); }
मूल डेटा के अलावा, आप यह भी प्राप्त कर सकते हैं:
-
लोड $ ,
लोडिंग $ - लोडिंग डेटा की स्थिति प्राप्त करना,
-
त्रुटियाँ $ - त्रुटियां जब सेवा चल रही हो,
-
गिनती में $ - रिकॉर्ड में कुल संख्या।
सर्वर से बातचीत करने के मुख्य तरीके:
-
getAll () - डेटा की पूरी सूची प्राप्त करना,
-
getWithQuery (क्वेरी) - क्वेरी मापदंडों का उपयोग करके फ़िल्टर की गई सूची प्राप्त करना,
-
getByKey (आईडी) - पहचानकर्ता द्वारा एक रिकॉर्ड प्राप्त करना,
-
add (एंटिटी) - बैकिंग के लिए अनुरोध के साथ एक नई इकाई जोड़ना
-
हटाएँ (संस्था) - समर्थन के लिए एक अनुरोध के साथ एक इकाई को हटाने,
-
अद्यतन (एंटिटी) - बैकिंग के लिए अनुरोध के साथ इकाई को अपडेट करें।
स्थानीय भंडारण विधियाँ:
-
addManyToCache (एंटिटी) - रिपॉजिटरी में नई संस्थाओं की एक सरणी जोड़कर,
-
addOneToCache (एंटिटी) - केवल रिपॉजिटरी में एक नई इकाई जोड़ना,
-
removeOneFromCache (आईडी) - रिपॉजिटरी से एक इकाई को हटा दें,
-
updateOneInCache (एंटिटी) - रिपॉजिटरी में इकाई को अपडेट करें,
-
upsertOneInCache (एंटिटी) - यदि निर्दिष्ट आईडी वाली इकाई मौजूद है, तो इसे अपडेट किया जाता है, यदि नहीं, तो एक नया बनाया जाता है,
- और अन्य
घटक उपयोग उदाहरण import { EntityCollectionService, EntityServices } from 'ngrx-data'; import { Hero } from '../../core'; @Component({ selector: 'app-heroes', templateUrl: './heroes.component.html', changeDetection: ChangeDetectionStrategy.OnPush }) export class HeroesComponent implements OnInit { heroes$: Observable<Hero[]>; heroesService: EntityCollectionService<Hero>; constructor(entityServices: EntityServices) { this.heroesService = entityServices.getEntityCollectionService('Hero'); } ngOnInit() { this.heroes$ = this.heroesService.entities$; this.getHeroes(); } getHeroes() { this.heroesService.getAll(); } addHero(hero: Hero) { this.heroesService.add(hero); } deleteHero(hero: Hero) { this.heroesService.delete(hero.id); } updateHero(hero: Hero) { this.heroesService.update(hero); } }
सभी
कोणीय-ngrx- डेटा विधियों को स्थानीय रूप से काम करने और सर्वर के साथ बातचीत करने के लिए विभाजित किया गया है। यह आपको क्लाइंट पर डेटा में हेरफेर करने और सर्वर का उपयोग करने पर लाइब्रेरी का उपयोग करने की अनुमति देता है।
लॉगिंग
लॉगिंग के लिए, आपको
EntityServices को एक घटक या सेवा में इंजेक्ट करने और गुणों का उपयोग करने की आवश्यकता है:
- कम
राशि $ - लॉगिंग क्रियाओं के लिए,
-
UnitActionErrors $ - लॉगिंग त्रुटियों के लिए।
import { Component, OnInit } from '@angular/core'; import { MessageService } from '@appServices/message.service'; import { EntityServices } from 'ngrx-data'; @Component({ selector: 'app-messages', templateUrl: './messages.component.html', styleUrls: ['./messages.component.css'] }) export class MessagesComponent implements OnInit { constructor( public messageService: MessageService, private entityServices: EntityServices ) {} ngOnInit() { this.entityServices.reducedActions$.subscribe(res => { if (res && res.type) { this.messageService.add(res.type); } }); } }
मुख्य NgRx रिपॉजिटरी में जा रहा है
एनजी- कॉन्फ 2018 की घोषणा के अनुसार,
कोणीय-एनग्रक्स-डेटा जल्द ही मुख्य
एनजीआरएक्स रिपॉजिटरी में माइग्रेट किया
जाएगा ।
NgRx टॉक वीडियो - ब्रैंडन रॉबर्ट्स और माइक रयान के साथ बॉयलरप्लेट को कम करना
संदर्भ
एंगर-एनग्रक्स-डेटा के निर्माता:- जॉन पापा
twitter.com/John_Papa- वार्ड बेल
twitter.com/wardbellआधिकारिक रिपॉजिटरी:-
NgRx-
कोणीय- ngrx-dataआवेदन उदाहरण:-
कोणीय- ngrx- डेटा के बिना NgRx के साथ-
NgRx और कोणीय-ngrx-data के साथटेलीग्राम पर रूसी भाषी कोणीय समुदाय