Summary Table

Categories Total Count
PII 0
URL 0
DNS 0
EKL 0
IP 0
PORT 0
VsID 0
CF 0
AI 0
VPD 0
PL 0
Other 0

File Content

import { Component, Input, Output, EventEmitter } from '@angular/core';
import { QueryParams, FilterOps } from '../../models/patientModels';
import { GridElement } from '../../models/uiModels';
import { NgbDateAdapter, NgbDateNativeAdapter, } from '@ng-bootstrap/ng-bootstrap';
import * as _ from 'lodash';

@Component({
selector: 'app-query-builder',
templateUrl: './query-builder.component.html',
styleUrls: ['./query-builder.component.css'],
providers: [ {provide: NgbDateAdapter, useClass: NgbDateNativeAdapter} ]
})
export class QueryBuilderComponent {

/**
* local view of current queryParameters from store
*/
_remoteParams: QueryParams;

operatorText = {
co: 'contains',
eq: 'is',
ne: 'is not',
lt: 'less than',
gt: 'greater than'
};

/**
* holds local state information for query builder form
*/
formState = {
addSort: null,
isCollapsed: true,
changed: false,
addFilter: {
field: '',
operator: 'co',
value: '',
valueValid: true,
value2: '',
value2Valid: true
},
sort: {},
filter: {},
size: 0
};

/**
* Incoming definition of data elements
*/
@Input()
dataDef: Array<GridElement>;

/**
* Store reference to current query parameters in use by data access service
*/
@Input()
set queryParams(data: QueryParams) {

// move the filter criteria over to the query form
// for the ng-bootstrap date controls, we have to change the ISO date strings to js dates..
if (data.filter) {
const filters = JSON.parse(JSON.stringify(data.filter));
this.dataDef.forEach((itm) => {
if (itm.format && itm.format === 'date') {
if (filters[itm.key]) {
if (filters[itm.key].value2) { filters[itm.key].value2 = new Date(filters[itm.key].value2); }
if (filters[itm.key].value) { filters[itm.key].value = new Date(filters[itm.key].value); }
}
}
});
this.formState.filter = {...filters};
} else {
this.formState.filter = {};
}

if (data.sort) {
this.formState.sort = JSON.parse(JSON.stringify(data.sort));
} else {
this.formState.sort = {};
}

this.formState.size = (data.size) ? data.size : 0;
this._remoteParams = data;
this.formState.changed = false;
}

/**
* event for notifying parent of requery request
*/
@Output() refresh = new EventEmitter<QueryParams>();

/**
* event for notifying parent of query reset request
*/
@Output() reset = new EventEmitter();


constructor() { }

/**
* Add item to form filter array
*/
addFilter() {

// validate minimum for entry
if (this.formState.addFilter.field && (this.formState.addFilter.value || this.formState.addFilter.value2)) {

// if this is a date range, validate date formats...
if (this.getDef(this.formState.addFilter.field).format === 'date') {
if (this.formState.addFilter.value && !_.isDate(this.formState.addFilter.value)) {
this.formState.addFilter.valueValid = false;
} else {
this.formState.addFilter.valueValid = true;
}
if (this.formState.addFilter.value2 && !_.isDate(this.formState.addFilter.value2)) {
this.formState.addFilter.value2Valid = false;
} else {
this.formState.addFilter.value2Valid = true;
}
if ((this.formState.addFilter.value > this.formState.addFilter.value2) && (this.formState.addFilter.value2)) {
this.formState.addFilter.value2Valid = false;
}

if (!this.formState.addFilter.value2Valid || !this.formState.addFilter.valueValid) {
return;
}
}

// move filter from addFilter into filter hash
let newVal: any = this.formState.addFilter.value;
let newVal2: any = this.formState.addFilter.value2;
let op = this.formState.addFilter.operator;

// if we have a date field, validate types
if (this.getDef(this.formState.addFilter.field).format === 'date') {

op = FilterOps.in;

if (this.formState.addFilter.valueValid && this.formState.addFilter.value) {
newVal = new Date(new Date(newVal).setHours(0, 0, 0, 0));
}
if (this.formState.addFilter.value2Valid && this.formState.addFilter.value2) {
newVal2 = new Date(new Date(newVal2).setHours(23, 59, 59, 999));
}

}
this.formState.filter[this.formState.addFilter.field] = { operator: op, value: newVal, value2: newVal2 };

// reset form
this.formState.addFilter.field = '';
this.resetAddFilter();
this.formState.changed = true;
}
}

/**
* clear op & values from add filter sub-form
*/
resetAddFilter() {
this.formState.addFilter.value = '';
this.formState.addFilter.valueValid = true;
this.formState.addFilter.value2 = '';
this.formState.addFilter.value2Valid = true;
}


/**
* Remove item from form filter array
* @param field filter key
*/
removeFilter(field: string) {
delete this.formState.filter[field];
this.formState.changed = true;
}

/**
* Returns a list of filter key names that exist in the component's data definition
* @param obj list of filters
*/
getFilterKeys(obj: object) {

const retVal = [];
let keys = [];
if (obj && typeof obj === 'object') {
keys = Object.keys(obj);
} else {
keys = [];
}

for (let i = 0; i < keys.length; i++) {
if (_.find(this.dataDef, (itm) => itm.key === keys[i])) { retVal.push(keys[i]); }
}
return retVal;

}

/**
* Utility for Object.getKeys() in template
* @param obj object to return keys from
*/
getKeys(obj: object) {
if (obj && typeof obj === 'object') {
return Object.keys(obj);
} else {
return [];
}
}

/**
* Utility to return GridElement definition of data property for template
* @param key hash key of item to return
*/
getDef(key: string): GridElement {
const itm = _.find(this.dataDef, { 'key': key } );
return (itm) ? itm : {header: '', key: ''};
}

/**
* Update form's sort array
* @param field key of element to work with
* @param action one of 'sort, add or delete'
*/
changeSort(field: string, action: string) {

switch (action) {
case 'add':
if (this.formState.addSort === '' || (this.formState.sort && this.formState.sort[this.formState.addSort])) { break; }
this.formState.sort[this.formState.addSort] = 'asc';
this.formState.addSort = '';
break;
case 'delete':
delete this.formState.sort[field];
break;
case 'sort':
(this.formState.sort[field] === 'asc') ? this.formState.sort[field] = 'desc' : this.formState.sort[field] = 'asc';
}

this.formState.changed = true;
}


/**
* Convert current query parameters into text string for collapsed builder display
*/
getQueryText() {
let retVal = '';

// Filters as text
if (this._remoteParams.filter) {

const filterFields = this.getFilterKeys(this._remoteParams.filter);

if (filterFields.length > 0 ) {
retVal += 'where ';
filterFields.forEach((filterField, idx) => {
if (idx > 0) { retVal += ' and '; }
retVal += this.getDef(filterField).header;

if (this._remoteParams.filter[filterField].operator === FilterOps.in) {
if (this._remoteParams.filter[filterField].value ) {
(this._remoteParams.filter[filterField].value2) ? retVal += ' between ' : retVal += ' on or after ';
retVal += new Date(this._remoteParams.filter[filterField].value).toLocaleDateString();
}
if (this._remoteParams.filter[filterField].value2) {
(this._remoteParams.filter[filterField].value) ? retVal += ' and ' : retVal += ' on or before ';
retVal += new Date(this._remoteParams.filter[filterField].value2).toLocaleDateString();
}
} else {
retVal += ' ' + this.operatorText[this._remoteParams.filter[filterField].operator] +
' "' + this._remoteParams.filter[filterField].value.replace(/\+/g, '" OR "') + '"';
}
});
retVal += ', ';
}
}

// Sort as text
if (this._remoteParams.sort) {
retVal += 'sorted by ';
const sortFields = Object.keys(this._remoteParams.sort);
sortFields.forEach((sortField, idx) => {
if (idx > 0 ) { retVal += 'then '; }
if (this._remoteParams.sort[sortField] === 'asc') {
retVal += this.getDef(sortField).header + ' ascending, ';
} else {
retVal += this.getDef(sortField).header + ' descending, ';
}
});
}

// page size as text
if (this._remoteParams.size) { retVal += this._remoteParams.size + ' results/page'; }

// strip any trailing comma
if (retVal.match(/\,\ $/)) { retVal = retVal.slice(0, -2); }

return retVal;

}

getFilterItemText(key: string) {

const def = this.getDef(key);
let retVal = def.header;
if (this.formState.filter[key].operator === FilterOps.in) {
if (this.formState.filter[key].value ) {
(this.formState.filter[key].value2) ? retVal += ' between ' : retVal += ' on or after ';
retVal += new Date(this.formState.filter[key].value).toLocaleDateString();
}
if (this.formState.filter[key].value2) {
(this.formState.filter[key].value) ? retVal += ' and ' : retVal += ' on or before ';
retVal += new Date(this.formState.filter[key].value2).toLocaleDateString();
}
} else {
retVal += ' ' + this.operatorText[this.formState.filter[key].operator] +
' "' + this.formState.filter[key].value.replace(/\+/g, '" OR "') + '"';
}
return retVal;
}

doRefresh() {

const query: QueryParams = {
filter: JSON.parse(JSON.stringify(this.formState.filter)),
sort: JSON.parse(JSON.stringify(this.formState.sort)),
size: this.formState.size
};

this.refresh.emit(query);

}

doReset() {

this.reset.emit();

}

}