【问题标题】:How to create selector to select an item by id from ngrx store如何创建选择器以从 ngrx 商店中按 id 选择项目
【发布时间】:2018-05-11 06:06:50
【问题描述】:

我阅读了 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;
  });

任何帮助将不胜感激。

【问题讨论】:

    标签: typescript ngrx ngrx-store


    【解决方案1】:

    经过一番研究,我解决了自己的问题。我使用工厂函数来获得所需的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;
      });
    

    【讨论】:

    • 如果您将实体存储为基于 id 的哈希表,您的访问将是 O(1),而您根本不必循环避免 O(n)。
    • 这很有帮助。我想知道是否也有办法为这些工厂的选择器链接。就像我们如何使用 createSelector 方法从现有的选择器创建子选择器一样。我想知道工厂是否也有办法做到这一点。
    • 如果 id 发生变化,如何触发对商店的新选择?
    【解决方案2】:

    使用实体时,一切都变得容易得多。 当您将它们存储为对象时,其中键是项目的id,值是项目本身。有两种方法可以将项保存为数组 - 仅存储 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)));
    }
    

    【讨论】:

    • 我认为您在 getItems 之后的 getItemById 选择器中缺少逗号
    【解决方案3】:

    根据商店中可用的数据:

    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;
        }
       }
    );
    

    商店中没有的数据:

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

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

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

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

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

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

    她的是Documentation。

    【讨论】:

      猜你喜欢
      • 2018-03-25
      • 1970-01-01
      • 2019-09-30
      • 2019-08-18
      • 1970-01-01
      • 2018-07-26
      • 1970-01-01
      • 1970-01-01
      • 2015-07-22
      相关资源
      最近更新 更多