How to create selector to select an item by id from ngrx store

Everything becomes much easier when using entities. When you store them as an object, where key is an id of item and value is the item itself. There are two ways for keeping items as an array - store only ids or full objects of Item.

in reducer:

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]
}

in selectors:

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

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

in container(component):

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

Based on the data available in the store:

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

Data that isn't available in the store:

To select a piece of state based on data that isn't available in the store you can pass props to the selector function. These props gets passed through every selector and the projector function. To do so we must specify these props when we use the selector inside our component.

For example if we have a counter and we want to multiply its value, we can add the multiply factor as a prop:

The last argument of a selector or a projector is the props argument, for our example it looks as follows:

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

Inside the component we can define the props:

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

Hers is the Documentation.


After some research I solved my own problem. I used factory function to get desired selectItemById selector. Here is my new selector:

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

Then all I have to do in my controller is to call this selector and pass it an id of desired item to get the item out of the store:

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

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