1 import {Component, Input, OnInit, QueryList, ViewChild, ViewChildren} from '@angular/core';
2 import {GridContext, GridColumn} from './grid';
3 import {IdlObject} from '@eg/core/idl.service';
4 import {ComboboxComponent} from '@eg/share/combobox/combobox.component';
5 import {DateSelectComponent} from '@eg/share/date-select/date-select.component';
6 import {OrgSelectComponent} from '@eg/share/org-select/org-select.component';
7 import {OrgService} from '@eg/core/org.service';
8 import {NgbDropdown} from '@ng-bootstrap/ng-bootstrap';
9 import {ComboboxEntry} from '@eg/share/combobox/combobox.component';
12 selector: 'eg-grid-filter-control',
13 templateUrl: './grid-filter-control.component.html'
16 export class GridFilterControlComponent implements OnInit {
18 @Input() context: GridContext;
19 @Input() col: GridColumn;
22 @ViewChildren(ComboboxComponent) filterComboboxes: QueryList<ComboboxComponent>;
23 @ViewChildren(OrgSelectComponent) orgSelects: QueryList<OrgSelectComponent>;
24 @ViewChildren(NgbDropdown) dropdowns: QueryList<NgbDropdown>;
26 @ViewChild('dateSelectOne') dateSelectOne: DateSelectComponent;
27 @ViewChild('dateSelectTwo') dateSelectTwo: DateSelectComponent;
29 // So we can use (ngModelChange) on the link combobox
30 linkFilterEntry: ComboboxEntry = null;
33 private org: OrgService
38 operatorChanged(col: GridColumn) {
39 if (col.filterOperator === 'null' || col.filterOperator === 'not null') {
40 col.filterInputDisabled = true;
41 col.filterValue = undefined;
43 col.filterInputDisabled = false;
47 applyOrgFilter(col: GridColumn) {
48 const org: IdlObject = (col.filterValue as unknown) as IdlObject;
51 this.clearFilter(col);
54 const ous: any[] = new Array();
55 if (col.filterIncludeOrgDescendants || col.filterIncludeOrgAncestors) {
56 if (col.filterIncludeOrgAncestors) {
57 ous.push(...this.org.ancestors(org, true));
59 if (col.filterIncludeOrgDescendants) {
60 ous.push(...this.org.descendants(org, true));
67 const op: string = (col.filterOperator === '=' ? 'in' : 'not in');
68 filt[col.name][op] = ous;
69 this.context.dataSource.filters[col.name] = [ filt ];
70 col.isFiltered = true;
71 this.context.reload();
76 applyLinkFilter(col: GridColumn) {
77 if (col.filterValue) {
78 this.applyFilter(col);
81 // Value was cleared from the combobox
82 this.clearFilter(col);
86 // TODO: this was copied from date-select and
87 // really belongs in a date service
88 localDateFromYmd(ymd: string): Date {
89 const parts = ymd.split('-');
91 Number(parts[0]), Number(parts[1]) - 1, Number(parts[2]));
94 applyDateFilter(col: GridColumn) {
95 const dateStr = this.dateSelectOne.currentAsYmd();
97 this.dateSelectTwo ? this.dateSelectTwo.currentAsYmd() : null;
99 if (endDateStr && !dateStr) {
100 // User has applied a second date (e.g. between) but cleared
101 // the first date. Avoid applying the filter until
102 // dateStr gets a value or endDateStr is cleared or the
107 if (col.filterOperator === 'null' || col.filterOperator === 'not null') {
108 this.applyFilter(col);
110 if (dateStr == null) {
111 this.clearFilter(col);
114 const date: Date = this.localDateFromYmd(dateStr);
115 let date1 = new Date();
116 let date2 = new Date();
117 const op: string = col.filterOperator;
118 const filt: Object = {};
119 const filt2: Object = {};
120 const filters = new Array();
121 if (col.filterOperator === '>') {
124 date1.setMinutes(59);
125 date1.setSeconds(59);
126 filt[op] = date1.toISOString();
127 if (col.name === 'dob') { filt[op] = dateStr; } // special case
128 filt2[col.name] = filt;
130 } else if (col.filterOperator === '>=') {
132 filt[op] = date1.toISOString();
133 if (col.name === 'dob') { filt[op] = dateStr; } // special case
134 filt2[col.name] = filt;
136 } else if (col.filterOperator === '<') {
138 filt[op] = date1.toISOString();
139 if (col.name === 'dob') { filt[op] = dateStr; } // special case
140 filt2[col.name] = filt;
142 } else if (col.filterOperator === '<=') {
145 date1.setMinutes(59);
146 date1.setSeconds(59);
147 filt[op] = date1.toISOString();
148 if (col.name === 'dob') { filt[op] = dateStr; } // special case
149 filt2[col.name] = filt;
151 } else if (col.filterOperator === '=') {
152 date1 = new Date(date.valueOf());
153 filt['>='] = date1.toISOString();
154 if (col.name === 'dob') { filt['>='] = dateStr; } // special case
155 filt2[col.name] = filt;
158 date2 = new Date(date.valueOf());
160 date2.setMinutes(59);
161 date2.setSeconds(59);
162 const filt_a: Object = {};
163 const filt2_a: Object = {};
164 filt_a['<='] = date2.toISOString();
165 if (col.name === 'dob') { filt_a['<='] = dateStr; } // special case
166 filt2_a[col.name] = filt_a;
167 filters.push(filt2_a);
168 } else if (col.filterOperator === '!=') {
169 date1 = new Date(date.valueOf());
170 filt['<'] = date1.toISOString();
171 if (col.name === 'dob') { filt['<'] = dateStr; } // special case
172 filt2[col.name] = filt;
174 date2 = new Date(date.valueOf());
176 date2.setMinutes(59);
177 date2.setSeconds(59);
178 const filt_a: Object = {};
179 const filt2_a: Object = {};
180 filt_a['>'] = date2.toISOString();
181 if (col.name === 'dob') { filt_a['>'] = dateStr; } // special case
182 filt2_a[col.name] = filt_a;
184 const date_filt: any = { '-or': [] };
185 date_filt['-or'].push(filt2);
186 date_filt['-or'].push(filt2_a);
187 filters.push(date_filt);
188 } else if (col.filterOperator === 'between') {
191 // User has not applied the second date yet.
196 date2 = this.localDateFromYmd(endDateStr);
201 // don't make user care about the order
202 // they enter the dates in
206 filt[date1op] = date1.toISOString();
207 if (col.name === 'dob') { filt['>='] = dateStr; } // special case
208 filt2[col.name] = filt;
212 date2.setMinutes(59);
213 date2.setSeconds(59);
214 const filt_a: Object = {};
215 const filt2_a: Object = {};
216 filt_a[date2op] = date2.toISOString();
217 if (col.name === 'dob') { filt_a['<='] = endDateStr; } // special case
218 filt2_a[col.name] = filt_a;
219 filters.push(filt2_a);
221 this.context.dataSource.filters[col.name] = filters;
222 col.isFiltered = true;
223 this.context.reload();
224 this.closeDropdown();
228 clearDateFilter(col: GridColumn) {
229 delete this.context.dataSource.filters[col.name];
230 col.isFiltered = false;
231 this.context.reload();
232 this.closeDropdown();
234 applyBooleanFilter(col: GridColumn) {
235 if (!col.filterValue || col.filterValue === '') {
236 delete this.context.dataSource.filters[col.name];
237 col.isFiltered = false;
238 this.context.reload();
240 const val: string = col.filterValue;
242 const filt: Object = {};
244 const filt2: Object = {};
245 filt2[col.name] = filt;
246 this.context.dataSource.filters[col.name] = [ filt2 ];
247 col.isFiltered = true;
248 this.context.reload();
251 this.closeDropdown();
254 applyFilterCommon(col: GridColumn) {
256 switch (col.datatype) {
259 this.linkFilterEntry ? this.linkFilterEntry.id : null;
260 return this.applyLinkFilter(col);
262 return this.applyBooleanFilter(col);
264 return this.applyDateFilter(col);
266 return this.applyOrgFilter(col);
268 return this.applyFilter(col);
272 applyFilter(col: GridColumn) {
273 // fallback if the operator somehow was not set yet
274 if (col.filterOperator === undefined) { col.filterOperator = '='; }
276 if ( (col.filterOperator !== 'null') && (col.filterOperator !== 'not null') &&
277 (!col.filterValue || col.filterValue === '') &&
278 (col.filterValue !== '0') ) {
279 // if value is empty and we're _not_ checking for null/not null, clear
281 delete this.context.dataSource.filters[col.name];
282 col.isFiltered = false;
284 let op: string = col.filterOperator;
285 let val: string = col.filterValue;
286 if (col.filterOperator === 'null') {
289 } else if (col.filterOperator === 'not null') {
292 } else if (col.filterOperator === 'like' || col.filterOperator === 'not like') {
293 val = '%' + val + '%';
294 } else if (col.filterOperator === 'startswith') {
297 } else if (col.filterOperator === 'endswith') {
301 const filt: any = {};
302 if (col.filterOperator === 'not like') {
304 filt['-not'][col.name] = {};
305 filt['-not'][col.name]['like'] = val;
306 this.context.dataSource.filters[col.name] = [ filt ];
307 col.isFiltered = true;
310 filt[col.name][op] = val;
311 this.context.dataSource.filters[col.name] = [ filt ];
312 col.isFiltered = true;
315 this.context.reload();
316 this.closeDropdown();
318 clearFilter(col: GridColumn) {
319 // clear filter values...
321 // ... and inform the data source
322 delete this.context.dataSource.filters[col.name];
323 col.isFiltered = false;
325 this.context.reload();
326 this.closeDropdown();
330 // Timeout allows actions to occur before closing (some) dropdows
331 // clears the values (e.g. link selector)
332 setTimeout(() => this.dropdowns.forEach(drp => { drp.close(); }));
336 this.filterComboboxes.forEach(ctl => { ctl.applyEntryId(null); });
337 this.orgSelects.forEach(ctl => { ctl.reset(); });
338 if (this.dateSelectOne) { this.dateSelectOne.reset(); }
339 if (this.dateSelectTwo) { this.dateSelectTwo.reset(); }