src/app/shared/table-helpers.ts
import { FormControl, AbstractControl } from '../../../node_modules/@angular/forms';
const dropdownString = (fieldValue: any, value: string) => {
if (fieldValue === undefined || value === undefined) {
// If there is no value to filter, include item. If the data field is undefined, exclude item.
return value !== undefined;
}
if (fieldValue instanceof Array) {
return fieldValue.indexOf(value) === -1;
}
// Ensure both value and fieldValue are strings before calling toLowerCase
if (typeof value === 'string' && typeof fieldValue === 'string') {
return value.toLowerCase() !== fieldValue.toLowerCase();
}
};
const dropdownArray = (fieldValue: any, values: string[]) => {
return values.findIndex(value => !dropdownString(fieldValue, value)) === -1;
};
const checkFilterItems = (data: any) => ((includeItem: boolean, [ field, val ]) => {
const dataField = getProperty(data, field);
// If field is an array field, check if one value matches. If not check if values match exactly.
const noMatch = val instanceof Array ? dropdownArray(dataField, val) : dropdownString(dataField, val);
if (val && noMatch) {
return false;
}
return includeItem;
});
// Multi level field filter by spliting each field by '.'
export const filterSpecificFields = (filterFields: string[]): any => {
return (data: any, filter: string) => {
for (let i = 0; i < filterFields.length; i++) {
if (getProperty(data, filterFields[i]).toLowerCase().indexOf(filter.trim().toLowerCase()) > -1) {
return true;
}
}
};
};
export const filterSpecificFieldsByWord = (filterFields: string[]): any => {
return (data: any, filter: string) => {
const words = filter.split(' ').map(value => value.toLowerCase());
return words.filter(word => word).find(word => !filterSpecificFields(filterFields)(data, word)) === undefined;
};
};
// Takes an object and string of dot seperated property keys. Returns the nested value of the succession of
// keys or undefined.
function getProperty(data: any, fields: string) {
const propertyArray = fields.split('.');
return propertyArray.reduce((obj, prop) => (obj && obj[prop] !== undefined) ? obj[prop] : undefined, data);
}
export const filterDropdowns = (filterObj: any) => {
return (data: any, filter: string) => {
// Object.entries returns an array of each key/value pair as arrays in the form of [ key, value ]
return Object.entries(filterObj).reduce(checkFilterItems(data), true);
};
};
// Takes array of field names and if trueIfExists is true, return true if field exists
// if false return true if it does not exist
export const filterFieldExists = (filterFields: string[], trueIfExists: boolean): any => {
return (data: any, filter: string) => {
for (let i = 0; i < filterFields.length; i++) {
return trueIfExists === (getProperty(data, filterFields[i]) !== undefined);
}
return true;
};
};
const matchAllItems = (filterItems: string[], propItems: string[]) => {
return filterItems.reduce((isMatch, filter) => isMatch && propItems.indexOf(filter) > -1, true);
};
export const filterArrayField = (filterField: string, filterItems: string[]) => {
return (data: any, filter: string) => {
return matchAllItems(filterItems, getProperty(data, filterField) || []);
};
};
export const filterTags = (filterControl: FormControl) => {
return (data: any, filter: string) => {
return filterArrayField('tags', filterControl.value)({ tags: data.tags.map((tag: any) => tag._id) }, filter);
};
};
export const filterAdvancedSearch = (searchObj: any) => {
return (data: any, filter: string) => {
return Object.entries(searchObj).reduce(
(isMatch, [ field, val ]: any[]) => isMatch && (field.indexOf('_') > -1 || filterArrayField(field, val)(data.doc, filter)),
true
);
};
};
// filterOnOff must be an object so it references a variable on component & changes with component changes
export const filterShelf = (filterOnOff: { value: 'on' | 'off' }, filterField: string) => {
return (data: any, filter: string) => {
return filterOnOff.value === 'off' || data[filterField] === true;
};
};
// Special filter for showing members that are admins
export const filterAdmin = (data, filter) => data.doc.isUserAdmin && data.doc.roles.length === 0;
// Takes an array of the above filtering functions and returns true if all match
export const composeFilterFunctions = (filterFunctions: any[]) => {
return (data: any, filter: any) => {
return filterFunctions.reduce((isMatch, filterFunction) => {
return isMatch && filterFunction(data, filter);
}, true);
};
};
export const sortNumberOrString = (item, property) => {
switch (typeof item[property]) {
case 'number':
return item[property];
case 'string':
return item[property].toLowerCase();
}
};
// Returns a space to fill the MatTable filter field so filtering runs for dropdowns when
// search text is deleted, but does not run when there are no active filters.
export const dropdownsFill = (filterObj) => Object.entries(filterObj).reduce((emptySpace, [ field, val ]) => {
if (val) {
return ' ';
}
return emptySpace;
}, '');
export const filteredItemsInPage = (filteredData: any[], pageIndex: number, pageSize: number) => {
return pageIndex === undefined ? filteredData : filteredData.slice(pageIndex * pageSize, (pageIndex * pageSize) + pageSize);
};
export const selectedOutOfFilter = (filteredData: any[], selection: any, paginator: any = {}) => {
const itemsInPage = filteredItemsInPage(filteredData, paginator.pageIndex, paginator.pageSize);
return selection.selected.filter((selectedId) => itemsInPage.find((filtered: any) => filtered._id === selectedId ) === undefined);
};
export const createDeleteArray = (array) => array.map((item: any) => ({ _id: item._id, _rev: item._rev, _deleted: true }));
export const commonSortingDataAccessor = (item: any, property: string) => {
switch (property) {
case 'rating':
return item.rating.rateSum / item.rating.totalRating || 0;
default:
return item[property] ? sortNumberOrString(item, property) : sortNumberOrString(item.doc, property);
}
};
export const deepSortingDataAccessor = (item: any, property: string) => {
const keys = property.split('.');
const simpleItem = keys.reduce((newItem, key, index) => {
if (index === keys.length - 1 || newItem[key] === undefined || newItem[key] === null) {
return newItem;
}
return newItem[key];
}, item);
return sortNumberOrString(simpleItem, keys[keys.length - 1]);
};
export const trackById = (index, item) => item._id;
export const showFormErrors = (controls: { [key: string]: AbstractControl }) => {
Object.values(controls).forEach(control => {
control.markAsTouched({ onlySelf: true });
});
};
export const filterIds = (filterObj: { ids: string[] }) => {
return (data: any, filter: string) => {
return filterObj.ids.length > 0 ? filterObj.ids.indexOf(data._id) > -1 : true;
};
};