Skip to content

Commit

Permalink
fix(akita-filter-plugins): split Akita Filters interface into two typ…
Browse files Browse the repository at this point in the history
…es for Local Filters and Server Filters. (#55)

This makes it possible to distinguish between local filters that must have a predicate function, and those that are server type must have a value, but not a predicate function.
  • Loading branch information
manudss authored Feb 8, 2021
1 parent 59655c4 commit 57f0285
Show file tree
Hide file tree
Showing 10 changed files with 93 additions and 55 deletions.
20 changes: 18 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,15 +165,31 @@ type AkitaFilter: AkitaFilter<EntityState> = {
};
```

- Id and function were mandatored. (By default, Id will guid(), and default function, will be defaultFilter helpers).
- Id and function were mandatory. (By default, Id will guid(), and default function, will be defaultFilter helpers).

- But you can set a name, that will be useful to display the filter in the ui. (by default, it will be calculated with ID and value).

- You can set the value, that could be used in your filter function, or retrieve the value for a filter (in ex to init the form filter)
- You can set the value, that could be used in your filter function, or retrieve the value for a filter (in example, to init the form filter)

- Or it could be useful, to execute a filter at the begin or the end. (Could be useful to execute simple filter at the beginning, and complex filter like full search at the end)

- hide: true, it will be applied and not displayed in the ui.

You can also use AkitaFilterLocal or AkitaFilterServer, to specify if a filter is local (that must have a predicate) or server that must have a value, and don't have predicate.

```typescript
export interface AkitaFilterLocal<S extends EntityState, E = getEntityType<S>> extends AkitaFilterBase<S, E> {
/** The function to apply filters, by default use defaultFilter helpers, that will search the value in the object */
predicate: (entity: E, index: number, array: E[] | HashMap<E>, filter: AkitaFilter<S>) => boolean;
}

export interface AkitaFilterServer<S extends EntityState, E = getEntityType<S>> extends AkitaFilterBase<S, E> {
/** If you have enabled server filter, specify witch filters will be call to server, default to false. */
server: true;
/** The filter value will be sent to withServer function */
value: any;
}
```


# AkitaFilterPlugins API
Expand Down
25 changes: 14 additions & 11 deletions projects/akita-filters-plugin/src/lib/akita-filters-plugin.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {AkitaFilter, AkitaFiltersStore, createFilter, FiltersState} from './akita-filters-store';
import {AkitaFiltersStore, FiltersState} from './akita-filters-store';
import {AkitaFiltersQuery} from './akita-filters-query';
import {combineLatest, isObservable, Observable, ObservedValueOf, of, Subscription} from 'rxjs';
import {distinctUntilChanged, map} from 'rxjs/operators';
Expand All @@ -22,6 +22,7 @@ import {
SelectAllOptionsE,
SortByOptions
} from '@datorama/akita';
import {AkitaFilter, AkitaFilterLocal, AkitaFilterServer, createFilter} from './akita-filters.model';

export interface FiltersParams<S extends EntityState> {
filtersStoreName?: string;
Expand Down Expand Up @@ -139,7 +140,7 @@ export class AkitaFiltersPlugin<S extends EntityState, E = getEntityType<S>, I =
/**
* Select all filters
*
* Note: Only all filters not hided (with hide=true), will not be displayed. If you want it, call directly to:
* Note: Only all filters not hide (with hide=true), will not be displayed. If you want it, call directly to:
* `this.filtersQuery.selectAll()`
*
*
Expand All @@ -164,8 +165,10 @@ export class AkitaFiltersPlugin<S extends EntityState, E = getEntityType<S>, I =
* Note: filters with server=false, will not be displayed. If you want it, call directly to:
* `this.filtersQuery.getAll()`
*/
getServerFilters(): Array<AkitaFilter<S>> {
return this.server ? this._filtersQuery.getAll({filterBy: filter => filter.server}) : this.getFilters();
getServerFilters(): Array<AkitaFilterServer<S>> {
return this.server ?
this._filtersQuery.getAll({filterBy: filter => filter.server}) as Array<AkitaFilterServer<S>>
: this.getFilters() as Array<AkitaFilterServer<S>>;
}

/**
Expand Down Expand Up @@ -205,7 +208,7 @@ export class AkitaFiltersPlugin<S extends EntityState, E = getEntityType<S>, I =
/**
* Create or update a filter
*/
setFilter(filter: Partial<AkitaFilter<S>>) {
setFilter(filter: Partial<AkitaFilterLocal<S> | AkitaFilterServer<S>>) {
if (this.server && isUndefined(filter.server)) {
filter.server = true;
}
Expand All @@ -217,7 +220,7 @@ export class AkitaFiltersPlugin<S extends EntityState, E = getEntityType<S>, I =
/**
* Create or update multiples filters
*/
setFilters(filters: Array<Partial<AkitaFilter<S>>>) {
setFilters(filters: Array<Partial<AkitaFilterLocal<S> | AkitaFilterServer<S>>>) {
if (!filters) { return; }
const entities = filters.map((filter => {
if (this.server && isUndefined(filter.server)) {
Expand Down Expand Up @@ -332,12 +335,12 @@ export class AkitaFiltersPlugin<S extends EntityState, E = getEntityType<S>, I =

private _applyFiltersForArray(
entities: Array<getEntityType<S>>,
filters: Array<AkitaFilter<S>>,
filters: Array<AkitaFilterLocal<S>>,
sort: ObservedValueOf<Observable<SortByOptions<E> | null>>): Array<getEntityType<S>> {
let entitiesFiltered = entities;
if (filters.length !== 0) {
entitiesFiltered = entities.filter((entity: getEntityType<S>, index: number, array: Array<getEntityType<S>>) => {
return filters.every((filter: AkitaFilter<S>) => {
return filters.every((filter: AkitaFilterLocal<S>) => {
if (filter.predicate) {
return !!filter.predicate(entity, index, array, filter);
}
Expand All @@ -355,7 +358,7 @@ export class AkitaFiltersPlugin<S extends EntityState, E = getEntityType<S>, I =

private _applyFiltersForHashMap(
entities: HashMap<getEntityType<S>>,
filters: Array<AkitaFilter<S>>): HashMap<getEntityType<S>> {
filters: Array<AkitaFilterLocal<S>>): HashMap<getEntityType<S>> {
if (filters.length === 0) {
return entities;
}
Expand All @@ -370,10 +373,10 @@ export class AkitaFiltersPlugin<S extends EntityState, E = getEntityType<S>, I =
return hashMapFiltered;
}

private _applyFiltersForOneEntity(filters: Array<AkitaFilter<S>>,
private _applyFiltersForOneEntity(filters: Array<AkitaFilterLocal<S>>,
entity: getEntityType<S>, index: number,
array: Array<getEntityType<S>> | HashMap<getEntityType<S>>) {
return filters.every((filter: AkitaFilter<S>) => {
return filters.every((filter: AkitaFilterLocal<S>) => {
if (filter.predicate) {
return !!filter.predicate(entity, index, array, filter);
}
Expand Down
3 changes: 2 additions & 1 deletion projects/akita-filters-plugin/src/lib/akita-filters-query.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { AkitaFilter, FiltersState, AkitaFiltersStore } from './akita-filters-store';
import { FiltersState, AkitaFiltersStore } from './akita-filters-store';
import {EntityState, getEntityType, Order, QueryConfig, QueryEntity} from '@datorama/akita';
import {AkitaFilter} from './akita-filters.model';


@QueryConfig({
Expand Down
40 changes: 2 additions & 38 deletions projects/akita-filters-plugin/src/lib/akita-filters-store.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,5 @@
import {EntityState, EntityStore, getEntityType, guid, HashMap, ID, SortByOptions, StoreConfig} from '@datorama/akita';
import {defaultFilter} from './filters-utils';

function capitalize(str: string): string {
return str.charAt(0).toUpperCase() + str.substr(1);
}

export interface AkitaFilter<S extends EntityState, E = getEntityType<S>> {
id: ID;
/** A corresponding name for display the filter, by default, it will be ${id): ${value} */
name?: string;
/** set the order for filter, by default, it is 10 */
order?: number;
/** The filter value, this will be used to compute name, or getting the current value, to initiate your form */
value?: any;
/** If you want to have filter that is not displayed on the list */
hide?: boolean;
/** If you have enabled server filter, specify witch filters will be call to server, default to false. */
server?: boolean;
/** The function to apply filters, by default use defaultFilter helpers, that will search the value in the object */
predicate: ( entity: E, index: number, array: E[] | HashMap<E>, filter: AkitaFilter<S> ) => boolean;
/** add any other data you want to add **/
[key: string]: any;
}

export function createFilter<S extends EntityState, E = getEntityType<S>>( filterParams: Partial<AkitaFilter<S, E>> ) {
const id = filterParams.id ? filterParams.id : guid();
const name = filterParams.name || (filterParams.value && filterParams.id ?
`${capitalize(filterParams.id.toString())}: ${filterParams.value.toString()}` : undefined);

if ( !filterParams.predicate && filterParams.value ) {
/** use default function, if not provided */
// @ts-ignore
filterParams.predicate = defaultFilter;
}

return { id, name, hide: false, order: 10, server: false, ...filterParams } as AkitaFilter<S>;
}
import {EntityState, EntityStore, getEntityType, SortByOptions, StoreConfig} from '@datorama/akita';
import {AkitaFilter} from './akita-filters.model';

export interface FiltersState<S extends EntityState, E = getEntityType<S>> extends EntityState<AkitaFilter<S, E>> {
sort: SortByOptions<any>;
Expand Down
52 changes: 52 additions & 0 deletions projects/akita-filters-plugin/src/lib/akita-filters.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import {EntityState, getEntityType, guid, HashMap, ID} from '@datorama/akita';
import {defaultFilter} from './filters-utils';

function capitalize(str: string): string {
return str.charAt(0).toUpperCase() + str.substr(1);
}

export interface AkitaFilterBase<S extends EntityState, E = getEntityType<S>> {
id: ID;
/** A corresponding name for display the filter, by default, it will be ${id): ${value} */
name?: string;
/** set the order for filter, by default, it is 10 */
order?: number;
/** If you want to have filter that is not displayed on the list */
hide?: boolean;
/** The filter value, this will be used to compute name, or getting the current value, to initiate your form */
value?: any;
}

export interface AkitaFilterLocal<S extends EntityState, E = getEntityType<S>> extends AkitaFilterBase<S, E> {
/** If you have enabled server filter, specify witch filters will be call to server, default to false. */
server?: false;
/** The function to apply filters, by default use defaultFilter helpers, that will search the value in the object */
predicate: (entity: E, index: number, array: E[] | HashMap<E>, filter: AkitaFilter<S>) => boolean;
}

export interface AkitaFilterServer<S extends EntityState, E = getEntityType<S>> extends AkitaFilterBase<S, E> {
/** If you have enabled server filter, specify witch filters will be call to server, default to false. */
server: true;
/** The filter value will be sent to withServer function */
value: any;
}

export type AkitaFilter<S extends EntityState, E = getEntityType<S>> = AkitaFilterLocal<S, E> | AkitaFilterServer<S, E> | {
/** add any other data you want to add, keep this to get the compatibilities with others versions **/
[key: string]: any;
};

export function createFilter<S extends EntityState, E = getEntityType<S>>
(filterParams: Partial<AkitaFilterLocal<S> | AkitaFilterServer<S>>) {
const id = filterParams.id ? filterParams.id : guid();
const name = filterParams.name || (filterParams.value && filterParams.id ?
`${capitalize(filterParams.id.toString())}: ${filterParams.value.toString()}` : undefined);

if (!(filterParams as AkitaFilterLocal<S>)?.predicate && filterParams.value && !filterParams.server) {
/** use default function, if not provided */
// @ts-ignore
(filterParams as AkitaFilterLocal<S>).predicate = defaultFilter;
}

return {id, name, hide: false, order: 10, server: false, ...filterParams} as AkitaFilter<S>;
}
2 changes: 1 addition & 1 deletion projects/akita-filters-plugin/src/lib/filters-utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

import {isDefined, isString, isObject, HashMap, getEntityType, isArray} from '@datorama/akita';
import {AkitaFilter} from './akita-filters-store';
import {AkitaFilter} from './akita-filters.model';

/**
* Helper function to do a default filter
Expand Down
1 change: 1 addition & 0 deletions projects/akita-filters-plugin/src/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export * from './akita-filters-plugin';
export * from './akita-filters-query';
export * from './akita-filters-store';
export * from './filters-utils';
export * from './akita-filters.model';
1 change: 1 addition & 0 deletions projects/akita-filters-plugin/src/public_api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export * from './lib/akita-filters-plugin';
export * from './lib/akita-filters-query';
export * from './lib/akita-filters-store';
export * from './lib/filters-utils';
export * from './lib/akita-filters.model';
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { FormControl, FormGroup } from '@angular/forms';
import { Observable } from 'rxjs';
import {ProductPlant, ProductPlantState, ProductsFiltersService} from '../state';
import { untilDestroyed } from 'ngx-take-until-destroy';
import {AkitaFilter} from '../../../../projects/akita-filters-plugin/src/lib/akita-filters-store';
import {searchFilter} from '../../../../projects/akita-filters-plugin/src/lib/filters-utils';
import {AkitaFilter} from '../../../../projects/akita-filters-plugin/src/lib/akita-filters.model';

@Component({
selector: 'app-filters-form',
Expand Down
2 changes: 1 addition & 1 deletion src/app/products-filters/state/products-filters.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import { tap } from 'rxjs/operators';
import {empty, Observable} from 'rxjs';
import { ProductsFiltersQuery } from './products-filters.query';
import {Order} from '@datorama/akita';
import {AkitaFilter} from '../../../../projects/akita-filters-plugin/src/lib/akita-filters-store';
import {AkitaFiltersPlugin} from '../../../../projects/akita-filters-plugin/src/lib/akita-filters-plugin';
import {AkitaFilter} from '../../../../projects/akita-filters-plugin/src/lib/akita-filters.model';


@Injectable({
Expand Down

0 comments on commit 57f0285

Please sign in to comment.