I would like to know how the setFilter method works for the EntityCollectionService in @ngrx/data. The documentation hints at how it is used, but there is no example showing the actual setFilter(pattern: any) function being used. Since the argument can be of type any, I cannot really infer what should be done here.
Basically, I have a list of objects in the data store using the @ngrx/data module. I would like to define a filter so that I can subscribe to the filteredEntities$ observable of the EntityCollectionService. I can successfully subscribe to the entities$ observable and receive the full unfiltered list. Previously, I was doing the filtering outside of the EntityCollectionService, but I would like to utilize the built-in filtering mechanism.
export class MyComponent implements OnInit {
filteredProjects$: Observable<Project[]>;
typeFilterOptions: FilterOption[];
stageFilterOptions: FilterOption[];
constructor(private projectService: ProjectEntityService, ptivate metadataService: MetadataService) {}
ngOnInit() {
this.typeFilterOptions = this.metadataService.getProjectTypes();
this.stageFilterOptions = this.metadataService.getProjectStages();
this.filteredProjects$ = this.projectService.filteredEntities$;
}
onFilterChange() {
typeFilter = typeFilterOptions.filter(option => option.isChecked).map(option.name);
stageFilter = stageFilterOptions.filter(option => option.isChecked).map(option.name);
this.projectService.setFilter(project => {
return (typeFilter.indexOf(project.type) >= 0) &&
(stageFilter.indexOf(project.stage) >= 0);
}
}
}
The above code is my best approach at trying to set the filter correctly. Obviously, that is not working as I expected it would. When setting the filter to a filter function nothing changes even though I can see the set filter
action firing as expected. The entities are still not being filtered at that point. The argument being label as pattern: any
make me think that it should be something other than a function, but again I cannot infer off of the documentation what it is expecting.
Ok so digging into the source code I was able to figure out how to use the filter on an ngrx/data Entity Service.
The piece I was missing was defining the filter function in the Entity Service metadata configuration (see docs here):
app.module.ts
const entityMetadata: EntityMetadataMap = {
Project: {
//pattern can be any object you want it to be. This is the same argument used in setFilter(pattern: any)
filterFn: (entities: Project[], pattern: {types: string[], stages: string[]}) => {
return entitites.filter(entity => {
return (pattern.types.indexOf(entity.type) >= 0) &&
(pattern.stages.indexOf(entity.stage) >= 0)
});
}
}
};
@NgModule({
...
})
export class AppModule {
constructor(private eds: EntityDefinitionService) {
eds.registerMetadataMap(entityMetadata);
}
}
then in the component all you need to do is create the filter object and use it as the argument to setFilter on the Entity Service:
my.component.ts
export class MyComponent implements OnInit {
filteredProjects$: Observable<Project[]>;
typeFilterOptions: FilterOption[];
stageFilterOptions: FilterOption[];
constructor(private projectService: ProjectEntityService, private metadataService: MetadataService) {}
ngOnInit() {
this.typeFilterOptions = this.metadataService.getProjectTypes();
this.stageFilterOptions = this.metadataService.getProjectStages();
this.filteredProjects$ = this.projectService.filteredEntities$;
}
onFilterChange() {
typeFilter = typeFilterOptions.filter(option => option.isChecked).map(option.name);
stageFilter = stageFilterOptions.filter(option => option.isChecked).map(option.name);
this.projectService.setFilter({
types: typeFilter,
stages: stageFilter
});
}
}
At this point anything in your template subscribed to the filteredProjects$ observable will get the update filtered entities when setFilter is called. For example:
my.component.html
...
<app-project-list [projects]="filteredProjects$ | async"></app-project-list>
...
An easy way is just to use your entity's model, say User
export const entityMetadata: EntityMetadataMap = {
User: {
filterFn: (entities: User[], { email, name }: Partial<User>) =>
entities
.filter((user) => (email? -1 < user.email.indexOf(email) : true))
.filter((user) => (name ? -1 < user.name.indexOf(name) : true))
}
};
and then in your component
constructor(private collectionService: UserCollectionService) {
...
}
filter() {
this.collectionService.setFilter({ name: 'jan' });
// or this.collectionService.setFilter({ name: 'jan', email: '@github.com' });
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With