如何创建选择器以从ngrx商店中按ID选择项目

时间:2017-11-27 21:55:00

标签: typescript ngrx ngrx-store

我读了关于选择器的ngrx商店的documentation,并且需要弄清楚如何创建选择器以便按项目ID选择一个项目。当我从selectAllItems选择器获取所有项目时,我已经可以将此作为商店订阅的一部分,但我需要弄清楚如何从商店中的项目列表中选择特定项目。我的主要原因是createSelector提供了我喜欢的性能提升。

这是我的代码:

import { AppState, Item } from '../../shared/models/index';
import { createSelector } from '@ngrx/store';

export const selectItems = (state: AppState) => state.eventsState;

export const selectAllItems = createSelector(selectItems, (allItems: { items: Item[] }) => {
  if (allItems && allItems.items) {
   return allItems.items.filter((item: Item) => (!item.active && !item.closed));
  } else {
    return allItems.items;
  }
});

然后在我的控制器中我按ID过滤以获得所需的项目:

this.store.select(selectors.selectAllItems)
  .filter(data => data !== null)
  .subscribe(allItems => {
    this.item = allItems.filter(item => {
      return item.id === this.navParams.data.id;
    });
  });

我希望能够为该特定项目创建一个选择器并像这样使用它:

this.store.select(selectors.selectItemById(this.navParams.data.id))
  .filter(data => data !== null)
  .subscribe(selectedItem => {
    this.item = selectedItem;
  });

任何帮助将不胜感激。

3 个答案:

答案 0 :(得分:18)

经过一番研究,我解决了自己的问题。我使用工厂函数来获得所需的selectItemById选择器。这是我的新选择器:

import { AppState, Item } from '../../shared/models/index';
import { createSelector } from '@ngrx/store';

export const selectItems = (state: AppState) => state.eventsState.items;

export const getItemById = (id) => createSelector(selectItems, (allItems) => {
  if (allItems) {
    return allItems.find(item => {
      return item.itemId === id;
    });
  } else {
    return {};
  }
});

然后我在控制器中所做的就是调用这个选择器并传递一个所需项目的id来将该项目从商店中取出:

import * as selectors from '../../app/store/selectors';

this.store.select(selectors.getItemById(id))
  .subscribe((item) => {
    this.item = item;
  });

答案 1 :(得分:7)

使用实体时,一切都变得容易得多。 当您将它们存储为对象时,其中key是项目的id,value是项目本身。有两种方法可以将项目保留为数组-仅存储ID或项目的完整对象。

在减速器中:

export interface ItemsState {
  loaded: boolean;
  loading: boolean;
  items: { [key: string]: Item };
  itemIds: any[];
}

const initialState: ItemsState = {
  loaded: true,
  loading: false,
  items: {
    0: {
      foo: 'bar'
    },
    1: {
      bar: 'foo'
    }
  },
  itemIds: [0, 1]
}

在选择器中:

export const getItems = (state: ItemsState) => state.items;

export const getItemById = (id) => createSelector(
  getItems 
  (items) => items[id]
);

在容器(组件)中:

constructor(
  private store: Store,
  private route: ActivatedRoute
) {}

getItemOnInit(): void {
  // or any other way to retrieve id from route
  const id = this.route.snapshot.params.id;
  this.store.pipe(select(getItemById(id)));
}

答案 2 :(得分:1)

基于商店中可用的数据:

export const selectUser = (state: AppState) => state.selectedUser;
export const selectAllBooks = (state: AppState) => state.allBooks;

export const selectVisibleBooks = createSelector(
  selectUser,
  selectAllBooks,
  (selectedUser: User, allBooks: Books[]) => {
    if (selectedUser && allBooks) {
      return allBooks.filter((book: Book) => book.userId === selectedUser.id);
    } else {
          return allBooks;
    }
   }
);

商店中不可用的数据:

要基于商店中不可用的数据选择状态,您可以将道具传递给选择器功能。这些道具通过每个选择器和投影仪功能传递。为此,我们必须在组件内部使用选择器时指定这些道具。

例如,如果我们有一个计数器,并且想将其值相乘,则可以将相乘因子添加为prop:

选择器或投影仪的最后一个参数是props参数,在我们的示例中,它看起来如下:

export const getCount = createSelector(
  getCounterValue,
  (counter, props) => counter * props.multiply
);

我们可以在组件内部定义道具:

ngOnInit() {
  this.counter = this.store.pipe(select(fromRoot.getCount, { multiply: 2 }))
}

她是Documentation