Использование NgRx для интеграции данных объекта в компонент с локальным дублированием - PullRequest
0 голосов
/ 07 декабря 2018

В моем приложении Angular у меня есть страница с набором фильтров, которые можно переключать и затем отправлять сразу.

Я построил это с помощью базового сервиса, который имеет объект фильтров (имяфильтр, указывающий на значение фильтра).Структура данных этого сервиса дублируется в локальную версию в компоненте (localFilters), которая обновляется по мере того, как пользователь нажимает флажки и т. Д. Если пользователь нажимает кнопку для отправки фильтров, локальные фильтры устанавливаются наслужба глобальных фильтров, и если пользователь завершает работу без отправки, он не обновляет глобальную службу (и localFilters очищается при выходе).

У меня возникли проблемы с получением компонентов, использующих этооднако данные должны оставаться в синхронизации со службой и другими компонентами, которые ее используют, поэтому я решил попробовать NgRx, понимая, что этот шаблон на основе наблюдаемых данных более типичен для Angular и ранее использовал Redux в ряде проектов React.

У меня возникают серьезные проблемы с его настройкой по двум причинам:

  1. Шаблон, который я использовал ранее со службой, включал установку localFilters объект, при монтировании компонента, основанный на состоянии объекта глобальных фильтров.Эти localFilters будут использоваться для определения начального состояния фильтров на странице и глобальной настройки при отправке.Однако с наблюдаемым шаблоном, который использует NgRx, нет объекта filters для копирования - есть только наблюдаемый объект, и поэтому я не могу понять, как инициализировать объект localFilters.Как следствие, я не знаю, как установить состояние по умолчанию для различных компонентов фильтра из этого глобального объекта.

  2. По сути, я не знаю, как отобразить значения фильтра в шаблоне (особенно это необходимо, если я не могу скопировать его данные в локальный объект).Основные документы по началу работы с 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) });

Не уверен, что циклически перебирает ключи / значенияфильтра возможно.

1 Ответ

0 голосов
/ 07 декабря 2018

Я нашел ответ после просмотра этого немного устаревшего, но полезного примера видео (для тех, кто обнаружил, что документация довольно значительно отсутствует).Ничего слишком сумасшедшего.Я просто не совсем понял, как интегрируются RxJs.

Мой код компонента - это все, что нужно было изменить:

import { Store, select } from '@ngrx/store';
import { Observable } from 'rxjs';

// These are local files. The @ format is just part
// of some path aliasing I've set up.
import { SetFilters } from '@store/filters/actions';
import { AppState } from '@store/reducers'; // Reducer interface

@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. */
  selectedIndices: any = {};

  /** Duplicate all filters locally, to save on submit and clear on cancel */
  localFilters: any = {};

  /** Filters reducer */
  filters$: Observable<object>;

  constructor(private store: Store<AppState>) {
    this.filters$ = this.store.pipe(select('filters'));
    this.initLocalFilters();
  }

  ngOnInit() {}

  /**
   On component mount, clear any preexisting filters (if not dismounted),
   subscribe to filters in store, and initialize selectedIndices from filters.
  */
  initLocalFilters () {
    this.localFilters = {};

    this.filters$.subscribe(filters => {
      this.localFilters = { ...filters };
    });

    // Fill pre-selections from filter service
    ['this', 'is a list of', 'names of filters with', 'an array of options']
      .forEach((arrayKey) => {
        this.selectedIndices[arrayKey] = (this.localFilters[arrayKey] || [])
          .map(t => this[arrayKey].indexOf(t));
      });
  }

  ...

  submitFilters() {
    this.store.dispatch(new SetFilters(this.localFilters));
  }
}

Очевидно, что это не решает вопрос напрямую 2 (вопрос о значениях шаблонных объектов), но это делает его спорным, потому что я могу легко дублировать содержимое хранилища локально и обновлять его при отправке.

...