Чтобы сделать ваш компонент как можно более тонким, я предлагаю абстрагировать состояние компонента в виде фасада . Основная идея здесь заключается в том, чтобы внедрить сервисы компонентного уровня для хранения бизнес-сущности, изображенной в вашем компоненте-контейнере.
State | RxAngular.
Да, на этот раз мы будем использовать вышеупомянутую библиотеку, чтобы продемонстрировать то же самое на следующем примере:
// state for the open events component { count: number; pageNumber: number; events: IUserEvent[]; }
Зависит от текущей страницы и идентификатора пользователя, для которого нужно получить события
userEvents$ = combineLatest([ this.userId$, // observable for retrieving userId this.select('pageNumber'), // this is how we listen to a peice of state in RxState ]).pipe( switchMap(([userId, pageNumber]) => this.fetchUserEvents(userId, pageNumber) // API call to fetch user events ), map(({ data }) => data) );
Поддержание состояния userEvents для каждого выдаваемого набора значений
this.connect(this.userEvents$, (oldState, newEmittedValue) => { if (oldState.pageNumber > 1) { // for pagination return { events: oldState.events.concat(newEmittedValue.events), }; } return value; // reset the state to the new emitted value (page 1 events) });
Существует также действие (закрытие этих событий), давайте определим его
// Action to be fired from within the component closeEvents$$ = new Subject<{ eventsToClose: IUserEvent[]; resolution: Record<'reason' | 'note', string>; }>();
События закрыты, выполняется вызов API обновления и помечаются как закрытые. Также они должны быть вычтены из коллекции открытых событий. Это порождает связанное состояние (eventsClosed$) для прослушивания, которое должно сработать после закрытия событий.
eventsClosed$ = this.closeEvents$$.pipe( // on closeEvents$$ action exhaustMap(({ eventsToClose, resolution }) => this.closeEvents(eventsToClose, resolution) // Api call to persist the change ), share() );
Манипулирование состоянием после закрытия выбранных событийКаждый раз, когда выбранное событие закрыто
this.connect(this.eventsClosed$, (oldState, value) => { const closedEventsIdSet = value.reduce( (acc, e) => acc.add(e.id), new Set() ); // updated state return { events: oldState.events.filter( (oEvent) => closedEventsIdSet.has(oEvent.id) === false ), count: oldState.count - value.length, }; });
interface ICloseEventsState { count: number; pageNumber: number; events: IUserEvent[]; } enum EventStatus { open = 0, close = 1, } @Injectable() export class CloseEventsState extends RxState<ICloseEventsState> { readonly closeEvents$ = this.select('events'); readonly totalCount$ = this.select('count'); private http = inject(HttpClient); private store = inject(Store); private userId$ = this.store.select(USER_STATE_TOKEN).pipe( map(({ id }) => id), distinctUntilChanged() ); private userClosedEvents$ = combineLatest([ this.userId$, this.select('pageNumber'), ]).pipe( switchMap(([userId, pageNumber]) => this.fetchUserEvents(userId, pageNumber) ), map(({ data }) => data) ); constructor(private openEventsState: OpenEventsState) { super(); this.set({ pageNumber: 1, }); this.connect(this.userClosedEvents$, (oldState, value) => { if (oldState.pageNumber > 1) { return { events: oldState.events.concat(value.events), }; } return value; }); // Derving state based on the event fired in facade for open event list this.connect(this.openEventsState.eventsClosed$, (oldState, value) => ({ events: value.concat(oldState.events), count: oldState.count + value.length, })); } private fetchUserEvents(userId: string, pageNumber: number) { return this.http .get<IResponse<IUserEventsData>>( `vital/v1/users/${userId}/events`, { params: { status: EventStatus.close, pageNumber, recordPerPage: 25, }, } ); } }
Если UserEventsComponent является вашим компонентом-контейнером для обоих компонентов представления (OpenEventsComponent, ClosedEventsComponent). Тогда эти состояния (OpenEventsState, ClosedEventsState) должны быть предоставлены на вашем компоненте-контейнере, а именно UserEventsComponentS
@Component({ ... providers: [OpenEventsState, ClosedEventsState] }) export class UserEventsComponent { ... ... }
На этом я оставляю вас с задачей найти множество возможностей абстрагировать состояние вашего контейнерного компонента в этих элегантных фасадах.
20.08.2023 18:21
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в 2023-2024 годах? Или это полная лажа?".
20.08.2023 17:46
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
19.08.2023 18:39
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в частности, магию поплавков и гибкость flexbox.
19.08.2023 17:22
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для чтения благодаря своей простоте. Кроме того, мы всегда хотим проверить самые последние возможности в наших проектах!
18.08.2023 20:33
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий их языку и культуре.
14.08.2023 14:49
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип предназначен для представления неделимого значения.