ngrxangular-ngrx-data

How to use setFilter on the EntityCollectionService


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.


Solution

  • 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>
    ...