Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Select filtered relations #1445

Open
webstik1811 opened this issue Apr 26, 2022 · 5 comments
Open

Select filtered relations #1445

webstik1811 opened this issue Apr 26, 2022 · 5 comments

Comments

@webstik1811
Copy link

webstik1811 commented Apr 26, 2022

Here is my case. I have two stores: orders and articles. Order is connected with articles with one to many relation.
artciles contain orderId which is the relational key .
Here is the structure of both objects:

export interface IOrders {
  id: string;
  tableName: string;
  operator: string;
  createdAt: Date;
  elements: IArticles[];
  ...
}

export interface IArticles {
  id: string;
  orderId: string;
  quantity: number;
  article: string;
  description: string;
  status: ArticleStatus
  ...
}

So, what I have into the selectors:
First I need to fetch all articleIds which have a specific status

const allArticleIds = createSelector(selectArticleState, state => Object.values(state.entities)
  .filter(article => [ArticleStatus.ORDERED, ArticleStatus.RETURNED, ArticleStatus.LOCKED, ArticleStatus.CANCELED].includes(article.status))
  .map(article => article.id));

then lets try to create a selector which will return only desired articles:

...
export const selectArticleState = createFeatureSelector<fromArticles.State>('articles');
...
const selectAllOrderedArticles = createSelector(
  s => s,
  // selecting ids
  allOrderedArticleIds,
  selectArticleState,
);

If I create a store selector from the last:

public readonly articles$ = this.store.select(selectAllOrderedArticles);

and subscribe to that, as result will have only the filtered articles. This is great, but now I want to select orders, with their ONLY FILTERED articles! How I can do that?

What I've trying is following:

export const selectOrderState = createFeatureSelector<fromOrders.State>('orders');
...

const selectOrderWithArticles = rootEntity(
  selectOrderState,
  childrenEntities(
    selectAllOrderedArticles,
    'orderId',
    'elements',
  ),
);

const selectOrdersWithArticles = rootEntities(selectOrderWithArticles);

const allOrdersIds = createSelector(selectOrderState, state => state.ids);

const selectAllOrders = createSelector(
  // selecting the whole store
  s => s,
  // selecting ids
  allOrdersIds,
  // selecting all users with desired relationships
  selectOrdersWithArticles,
);

I expected selectOrderWithArticles to return orders with the filtered articles because I've used selectAllOrderedArticles, but actually I receive all articles instead.

Do I have something wrong, or there is a bug in the package?

@satanTime
Copy link
Owner

Hi there,

you can either pass desired selector (which filters items) to createSelector, or filter selected items afterwards in pipe with filter operator.

@nep7un
Copy link

nep7un commented May 14, 2022

Hi,
can you give an example please for the first use case?

@satanTime
Copy link
Owner

satanTime commented May 17, 2022

Here you go, an example how to create a selector just for active users.

https://codesandbox.io/s/happy-bas-dhu0fp?file=/src/app/store/reducers.ts line 84+
https://codesandbox.io/s/happy-bas-dhu0fp?file=/src/app/app.component.ts line 28

@nep7un
Copy link

nep7un commented May 17, 2022

thanks, it's super useful!
I tried to apply the same logic but I have parameters for the filter selector (selectProductsByCollection ). Here is where I'm at currently. I'm struggling to call the selector like this this.store.select(selectProductsByCollection(this.collectionKey)).subscribe(k => console.log(k)) with NGRX 12. Can you please extend your example with a filter Selector with parameters?

import { createFeatureSelector, createSelector, select } from "@ngrx/store";
import { relatedEntitySelector, rootEntities, rootEntitySelector, toFactorySelector } from "ngrx-entity-relationship";
import { ArtistState } from "../artist/artist.state";
import { CollectionState } from "../collection/collection.state";
import { ProductAdapter, ProductState } from "./product.state";


export const selectProducts = createFeatureSelector<ProductState>("products");
export const selectArtists = createFeatureSelector<ArtistState>("artists");
export const selectCollections = createFeatureSelector<CollectionState>("collections");

const {
    selectIds,
    selectEntities,
    selectAll,
    selectTotal,
} = ProductAdapter.getSelectors();

// creating selector producers for Product and its relationships
const rootProduct = rootEntitySelector(selectProducts);
const relProductArtist = relatedEntitySelector(selectArtists, 'artistId', 'artist');
const relProductCollection = relatedEntitySelector(selectCollections, 'collectionId', 'collection');

export const selectProductById =
    toFactorySelector(rootEntities(rootProduct(
        relProductArtist(),
        relProductCollection()
    )));

export const selectProductsByCollection = (key: string) => rootEntities(rootEntitySelector(createSelector(
    selectProducts,
    (state): ProductState => {
        const ids = selectAll(state).filter(product => product.collection?.key === key).map(product => product.tokenId)
        const entities = ids.reduce((acc, item) => {acc[item] = selectEntities(state)[item]; return acc;}, {});

        return {
          ...state,
          entities,
          ids
        }
    }
))(relProductArtist(), relProductCollection()));

@satanTime
Copy link
Owner

satanTime commented May 18, 2022

ah, I think, everything is much simpler here, you can use a pipe operator: https://ngrx-entity-relationship.sudo.eu/api/rxjs/relationships

this.store.pipe(
  switchMap(state => {
    return this.store
      .select(selectProductsByCollection(this.collectionKey)) // <- should be a normal selector of products only
      .pipe(relationships(store, selectProductById)); // <- should be a ngrx-entity-relationships selector to fill related entities
  })
).subscribe(k => console.log(k)) 

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants