{"id":287344,"date":"2018-08-16T14:07:11","date_gmt":"2018-08-16T10:07:11","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=287344"},"modified":"-0001-11-30T00:00:00","modified_gmt":"2018-08-16T10:07:11","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=287344","title":{"rendered":"angular-ngrx-data \u2014 state management \u0438 CRUD \u0437\u0430 \u043f\u044f\u0442\u044c \u043c\u0438\u043d\u0443\u0442"},"content":{"rendered":"\n<div data-io-article-url=\"https:\/\/habr.com\/post\/418369\/\" class=\"post__text post__text-html js-mediator-article\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/b8\/vq\/w4\/b8vqw4vomyo32ddmdrogi6lnnxo.png\" alt=\"image\"><br \/>  \u041d\u0430 \u0441\u0435\u0433\u043e\u0434\u043d\u044f\u0448\u043d\u0438\u0439 \u0434\u0435\u043d\u044c \u043d\u0438 \u043e\u0434\u043d\u043e \u0431\u043e\u043b\u044c\u0448\u043e\u0435 <b>SPA<\/b> \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043d\u0435 \u043e\u0431\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0431\u0435\u0437 <b>state management (\u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435\u043c)<\/b>. \u0414\u043b\u044f <a href=\"https:\/\/angular.io\/\"><b>Angular<\/b><\/a> \u043f\u043e \u0434\u0430\u043d\u043d\u043e\u043c\u0443 \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044e \u0435\u0441\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0440\u0435\u0448\u0435\u043d\u0438\u0439. \u0421\u0430\u043c\u044b\u043c \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u044b\u043c \u0438\u0437 \u043d\u0438\u0445 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f <a href=\"https:\/\/github.com\/ngrx\/platform\"><b>NgRx<\/b><\/a>. \u041e\u043d \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u0442 <b>Redux<\/b> \u043f\u0430\u0442\u0442\u0435\u0440\u043d \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 <b>RxJs<\/b> \u0438 \u043e\u0431\u043b\u0430\u0434\u0430\u0435\u0442 \u0445\u043e\u0440\u043e\u0448\u0438\u043c \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0435\u043c. <\/p>\n<p>  \u0412 \u0434\u0430\u043d\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u043c\u044b \u043a\u0440\u0430\u0442\u043a\u043e \u043f\u0440\u043e\u0439\u0434\u0435\u043c\u0441\u044f \u043f\u043e \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u043c \u043c\u043e\u0434\u0443\u043b\u044f\u043c <a href=\"https:\/\/github.com\/ngrx\/platform\"><b>NgRx<\/b><\/a> \u0438 \u0431\u043e\u043b\u0435\u0435 \u0434\u0435\u0442\u0430\u043b\u044c\u043d\u043e \u0441\u043e\u0441\u0440\u0435\u0434\u043e\u0442\u043e\u0447\u0438\u043c\u0441\u044f \u043d\u0430 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0435 <a href=\"https:\/\/github.com\/johnpapa\/angular-ngrx-data\"><b>angular-ngrx-data<\/b><\/a>, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u044b\u0439 <a href=\"https:\/\/ru.wikipedia.org\/wiki\/CRUD\"><b>CRUD<\/b><\/a> \u0441\u043e <b>state management<\/b> \u0437\u0430 \u043f\u044f\u0442\u044c \u043c\u0438\u043d\u0443\u0442.<br \/>  <a name=\"habracut\"><\/a>  <\/p>\n<h2>\u041e\u0431\u0437\u043e\u0440 NgRx<\/h2>\n<p>  \u0414\u0435\u0442\u0430\u043b\u044c\u043d\u043e \u043f\u0440\u043e <b>NgRx<\/b> \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0447\u0438\u0442\u0430\u0442\u044c \u0432 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0445 \u0441\u0442\u0430\u0442\u044c\u044f\u0445:<\/p>\n<p>   \u2014 <a href=\"https:\/\/medium.com\/@demyanyuk\/%D1%80%D0%B5%D0%B0%D0%BA%D1%82%D0%B8%D0%B2%D0%BD%D1%8B%D0%B5-%D0%BF%D1%80%D0%B8%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D1%8F-%D0%BD%D0%B0-angular-ngrx-%D1%87%D0%B0%D1%81%D1%82%D1%8C-1-cb7b4f2852dc\">\u0420\u0435\u0430\u043a\u0442\u0438\u0432\u043d\u044b\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043d\u0430 Angular\/NGRX. \u0427\u0430\u0441\u0442\u044c 1. \u0412\u0432\u0435\u0434\u0435\u043d\u0438\u0435<\/a><br \/>   \u2014 <a href=\"https:\/\/medium.com\/@demyanyuk\/%D1%80%D0%B5%D0%B0%D0%BA%D1%82%D0%B8%D0%B2%D0%BD%D1%8B%D0%B5-%D0%BF%D1%80%D0%B8%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D1%8F-%D0%BD%D0%B0-angular-ngrx-%D1%87%D0%B0%D1%81%D1%82%D1%8C-2-1412bc9adf17\">\u0420\u0435\u0430\u043a\u0442\u0438\u0432\u043d\u044b\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043d\u0430 Angular\/NGRX. \u0427\u0430\u0441\u0442\u044c 2. Store<\/a><br \/>   \u2014 <a href=\"https:\/\/medium.com\/@demyanyuk\/%D1%80%D0%B5%D0%B0%D0%BA%D1%82%D0%B8%D0%B2%D0%BD%D1%8B%D0%B5-%D0%BF%D1%80%D0%B8%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D1%8F-%D0%BD%D0%B0-angular-ngrx-%D1%87%D0%B0%D1%81%D1%82%D1%8C-3-effects-6f4b34dfa289\">\u0420\u0435\u0430\u043a\u0442\u0438\u0432\u043d\u044b\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043d\u0430 Angular\/NGRX. \u0427\u0430\u0441\u0442\u044c 3. Effects<\/a><\/p>\n<p>  \u041a\u0440\u0430\u0442\u043a\u043e \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u043c\u043e\u0434\u0443\u043b\u0438 <a href=\"https:\/\/github.com\/ngrx\/platform\">NgRx<\/a>, \u0435\u0433\u043e \u043f\u043b\u044e\u0441\u044b \u0438 \u043c\u0438\u043d\u0443\u0441\u044b.<\/p>\n<p>  <b><a href=\"https:\/\/github.com\/ngrx\/platform\/blob\/master\/docs\/store\/README.md\">NgRx\/store<\/a><\/b> \u2014 \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u0442 Redux \u043f\u0430\u0442\u0442\u0435\u0440\u043d.<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\u041f\u0440\u043e\u0441\u0442\u0430\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f store<\/b><\/p>\n<div class=\"spoiler_text\">counter.actions.ts  <\/p>\n<pre><code>export const INCREMENT = 'INCREMENT'; export const DECREMENT = 'DECREMENT'; export const RESET = 'RESET'; <\/code><\/pre>\n<p>  counter.reducer.ts<\/p>\n<pre><code>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;   } } <\/code><\/pre>\n<p>.<br \/>  \u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0432 \u043c\u043e\u0434\u0443\u043b\u044c  <\/p>\n<pre><code>import { NgModule } from '@angular\/core'; import { StoreModule } from '@ngrx\/store'; import { counterReducer } from '.\/counter';  @NgModule({   imports: [StoreModule.forRoot({ count: counterReducer })], }) export class AppModule {} <\/code><\/pre>\n<p>  \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u0432 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0435  <\/p>\n<pre><code>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: `     &lt;button (click)=\"increment()\"&gt;Increment&lt;\/button&gt;     &lt;div&gt;Current Count: {{ count$ | async }}&lt;\/div&gt;     &lt;button (click)=\"decrement()\"&gt;Decrement&lt;\/button&gt;      &lt;button (click)=\"reset()\"&gt;Reset Counter&lt;\/button&gt;   `, }) export class MyCounterComponent {   count$: Observable&lt;number&gt;;    constructor(private store: Store&lt;AppState&gt;) {     this.count$ = store.pipe(select('count'));   }    increment() {     this.store.dispatch({ type: INCREMENT });   }    decrement() {     this.store.dispatch({ type: DECREMENT });   }    reset() {     this.store.dispatch({ type: RESET });   } } <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  <b><a href=\"https:\/\/github.com\/ngrx\/platform\/blob\/master\/docs\/store-devtools\/README.md\">NgRx\/store-devtools<\/a><\/b> \u2014 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0442\u044c \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438 \u0447\u0435\u0440\u0435\u0437<a href=\"https:\/\/chrome.google.com\/webstore\/detail\/redux-devtools\/lmhkpmbekcpmknklioeibfkpmmfibljd\"> redux-devtools<\/a>.<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\u041f\u0440\u0438\u043c\u0435\u0440 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code>import { StoreDevtoolsModule } from '@ngrx\/store-devtools';  @NgModule({   imports: [     StoreModule.forRoot(reducers),     \/\/ \u041c\u043e\u0434\u0443\u043b\u044c \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d \u043f\u043e\u0441\u043b\u0435 StoreModule     StoreDevtoolsModule.instrument({       maxAge: 25, \/\/ \u0425\u0440\u0430\u043d\u044f\u0442\u0441\u044f \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0435 25 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0439     }),   ], }) export class AppModule {}<\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  <b><a href=\"https:\/\/github.com\/ngrx\/platform\/blob\/master\/docs\/effects\/README.md\">NgRx\/effects<\/a><\/b> \u2014 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u0432 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435 \u0434\u0430\u043d\u043d\u044b\u0435, \u043f\u0440\u0438\u0445\u043e\u0434\u044f\u0449\u0438\u0435 \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u0442\u0430\u043a\u0438\u0435 \u043a\u0430\u043a http \u0437\u0430\u043f\u0440\u043e\u0441\u044b.<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\u041f\u0440\u0438\u043c\u0435\u0440<\/b><\/p>\n<div class=\"spoiler_text\">.\/effects\/auth.effects.ts  <\/p>\n<pre><code>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&lt;Action&gt; = this.actions$.pipe(     ofType('LOGIN'),     mergeMap(action =&gt;       this.http.post('\/auth', action.payload).pipe(         \/\/ If successful, dispatch success action with result         map(data =&gt; ({ type: 'LOGIN_SUCCESS', payload: data })),         \/\/ If request fails, dispatch failed action         catchError(() =&gt; of({ type: 'LOGIN_FAILED' }))       )     )   );    constructor(private http: HttpClient, private actions$: Actions) {} } <\/code><\/pre>\n<p>  \u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u044d\u0444\u0444\u0435\u043a\u0442\u0430 \u0432 \u043c\u043e\u0434\u0443\u043b\u044c<\/p>\n<pre><code>import { EffectsModule } from '@ngrx\/effects'; import { AuthEffects } from '.\/effects\/auth.effects';  @NgModule({   imports: [EffectsModule.forRoot([AuthEffects])], }) export class AppModule {} <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  <b><a href=\"https:\/\/github.com\/ngrx\/platform\/blob\/master\/docs\/entity\/README.md\">NgRx\/entity<\/a><\/b> \u2014 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u043c\u0430\u0441\u0441\u0438\u0432\u0430\u043c\u0438 \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\u041f\u0440\u0438\u043c\u0435\u0440<\/b><\/p>\n<div class=\"spoiler_text\">user.model.ts<\/p>\n<pre><code>export interface User {   id: string;   name: string; } <\/code><\/pre>\n<p>  user.actions.ts<\/p>\n<pre><code>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&lt;User&gt; }) {} }  export class UpdateUsers implements Action {   readonly type = UserActionTypes.UPDATE_USERS;    constructor(public payload: { users: Update&lt;User&gt;[] }) {} }  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; <\/code><\/pre>\n<p>  user.reducer.ts  <\/p>\n<pre><code>import { EntityState, EntityAdapter, createEntityAdapter } from '@ngrx\/entity'; import { User } from '.\/user.model'; import { UserActionsUnion, UserActionTypes } from '.\/user.actions';  export interface State extends EntityState&lt;User&gt; {   \/\/ additional entities state properties   selectedUserId: number | null; }  export const adapter: EntityAdapter&lt;User&gt; = createEntityAdapter&lt;User&gt;();  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) =&gt; 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; <\/code><\/pre>\n<p>  reducers\/index.ts<\/p>\n<pre><code>import {   createSelector,   createFeatureSelector,   ActionReducerMap, } from '@ngrx\/store'; import * as fromUser from '.\/user.reducer';  export interface State {   users: fromUser.State; }  export const reducers: ActionReducerMap&lt;State&gt; = {   users: fromUser.reducer, };  export const selectUserState = createFeatureSelector&lt;fromUser.State&gt;('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) =&gt; userEntities[userId] ); <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  <\/p>\n<h2>\u0427\u0442\u043e \u0432 \u0438\u0442\u043e\u0433\u0435?<\/h2>\n<p>  \u041c\u044b \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u044b\u0439 <b>state management<\/b> \u0441 \u043a\u0443\u0447\u0435\u0439 \u043f\u043b\u044e\u0441\u043e\u0432:<\/p>\n<p>   \u2014 \u0435\u0434\u0438\u043d\u044b\u0439 \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a \u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u043b\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f,<br \/>   \u2014 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0445\u0440\u0430\u043d\u0438\u0442\u0441\u044f \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e \u043e\u0442 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f,<br \/>   \u2014 \u0435\u0434\u0438\u043d\u044b\u0439 \u0441\u0442\u0438\u043b\u044c \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0434\u043b\u044f \u0432\u0441\u0435\u0445 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432 \u0432 \u043f\u0440\u043e\u0435\u043a\u0442\u0435,<br \/>   \u2014 <b>changeDetectionStrategy.OnPush<\/b> \u0432\u043e \u0432\u0441\u0435\u0445 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430\u0445 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f,<br \/>   \u2014 \u0443\u0434\u043e\u0431\u043d\u0430\u044f \u043e\u0442\u043b\u0430\u0434\u043a\u0430 \u0447\u0435\u0440\u0435\u0437 <b>redux-devtools<\/b>,<br \/>   \u2014 \u043b\u0435\u0433\u043a\u043e\u0441\u0442\u044c \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f, \u0442.\u043a. <b>reducers<\/b> \u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u201c\u0447\u0438\u0441\u0442\u044b\u043c\u0438\u201d \u0444\u0443\u043d\u043a\u0446\u0438\u044f\u043c\u0438.<\/p>\n<p>  <b>\u041d\u043e \u0435\u0441\u0442\u044c \u0438 \u043c\u0438\u043d\u0443\u0441\u044b:<\/b><\/p>\n<p>   \u2014 \u0431\u043e\u043b\u044c\u0448\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043d\u0435\u043f\u043e\u043d\u044f\u0442\u043d\u044b\u0445 \u043d\u0430 \u043f\u0435\u0440\u0432\u044b\u0439 \u0432\u0437\u0433\u043b\u044f\u0434 \u043c\u043e\u0434\u0443\u043b\u0435\u0439,<br \/>   \u2014 \u043c\u043d\u043e\u0433\u043e \u043e\u0434\u043d\u043e\u0442\u0438\u043f\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0435\u0437 \u0433\u0440\u0443\u0441\u0442\u0438 \u043d\u0435 \u0432\u0437\u0433\u043b\u044f\u043d\u0435\u0448\u044c,<br \/>   \u2014 \u0441\u043b\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0432 \u043e\u0441\u0432\u043e\u0435\u043d\u0438\u0438 \u0438\u0437-\u0437\u0430 \u0432\u0441\u0435\u0433\u043e \u0432\u044b\u0448\u0435 \u043f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u0435\u043d\u043d\u043e\u0433\u043e.<\/p>\n<h2>CRUD<\/h2>\n<p>  \u041a\u0430\u043a \u043f\u0440\u0430\u0432\u0438\u043b\u043e, \u0437\u043d\u0430\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u0443\u044e \u0447\u0430\u0441\u0442\u044c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0437\u0430\u043d\u0438\u043c\u0430\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430 \u0441 \u043e\u0431\u044a\u0435\u043a\u0442\u0430\u043c\u0438 (\u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435, \u0447\u0442\u0435\u043d\u0438\u0435, \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435, \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0435), \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0434\u043b\u044f \u0443\u0434\u043e\u0431\u0441\u0442\u0432\u0430 \u0440\u0430\u0431\u043e\u0442\u044b \u0431\u044b\u043b\u0430 \u043f\u0440\u0438\u0434\u0443\u043c\u0430\u043d\u0430 \u043a\u043e\u043d\u0446\u0435\u043f\u0446\u0438\u044f <b>CRUD<\/b> (Create, Read, Update, Delete). \u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, \u0431\u0430\u0437\u043e\u0432\u044b\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441\u043e \u0432\u0441\u0435\u043c\u0438 \u0442\u0438\u043f\u0430\u043c\u0438 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u044b. \u041d\u0430 \u0431\u044d\u043a\u0435\u043d\u0434\u0435 \u044d\u0442\u043e \u0443\u0436\u0435 \u0434\u0430\u0432\u043d\u043e \u043f\u0440\u043e\u0446\u0432\u0435\u0442\u0430\u0435\u0442. \u041c\u043d\u043e\u0433\u0438\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u043f\u043e\u043c\u043e\u0433\u0430\u044e\u0442 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0438 \u0438\u0437\u0431\u0430\u0432\u0438\u0442\u044c\u0441\u044f \u043e\u0442 \u0440\u0443\u0442\u0438\u043d\u043d\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u044b. <\/p>\n<p>  \u0412 <a href=\"https:\/\/github.com\/ngrx\/platform\">NgRx<\/a> \u0437\u0430 <b>CRUD<\/b> \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u043c\u043e\u0434\u0443\u043b\u044c <a href=\"https:\/\/github.com\/ngrx\/platform\/blob\/master\/docs\/entity\/README.md\">entity<\/a>, \u0438 \u0435\u0441\u043b\u0438 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043f\u0440\u0438\u043c\u0435\u0440 \u0435\u0433\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438, \u0441\u0440\u0430\u0437\u0443 \u0432\u0438\u0434\u043d\u043e, \u0447\u0442\u043e \u044d\u0442\u043e \u0441\u0430\u043c\u0430\u044f \u0431\u043e\u043b\u044c\u0448\u0430\u044f \u0438 \u043d\u0430\u0438\u0431\u043e\u043b\u0435\u0435 \u0441\u043b\u043e\u0436\u043d\u0430\u044f \u0447\u0430\u0441\u0442\u044c <a href=\"https:\/\/github.com\/ngrx\/platform\">NgRx<\/a>. \u0418\u043c\u0435\u043d\u043d\u043e \u043f\u043e\u044d\u0442\u043e\u043c\u0443 <a href=\"http:\/\/twitter.com\/John_Papa\">John Papa<\/a> \u0438 <a href=\"http:\/\/twitter.com\/wardbell\">Ward Bell<\/a> \u0441\u043e\u0437\u0434\u0430\u043b\u0438 <b>angular-ngrx-data<\/b>.<\/p>\n<h2>angular-ngrx-data<\/h2>\n<p>  <b><a href=\"https:\/\/github.com\/johnpapa\/angular-ngrx-data\">angular-ngrx-data<\/a><\/b> \u2014 \u044d\u0442\u043e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430-\u043d\u0430\u0434\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043d\u0430\u0434 <a href=\"https:\/\/github.com\/ngrx\/platform\">NgRx<\/a>, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u043c\u0430\u0441\u0441\u0438\u0432\u0430\u043c\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0431\u0435\u0437 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u043b\u0438\u0448\u043d\u0435\u0433\u043e \u043a\u043e\u0434\u0430. <br \/>  \u041f\u043e\u043c\u0438\u043c\u043e \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u043e\u0433\u043e <b>state management<\/b>, \u043e\u043d\u0430 \u0431\u0435\u0440\u0435\u0442 \u043d\u0430 \u0441\u0435\u0431\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432 \u0441 <b>http<\/b> \u0434\u043b\u044f \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0441 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c.<\/p>\n<h2>\u0420\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043d\u0430 \u043f\u0440\u0438\u043c\u0435\u0440\u0435<\/h2>\n<p>  <b>\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430<\/b><\/p>\n<pre><code>npm install --save @ngrx\/store @ngrx\/effects @ngrx\/entity @ngrx\/store-devtools ngrx-data <\/code><\/pre>\n<p>  <b>\u041c\u043e\u0434\u0443\u043b\u044c angular-ngrx-data<\/b><\/p>\n<pre><code>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 {}<\/code><\/pre>\n<p>  <b>\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435<\/b><\/p>\n<pre><code>@NgModule({   imports: [     BrowserModule,     HttpClientModule,     StoreModule.forRoot({}),     EffectsModule.forRoot([]),     EntityStoreModule,     StoreDevtoolsModule.instrument({       maxAge: 25,      }),   ],   declarations: [     AppComponent   ],   providers: [],   bootstrap: [AppComponent] }) export class AppModule {} <\/code><\/pre>\n<p>  <i>\u0422\u043e\u043b\u044c\u043a\u043e \u0447\u0442\u043e \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u0438 \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0435 <b>API<\/b> \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0431\u044d\u043a\u043e\u043c \u0438 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044e <b>API<\/b> \u0441 <b>NgRx<\/b>, <u><b>\u043d\u0435 \u043d\u0430\u043f\u0438\u0441\u0430\u0432 \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u043d\u0438 \u043e\u0434\u043d\u043e\u0433\u043e effect, reducer \u0438 action \u0438 selector.<\/b><\/u><\/i><\/p>\n<h2>\u0420\u0430\u0437\u0431\u0435\u0440\u0435\u043c \u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e \u0442\u043e, \u0447\u0442\u043e \u0442\u0443\u0442 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442<\/h2>\n<p>  \u041a\u043e\u043d\u0441\u0442\u0430\u043d\u0442\u0430 <b>defaultDataServiceConfig<\/b> \u0437\u0430\u0434\u0430\u0435\u0442 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e \u0434\u043b\u044f \u043d\u0430\u0448\u0435\u0433\u043e API \u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f \u0432 <b>providers<\/b> \u043c\u043e\u0434\u0443\u043b\u044f. \u0421\u0432\u043e\u0439\u0441\u0442\u0432\u043e <b>root<\/b> \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442, \u043a\u0443\u0434\u0430 \u043e\u0431\u0440\u0430\u0449\u0430\u0442\u044c\u0441\u044f \u0437\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u0430\u043c\u0438. \u0415\u0441\u043b\u0438 \u0435\u0433\u043e \u043d\u0435 \u0437\u0430\u0434\u0430\u0442\u044c, \u0442\u043e \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0431\u0443\u0434\u0435\u0442 \u00abapi\u00bb.<\/p>\n<pre><code>const defaultDataServiceConfig: DefaultDataServiceConfig = {  root: 'crud' };<\/code><\/pre>\n<p>  \u041a\u043e\u043d\u0441\u0442\u0430\u043d\u0442\u0430 <b>entityMetadata<\/b> \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044f \u0441\u0442\u043e\u0440\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0431\u0443\u0434\u0443\u0442 \u0441\u043e\u0437\u0434\u0430\u043d\u044b \u043f\u0440\u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 <b>NgrxDataModule.forRoot<\/b>.<\/p>\n<pre><code>export const entityMetadata: EntityMetadataMap = {  Hero: {},  User:{} }; ... NgrxDataModule.forRoot({ entityMetadata, pluralNames }) <\/code><\/pre>\n<p>  \u041f\u0443\u0442\u044c \u043a API \u0441\u043e\u0441\u0442\u043e\u0438\u0442 \u0438\u0437 \u0431\u0430\u0437\u043e\u0432\u043e\u0433\u043e \u043f\u0443\u0442\u0438 (\u0432 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u00abcrud\u00bb) \u0438 \u0438\u043c\u0435\u043d\u0438 \u0441\u0442\u043e\u0440\u0430.<br \/>  \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0441 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u043c \u043d\u043e\u043c\u0435\u0440\u043e\u043c \u043f\u0443\u0442\u044c \u0431\u0443\u0434\u0435\u0442 \u0442\u0430\u043a\u043e\u0439 \u2014 \u00abcrud\/user\/{userId}\u00bb.<\/p>\n<p>  \u0414\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u043f\u043e\u043b\u043d\u043e\u0433\u043e \u0441\u043f\u0438\u0441\u043a\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u043b\u0435\u0439 \u0432 \u043a\u043e\u043d\u0446\u0435 \u0438\u043c\u0435\u043d\u0438 \u0441\u0442\u043e\u0440\u0430 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0431\u0443\u043a\u0432\u0430 \u00abs\u00bb \u2014 \u00abcrud\/user<b>s<\/b>\u00bb.<\/p>\n<p>  \u0415\u0441\u043b\u0438 \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u043f\u043e\u043b\u043d\u043e\u0433\u043e \u0441\u043f\u0438\u0441\u043a\u0430 \u043d\u0443\u0436\u0435\u043d \u0434\u0440\u0443\u0433\u043e\u0439 \u0440\u043e\u0443\u0442 (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u00abheroes\u00bb, \u0430 \u043d\u0435 \u00abheros\u00bb), \u0435\u0433\u043e \u043c\u043e\u0436\u043d\u043e \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c, \u0437\u0430\u0434\u0430\u0432 <b>pluralNames<\/b> \u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0432 \u0438\u0445 \u0432 <b>NgrxDataModule.forRoot<\/b>.<\/p>\n<pre><code>export const pluralNames = { Hero: 'heroes' }; ... NgrxDataModule.forRoot({ entityMetadata, pluralNames }) <\/code><\/pre>\n<p>  <\/p>\n<h2>\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0432 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0435<\/h2>\n<p>  \u0414\u043b\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u0432 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u0432 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440 <b>entityServices<\/b> \u0438 \u0447\u0435\u0440\u0435\u0437 \u043c\u0435\u0442\u043e\u0434 <b>getEntityCollectionService<\/b> \u0432\u044b\u0431\u0440\u0430\u0442\u044c \u0441\u0435\u0440\u0432\u0438\u0441 \u043d\u0443\u0436\u043d\u043e\u0433\u043e \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430<\/p>\n<pre><code>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&lt;Hero[]&gt;;   heroesService: EntityCollectionService&lt;Hero&gt;;    constructor(entityServices: EntityServices) {     this.heroesService = entityServices.getEntityCollectionService('Hero');   }   ... } <\/code><\/pre>\n<p>  \u0414\u043b\u044f \u043f\u0440\u0438\u0432\u044f\u0437\u043a\u0438 \u0441\u043f\u0438\u0441\u043a\u0430 \u043a \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0443 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0432\u0437\u044f\u0442\u044c \u0438\u0437 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e <b>entities$<\/b>, \u0430 \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u0441 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0432\u044b\u0437\u0432\u0430\u0442\u044c \u043c\u0435\u0442\u043e\u0434 <b>getAll()<\/b>.<\/p>\n<pre><code>ngOnInit() {   this.heroes$ = this.heroesService.entities$;   this.heroesService.getAll(); } <\/code><\/pre>\n<p>  \u0422\u0430\u043a\u0436\u0435, \u043f\u043e\u043c\u0438\u043c\u043e \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445, \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c:<\/p>\n<p>   \u2014 <b>loaded$<\/b>, <b>loading$<\/b> \u2014 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0441\u0442\u0430\u0442\u0443\u0441\u0430 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445,<br \/>   \u2014 <b>errors$<\/b> \u2014 \u043e\u0448\u0438\u0431\u043a\u0438 \u043f\u0440\u0438 \u0440\u0430\u0431\u043e\u0442\u0435 \u0441\u0435\u0440\u0432\u0438\u0441\u0430,<br \/>   \u2014 <b>count$<\/b> \u2014 \u043e\u0431\u0449\u0435\u0435 \u043a\u043e\u043b\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0437\u0430\u043f\u0438\u0441\u0435\u0439 \u0432 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435.<\/p>\n<p>  \u041e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 \u043c\u0435\u0442\u043e\u0434\u044b \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0441 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c:<\/p>\n<p>   \u2014 <b>getAll()<\/b> \u2014 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0432\u0441\u0435\u0433\u043e \u0441\u043f\u0438\u0441\u043a\u0430 \u0434\u0430\u043d\u043d\u044b\u0445,<br \/>   \u2014 <b>getWithQuery(query)<\/b> \u2014 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0441\u043f\u0438\u0441\u043a\u0430, \u043e\u0442\u0444\u0438\u043b\u044c\u0442\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0433\u043e \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e query-\u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432,<br \/>   \u2014 <b>getByKey(id)<\/b> \u2014 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u043e\u0434\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438 \u043f\u043e \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440\u0443,<br \/>   \u2014 <b>add(entity)<\/b> \u2014 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043d\u043e\u0432\u043e\u0439 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438 \u0441 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u043c \u043d\u0430 \u0431\u044d\u043a,<br \/>   \u2014 <b>delete(entity)<\/b> \u2014 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438 \u0441 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u043c \u043d\u0430 \u0431\u044d\u043a,<br \/>   \u2014 <b>update(entity)<\/b> \u2014 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438 \u0441 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u043c \u043d\u0430 \u0431\u044d\u043a.<\/p>\n<p>  \u041c\u0435\u0442\u043e\u0434\u044b \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435\u043c:<\/p>\n<p>   \u2014 <b>addManyToCache(entity)<\/b> \u2014 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043c\u0430\u0441\u0441\u0438\u0432\u0430 \u043d\u043e\u0432\u044b\u0445 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0435\u0439 \u0432 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435,<br \/>   \u2014 <b>addOneToCache(entity)<\/b> \u2014 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043d\u043e\u0432\u043e\u0439 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438 \u0442\u043e\u043b\u044c\u043a\u043e \u0432 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435,<br \/>   \u2014 <b>removeOneFromCache(id)<\/b> \u2014 \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u043e\u0434\u043d\u043e\u0439 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438 \u0438\u0437 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430,<br \/>   \u2014 <b>updateOneInCache(entity)<\/b> \u2014 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438 \u0432 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435,<br \/>   \u2014 <b>upsertOneInCache(entity)<\/b> \u2014 \u0435\u0441\u043b\u0438 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u044c \u0441 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u043c id \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442, \u043e\u043d\u0430 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u0442\u0441\u044f, \u0435\u0441\u043b\u0438 \u043d\u0435\u0442 \u2014 \u0441\u043e\u0437\u0434\u0430\u0435\u0442\u0441\u044f \u043d\u043e\u0432\u0430\u044f,<br \/>   \u2014 \u0438 \u0434\u0440.<\/p>\n<p>  <b>\u041f\u0440\u0438\u043c\u0435\u0440 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0432 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0435<\/b><\/p>\n<pre><code>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&lt;Hero[]&gt;;   heroesService: EntityCollectionService&lt;Hero&gt;;    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);   } }<\/code><\/pre>\n<p>  \u0412\u0441\u0435 \u043c\u0435\u0442\u043e\u0434\u044b <b>angular-ngrx-data<\/b> \u0434\u0435\u043b\u044f\u0442\u0441\u044f \u043d\u0430 \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0449\u0438\u0435 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e \u0438 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0435 \u0441 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c. \u042d\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 \u043f\u0440\u0438 \u043c\u0430\u043d\u0438\u043f\u0443\u043b\u044f\u0446\u0438\u044f\u0445 \u0441 \u0434\u0430\u043d\u043d\u044b\u043c\u0438 \u043a\u0430\u043a \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0435, \u0442\u0430\u043a \u0438 \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u0441\u0435\u0440\u0432\u0435\u0440\u0430.<\/p>\n<h2>\u041b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435<\/h2>\n<p>  \u0414\u043b\u044f \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0437\u0430\u0438\u043d\u0436\u0435\u043a\u0442\u0438\u0442\u044c <a href=\"https:\/\/github.com\/johnpapa\/angular-ngrx-data\/blob\/master\/docs\/entity-services.md\">EntityServices<\/a> \u0432 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 \u0438\u043b\u0438 \u0441\u0435\u0440\u0432\u0438\u0441 \u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430:<\/p>\n<p>   \u2014 <b>reducedActions$<\/b> \u2014 \u0434\u043b\u044f \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439,<br \/>   \u2014 <b>entityActionErrors$<\/b> \u2014 \u0434\u043b\u044f \u043b\u043e\u0433\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043e\u0448\u0438\u0431\u043e\u043a.<\/p>\n<pre><code>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 =&gt; {       if (res &amp;&amp; res.type) {         this.messageService.add(res.type);       }     });   } } <\/code><\/pre>\n<p>  <\/p>\n<h2>\u041f\u0435\u0440\u0435\u0435\u0437\u0434 \u0432 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439 NgRx<\/h2>\n<p>  \u041a\u0430\u043a \u0431\u044b\u043b\u043e \u043e\u0431\u044a\u044f\u0432\u043b\u0435\u043d\u043e \u043d\u0430 <a href=\"https:\/\/nitayneeman.com\/posts\/all-talks-from-ng-conf-2018\/\">ng-conf 2018<\/a>, <b>angular-ngrx-data<\/b> \u0432 \u0431\u043b\u0438\u0436\u0430\u0439\u0448\u0435\u0435 \u0432\u0440\u0435\u043c\u044f \u0431\u0443\u0434\u0435\u0442 \u043f\u0435\u0440\u0435\u043d\u0435\u0441\u0435\u043d \u0432 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439 <b>NgRx<\/b>.<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\u0412\u0438\u0434\u0435\u043e \u0441 \u0434\u043e\u043a\u043b\u0430\u0434\u043e\u043c Reducing the Boilerplate with NgRx &#8212; Brandon Roberts &amp; Mike Ryan<\/b><\/p>\n<div class=\"spoiler_text\">\n<div class=\"oembed\">\n<div>\n<div style=\"left: 0; width: 100%; height: 0; position: relative; padding-bottom: 56.2493%;\"><iframe src=\"https:\/\/www.youtube.com\/embed\/t3jx0EC-Y3c?rel=0&amp;showinfo=1&amp;hl=en-US\" style=\"border: 0; top: 0; left: 0; width: 100%; height: 100%; position: absolute;\" allowfullscreen scrolling=\"no\"><\/iframe><\/div>\n<\/div>\n<\/div>\n<p>  <\/div>\n<\/div>\n<p>  <\/p>\n<h2>\u0421\u0441\u044b\u043b\u043a\u0438<\/h2>\n<p>  <b>\u0421\u043e\u0437\u0434\u0430\u0442\u0435\u043b\u0438 anguar-ngrx-data:<\/b><br \/>   \u2014 John Papa <a href=\"https:\/\/twitter.com\/John_Papa\">twitter.com\/John_Papa<\/a><br \/>   \u2014 Ward Bell <a href=\"https:\/\/twitter.com\/wardbell\">twitter.com\/wardbell<\/a><\/p>\n<p>  <b>\u041e\u0444\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0435 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438:<\/b><br \/>   \u2014 <a href=\"https:\/\/github.com\/ngrx\/platform\">NgRx<\/a><br \/>   \u2014 <a href=\"https:\/\/github.com\/johnpapa\/angular-ngrx-data\">angular-ngrx-data<\/a><\/p>\n<p>  <b>\u041f\u0440\u0438\u043c\u0435\u0440 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f:<\/b><br \/>   \u2014 <a href=\"https:\/\/github.com\/Angular-RU\/angular-ngrx-starter\">\u0441 NgRx \u0431\u0435\u0437 angular-ngrx-data<\/a><br \/>   \u2014 <a href=\"https:\/\/github.com\/Angular-RU\/angular-ngrx-starter\/tree\/ngrx-data\">c NgRx \u0438 angular-ngrx-data<\/a><\/p>\n<p>  <a href=\"https:\/\/t.me\/angular_ru\"><b>\u0420\u0443\u0441\u0441\u043a\u043e\u0433\u043e\u0432\u043e\u0440\u044f\u0449\u0435\u0435 Angular \u0441\u043e\u043e\u0431\u0449\u0435\u0441\u0442\u0432\u043e \u0432 Telegram<\/b><\/a><\/div>\n<p>        <script class=\"js-mediator-script\">!function(e){function t(t,n){if(!(n in e)){for(var r,a=e.document,i=a.scripts,o=i.length;o--;)if(-1!==i[o].src.indexOf(t)){r=i[o];break}if(!r){r=a.createElement(\"script\"),r.type=\"text\/javascript\",r.async=!0,r.defer=!0,r.src=t,r.charset=\"UTF-8\";var d=function(){var e=a.getElementsByTagName(\"script\")[0];e.parentNode.insertBefore(r,e)};\"[object Opera]\"==e.opera?a.addEventListener?a.addEventListener(\"DOMContentLoaded\",d,!1):e.attachEvent(\"onload\",d):d()}}}t(\"\/\/mediator.mail.ru\/script\/2820404\/\",\"_mediator\")}(window);<\/script>     <br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/post\/418369\/\"> https:\/\/habr.com\/post\/418369\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"\n<div data-io-article-url=\"https:\/\/habr.com\/post\/418369\/\" class=\"post__text post__text-html js-mediator-article\"><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/b8\/vq\/w4\/b8vqw4vomyo32ddmdrogi6lnnxo.png\" alt=\"image\"><br \/>  \u041d\u0430 \u0441\u0435\u0433\u043e\u0434\u043d\u044f\u0448\u043d\u0438\u0439 \u0434\u0435\u043d\u044c \u043d\u0438 \u043e\u0434\u043d\u043e \u0431\u043e\u043b\u044c\u0448\u043e\u0435 <b>SPA<\/b> \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043d\u0435 \u043e\u0431\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0431\u0435\u0437 <b>state management (\u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435\u043c)<\/b>. \u0414\u043b\u044f <a href=\"https:\/\/angular.io\/\"><b>Angular<\/b><\/a> \u043f\u043e \u0434\u0430\u043d\u043d\u043e\u043c\u0443 \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044e \u0435\u0441\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0440\u0435\u0448\u0435\u043d\u0438\u0439. \u0421\u0430\u043c\u044b\u043c \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u044b\u043c \u0438\u0437 \u043d\u0438\u0445 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f <a href=\"https:\/\/github.com\/ngrx\/platform\"><b>NgRx<\/b><\/a>. \u041e\u043d \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u0442 <b>Redux<\/b> \u043f\u0430\u0442\u0442\u0435\u0440\u043d \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 <b>RxJs<\/b> \u0438 \u043e\u0431\u043b\u0430\u0434\u0430\u0435\u0442 \u0445\u043e\u0440\u043e\u0448\u0438\u043c \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0435\u043c. <\/p>\n<p>  \u0412 \u0434\u0430\u043d\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u043c\u044b \u043a\u0440\u0430\u0442\u043a\u043e \u043f\u0440\u043e\u0439\u0434\u0435\u043c\u0441\u044f \u043f\u043e \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u043c \u043c\u043e\u0434\u0443\u043b\u044f\u043c <a href=\"https:\/\/github.com\/ngrx\/platform\"><b>NgRx<\/b><\/a> \u0438 \u0431\u043e\u043b\u0435\u0435 \u0434\u0435\u0442\u0430\u043b\u044c\u043d\u043e \u0441\u043e\u0441\u0440\u0435\u0434\u043e\u0442\u043e\u0447\u0438\u043c\u0441\u044f \u043d\u0430 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0435 <a href=\"https:\/\/github.com\/johnpapa\/angular-ngrx-data\"><b>angular-ngrx-data<\/b><\/a>, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u044b\u0439 <a href=\"https:\/\/ru.wikipedia.org\/wiki\/CRUD\"><b>CRUD<\/b><\/a> \u0441\u043e <b>state management<\/b> \u0437\u0430 \u043f\u044f\u0442\u044c \u043c\u0438\u043d\u0443\u0442.  <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-287344","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/287344","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=287344"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/287344\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=287344"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=287344"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=287344"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}