I have a component which have a role as a widget in a dashboard. So, I'll use an *ngFor
to render as many widgets as the dashboard have. The WidgetComponent
is only one and receive a part of its data by @Input()
from the parent.
parent
<app-widget *ngFor="let widget of widgets"
[widget]="widget">
</app-widget>
In the child, I listen an event using NGRX Selectors:
this.store.pipe(
select(fromStore.selectPage, {widgetId: this.widgetId, page: this.paginator.currentPage}),
distinctUntilChanged() // can be used, but is unnecessary
).subscribe(rows => {
console.log(rows);
});
When I want to change the page, I dispatch a new event in my store:
ngAfterViewInit(): void {
const pages = currentPage < 3
? [1, 2, 3]
: [currentPage - 1, currentPage, currentPage + 1];
const request: PaginatedData = {
widgetId: this.widget.id,
itemsPerPage: paginator.itemsPerPage,
pages
};
this.store.dispatch(updatePagesAction({request}));
}
selector:
export const selectPage = createSelector(selectState, (state, props) => {
const table = state.widgetTables.find(x => x.widgetId === props.widgetId);
if (typeof table === 'undefined') {
return [];
}
const existingPageKey = Object.keys(table.pages).find(key => key === props.page.toString());
return existingPageKey ? table.pages[existingPageKey] : [];
});
Problem: When I dispatch an action for a widget, there will be fired the selector for all widgets which listen in same time at the store.
I need to fire the selector only for in cause widget. The problem can be that I use the same selector for all widgets?
I can not use a filter()
in my widget component pipe()
because even if I use something like filter(x => x.widgetId === this.widget.Id)
, the event will be fired and all widgets will receive again the data, even if is equals with the last value.
Ah, I know: this can be due of at every pange changed, my store return a new state (for all widgets) and so the selectors are fired for all.
Also, I have this feature stored in a service which works very well, but because the app use already ngrx in another modules, I'm thought that is better to align all data which must be saved in memory and used later, to be saved inside a ngrx store (and not using custom services).
thanks
I think you can use a function that returns a selector instead, Try to implement like below
export const selectPageWith = ({widgetId, page}: widgetId: number, page: any) =>
createSelector(selectState, state => {
const table = state.widgetTables.find(x => x.widgetId === widgetId);
if (typeof table === 'undefined') {
return [];
}
const existingPageKey = Object.keys(table.pages).find(key => key === page.toString());
return existingPageKey ? table.pages[existingPageKey] : [];
})
Now you can use this in your component like
this.store.pipe(
select(fromStore.selectPageWith({widgetId: this.widgetId, page: this.paginator.currentPage}),
distinctUntilChanged() // can be used, but is unnecessary
).subscribe(rows => {
console.log(rows);
});
Simply we are trying to create unique selectors for each of the widget. By creating a function that returns a selector, different parameters produces different selectors for each widget