В моем приложении Angular у меня есть страница с набором фильтров, которые можно переключать, а затем сразу же отправлять.
Я построил это с помощью базовой службы, в которой есть объект фильтров (имя фильтра, указывающее на значение фильтра). Структура данных этой службы дублируется в локальную версию в компоненте (localFilters
), которая обновляется по мере того, как пользователь нажимает флажки и т. Д. Если пользователь нажимает кнопку для отправки фильтров, локальные фильтры устанавливаются на глобальные фильтры. service, и если пользователь выходит без отправки, он не обновляет глобальную службу (и localFilters
очищаются при выходе).
Однако у меня возникли проблемы с получением компонентов, которые используют эти данные для синхронизации со службой и другими компонентами, которые их используют, поэтому я решил попробовать NgRx, понимая, что этот шаблон на основе наблюдаемых более типичен для Angular и имеет ранее использовал Redux в ряде проектов React.
Однако у меня возникают серьезные проблемы с его настройкой по двум причинам:
Шаблон, который я использовал раньше со службой, включал установку объекта
localFilters
при монтировании компонента на основе состояния объекта глобальных фильтров. ЭтиlocalFilters
будут использоваться для определения начального состояния фильтров на странице и установлены глобально при отправке. Однако с наблюдаемым шаблоном, который использует NgRx, нет объектаfilters
для копирования - есть только наблюдаемый объект, и, следовательно, я не могу понять, как инициализировать объектlocalFilters
. Как следствие, я не знаю, как установить состояние по умолчанию для различных компонентов фильтра из этого глобального объекта.По сути, я не знаю, как отображать значения фильтра в шаблоне (особенно это необходимо, если я не могу скопировать его данные в локальный объект). Базовые документы по началу работы с NgRx показывают, как включить числовое значение в шаблон с помощью канала
async
, но поскольку мои данные находятся в объектной форме, и я хочу передать значения этого объекта, этот метод не работает. Я пробовал следующие попытки на основе приведенной выше ссылки -filters$ | async
(показывает[Object object]
),filters$.someKey | async
(ничего не показывает) и(filters$ | async).someKey
(аналогично ничего не показывает).
По сути, большой вопрос заключается в том, как получить доступ к моментальному снимку объекта, хранящегося в состоянии NgRx, как для инициализации локального состояния этого компонента фильтров, так и для визуализации значений из объекта (или передачи этих значений) в шаблоне.
Или есть лучший образец, которому я должен следовать? (Хорошие примеры найти было трудно, и мы будем очень признательны).
Ниже приведена связка моего соответствующего кода.
Файл действий:
import { Action } from '@ngrx/store';
export enum ActionTypes {
SetFilter = 'SetFilter',
SetFilters = 'SetFilters',
ClearFilters = 'ClearFilters',
}
export class SetFilter implements Action {
readonly type = ActionTypes.SetFilter;
constructor(public name: string, public value: any) {}
}
export class SetFilters implements Action {
readonly type = ActionTypes.SetFilters;
constructor(public filters: object) {}
}
export class ClearFilters implements Action {
readonly type = ActionTypes.ClearFilters;
}
export type ActionsUnion = SetFilter | SetFilters | ClearFilters;
Файл-редукторы:
import * as FilterActions from './actions';
export interface State {
filters: object
};
export const initialState: State = {
filters: { wassup: 'true' } // testing initial state with some nonsense
};
export function reducer(state = initialState, action: FilterActions.ActionsUnion) {
switch (action.type) {
case FilterActions.ActionTypes.SetFilter: {
return { ...state, [action.name]: action.value };
}
case FilterActions.ActionTypes.SetFilters: {
return { ...state, ...action.filters };
}
case FilterActions.ActionTypes.ClearFilters: {
return {};
}
default: return state;
}
}
Сокращенный модуль приложения:
import { StoreModule } from '@ngrx/store';
import { reducer } from './ngrx/filters/reducer';
@NgModule({
declarations: [...],
imports: [
...,
StoreModule.forRoot({ filters: reducer })
],
...
})
И сокращенная версия соответствующего файла component.ts:
@Component({
selector: 'app-base-filter',
templateUrl: './base-filter.component.html',
styleUrls: ['./base-filter.component.scss']
})
export class BaseFilterComponent implements OnInit {
/** Object with selected indices for given filter keys. */
selectedIndices: any = {};
/** Duplicate all filters locally, to save on submit and clear on cancel */
localFilters: any = {};
filters$: Observable<object>;
constructor(private store: Store<{ filters: object }>) {
this.filters$ = store.pipe(select('filters'));
this.initLocalFilters();
}
ngOnInit() {}
// This worked with the old filtersService model
// But is obviously broken here, because I haven't been able to init
// localFilters correctly.
initLocalFilters () {
this.localFilters = {};
// Fill pre-selections from filter service
['this', 'is a list of', 'names of filters with', 'an array of options']
.forEach((arrayKey) => {
// The selected indices are used in the template to pass to child
// components and determine selected content.
this.selectedIndices[arrayKey] = (this.localFilters[arrayKey] || [])
.map(t => this[arrayKey].indexOf(t));
});
}
});
Между прочим, я пробовал некоторые из приведенных ниже в конструкторе компонентов выше:
// Doesn't throw an error, but doesn't enter the callback
this.store.select(data => { console.log(data) });
// Doesn't throw an error, but filter is undefined inside the loop
this.filters$ = store.pipe(select('filters'));
this.filters$.forEach(filter => { console.log(filter) });
Не уверен, возможен ли цикл по ключам / значениям фильтра.