Entity state Для эффективного управления массивами сущностей хранилища в NgRx имеется Entity State, который предоставляет собственное API для изменения этих сущностей и обеспечивает:
меньшее по объему написание кода для создания редюсеров, которые обрабатывают массив сущностей; оптимизированное по производительности выполнение всех CRUD операций над массивом или его отдельным элементом. NgRx Entity State устанавливается отдельно.
npm i @ngrx/entity --save
Entity State Entity State представляет собой обычный интерфейс обобщенного типа, в котором обобщенным типом является модель (интерфейс) сущности, составляющей массив. Ниже приведен код интерфейса.
interface EntityState < V > {
ids : string [] | number [];
entities : { [ id : string | id : number ] : V };
}
Свойства интерфейса:
ids
— массив идентификаторов сущностей (по умолчанию id
); entities
— объект со всеми сущностями, в котором ключ — идентификатор сущности, а значение — сама сущность. Часть состояния, которая обрабатывает массивы сущностей, должна реализовывать интерфейс NgRx Entity State.
interface Article {
id : number ;
user_id : number ;
title : string ;
}
export interface State extends EntityState < Article > {
currentArticle : number | null ;
}
Entity Adapter Доступ к API для работы с сущностями предоставляет экземпляр класса EntityAdapter
, который создается с явным указанием типа сущности.
export const adapter : EntityAdapter < Article > = createEntityAdapter <
Article
> ();
Метод createEntityAdapter()
принимает необязательный параметр — объект с свойствами:
selectId
метод для выбора идентификатора сущности, обязателен, если у сущности отсутствует поле id
; sortComparer
— функция для сортировки сущностей в массива, но помните, что CRUD операции будут выполняться быстрее, если не придется сортировать массив. export const adapter : EntityAdapter < Article > = createEntityAdapter <
Article
> ({
selectId : ( item ) => item . id ,
sortComparer : false , //явное указание, что сортировать массив не нужно
});
NgRx Entity Adapter имеет обширное API для работы с сущностями:
getInitialState()
— возвращает исходное состояние для массива сущностей заданного типа, параметром принимает объект со свойствами, которые также должны быть частью состояния; addOne()
— добавляет в массив новую сущность; addMany()
— добавляет несколько сущностей в массив; addAll()
— заменяет все текущие записи переданными; removeOne()
— удаляет одну указанную сущность; removeMany()
— удаляет несколько заданных сущностей; removeAll()
— удаляет все записи; updateOne()
— обновляет указанную сущность; updateMany()
— обновляет несколько заданных записей; upsertOne()
— обновляет переданную сущность, если она уже есть в массиве, в противном случае добавляет ее как новую; upsertMany()
— то же самое, что и upsertOne()
, только может принимать массив записей; map()
— применяет к каждой сущности массива переданную функцию, которая должна возвращать обновленную сущность. Пример использования метода getInitialState()
.
export const initialState : State = adapter . getInitialState ({
currentArticle : null ,
});
Пример использования методов управления массивом.
article.actions.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96 export enum ArticleActionTypes {
LoadArticles = '[Articles Page] Load Articles' ,
AddArticle = '[Articles Page] Add Article' ,
UpsertArticle = '[Articles Page] Upsert Article' ,
AddArticles = '[Articles Page] Add Articles' ,
UpsertArticles = '[Articles Page] Upsert Articles' ,
UpdateArticle = '[Articles Page] Update Article' ,
UpdateArticles = '[Articles Page] Update Articles' ,
MapArticles = '[Articles Page] Map Articles' ,
DeleteArticle = '[Articles Page] Delete Article' ,
DeleteArticles = '[Articles Page] Delete Articles' ,
DeleteAllArticles = '[Articles Page] Delete All Articles' ,
}
export class LoadArticles implements Action {
readonly type = ArticleActionTypes . LoadArticles ;
constructor ( public payload : { articles : Article [] }) {}
}
export class AddArticle implements Action {
readonly type = ArticleActionTypes . AddArticle ;
constructor ( public payload : { article : Article }) {}
}
export class UpsertArticle implements Action {
readonly type = ArticleActionTypes . UpsertArticle ;
constructor ( public payload : { article : Article }) {}
}
export class AddArticles implements Action {
readonly type = ArticleActionTypes . AddArticles ;
constructor ( public payload : { articles : Article [] }) {}
}
export class UpsertArticles implements Action {
readonly type = ArticleActionTypes . UpsertArticles ;
constructor ( public payload : { articles : Article [] }) {}
}
export class UpdateArticle implements Action {
readonly type = ArticleActionTypes . UpdateArticle ;
constructor (
public payload : { article : Update < Article > }
) {}
}
export class UpdateArticles implements Action {
readonly type = ArticleActionTypes . UpdateArticles ;
constructor (
public payload : { articles : Update < Article > [] }
) {}
}
export class MapArticles implements Action {
readonly type = ArticleActionTypes . MapArticles ;
constructor (
public payload : { entityMap : EntityMap < Article > }
) {}
}
export class DeleteArticle implements Action {
readonly type = ArticleActionTypes . DeleteArticle ;
constructor ( public payload : { id : string }) {}
}
export class DeleteArticles implements Action {
readonly type = ArticleActionTypes . DeleteArticles ;
constructor ( public payload : { ids : string [] }) {}
}
export class DeleteAllArticles implements Action {
readonly type = ArticleActionTypes . DeleteAllArticles ;
}
export type ArticleActionsUnion =
| LoadArticles
| AddArticle
| UpsertArticle
| AddArticles
| UpsertArticles
| UpdateArticle
| UpdateArticles
| MapArticles
| DeleteArticle
| DeleteArticles
| DeleteAllArticles ;
article.reducer.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105 interface Article {
id : number ;
user_id : number ;
title : string ;
}
export interface State extends EntityState < Article > {
currentArticle : number | null ;
}
export const adapter : EntityAdapter < Article > = createEntityAdapter <
Article
> ();
export const initialState : State = adapter . getInitialState ({
currentArticle : null ,
});
export function articlesReducer (
state = initialState ,
action : ArticleActionsUnion
) : State {
switch ( action . type ) {
case ArticleActionTypes . LoadArticles : {
return adapter . addAll (
action . payload . articles ,
state
);
}
case ArticleActionTypes . AddArticle : {
return adapter . addOne (
action . payload . article ,
state
);
}
case ArticleActionTypes . UpsertArticle : {
return adapter . upsertOne (
action . payload . article ,
state
);
}
case ArticleActionTypes . AddArticles : {
return adapter . addMany (
action . payload . articles ,
state
);
}
case ArticleActionTypes . UpsertArticles : {
return adapter . upsertMany (
action . payload . articles ,
state
);
}
case ArticleActionTypes . UpdateArticle : {
return adapter . updateOne (
action . payload . article ,
state
);
}
case ArticleActionTypes . UpdateArticles : {
return adapter . updateMany (
action . payload . articles ,
state
);
}
case ArticleActionTypes . MapArticles : {
return adapter . map (
action . payload . entityMap ,
state
);
}
case ArticleActionTypes . DeleteArticle : {
return adapter . removeOne (
action . payload . id ,
state
);
}
case ArticleActionTypes . DeleteArticles : {
return adapter . removeMany (
action . payload . ids ,
state
);
}
case ArticleActionTypes . DeleteAllArticles : {
return adapter . removeAll ({
... state ,
currentArticle : null ,
});
}
default : {
return state ;
}
}
}
Еще одним полезным методом NgRx Entity Adapter является getSelectors()
, который возвращает четыре селектора:
selectIds
— возвращает массив идентификаторов сущностей; selectEntities
— возвращает объект, в котором ключи это идентификаторы записей, а значения — сами записи; selectAll
— возвращает массив всех сущностей; selectTotal
— возвращает общее количество записей в массиве. const {
selectIds ,
selectEntities ,
selectAll ,
selectTotal ,
} = adapter . getSelectors ();
export const selectArticleIds = selectIds ;
export const selectArticleEntities = selectEntities ;
export const selectAllArticles = selectAll ;
export const selectArticleTotal = selectTotal ;