В моем приложении Angular у меня есть страница с набором фильтров, которые можно переключать и затем отправлять сразу.
Я построил это с помощью базового сервиса, который имеет объект фильтров (имяфильтр, указывающий на значение фильтра).Структура данных этого сервиса дублируется в локальную версию в компоненте (localFilters
), которая обновляется по мере того, как пользователь нажимает флажки и т. Д. Если пользователь нажимает кнопку для отправки фильтров, локальные фильтры устанавливаются наслужба глобальных фильтров, и если пользователь завершает работу без отправки, он не обновляет глобальную службу (и 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;
}
}
Сокращенный AppModule:
import { StoreModule } from '@ngrx/store';
import { reducer } from './ngrx/filters/reducer';
@NgModule({
declarations: [...],
imports: [
...,
StoreModule.forRoot({ filters: reducer })
],
...
})
И сокращенная версия соответствующего компонента.ts file:
@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) });
Не уверен, что циклически перебирает ключи / значенияфильтра возможно.