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
37 if (this.col.filterValue !== undefined) {
38 this.applyFilter(this.col);
42 operatorChanged(col: GridColumn) {
43 if (col.filterOperator === 'null' || col.filterOperator === 'not null') {
44 col.filterInputDisabled = true;
45 col.filterValue = undefined;
47 col.filterInputDisabled = false;
51 applyOrgFilter(col: GridColumn) {
52 const org: IdlObject = (col.filterValue as unknown) as IdlObject;
55 this.clearFilter(col);
58 const ous: any[] = new Array();
59 if (col.filterIncludeOrgDescendants || col.filterIncludeOrgAncestors) {
60 if (col.filterIncludeOrgAncestors) {
61 ous.push(...this.org.ancestors(org, true));
63 if (col.filterIncludeOrgDescendants) {
64 ous.push(...this.org.descendants(org, true));
71 const op: string = (col.filterOperator === '=' ? 'in' : 'not in');
72 filt[col.name][op] = ous;
73 this.context.dataSource.filters[col.name] = [ filt ];
74 col.isFiltered = true;
75 this.context.reload();
80 applyLinkFilter(col: GridColumn) {
81 if (col.filterValue) {
82 this.applyFilter(col);
85 // Value was cleared from the combobox
86 this.clearFilter(col);
90 // TODO: this was copied from date-select and
91 // really belongs in a date service
92 localDateFromYmd(ymd: string): Date {
93 const parts = ymd.split('-');
95 Number(parts[0]), Number(parts[1]) - 1, Number(parts[2]));
98 applyDateFilter(col: GridColumn) {
99 const dateStr = this.dateSelectOne.currentAsYmd();
101 this.dateSelectTwo ? this.dateSelectTwo.currentAsYmd() : null;
103 if (endDateStr && !dateStr) {
104 // User has applied a second date (e.g. between) but cleared
105 // the first date. Avoid applying the filter until
106 // dateStr gets a value or endDateStr is cleared or the
111 if (col.filterOperator === 'null' || col.filterOperator === 'not null') {
112 this.applyFilter(col);
114 if (dateStr == null) {
115 this.clearFilter(col);
118 const date: Date = this.localDateFromYmd(dateStr);
119 let date1 = new Date();
120 let date2 = new Date();
121 const op: string = col.filterOperator;
122 const filt: Object = {};
123 const filt2: Object = {};
124 const filters = new Array();
125 if (col.filterOperator === '>') {
128 date1.setMinutes(59);
129 date1.setSeconds(59);
130 filt[op] = date1.toISOString();
131 if (col.name === 'dob') { filt[op] = dateStr; } // special case
132 filt2[col.name] = filt;
134 } else if (col.filterOperator === '>=') {
136 filt[op] = date1.toISOString();
137 if (col.name === 'dob') { filt[op] = dateStr; } // special case
138 filt2[col.name] = filt;
140 } else if (col.filterOperator === '<') {
142 filt[op] = date1.toISOString();
143 if (col.name === 'dob') { filt[op] = dateStr; } // special case
144 filt2[col.name] = filt;
146 } else if (col.filterOperator === '<=') {
149 date1.setMinutes(59);
150 date1.setSeconds(59);
151 filt[op] = date1.toISOString();
152 if (col.name === 'dob') { filt[op] = dateStr; } // special case
153 filt2[col.name] = filt;
155 } else if (col.filterOperator === '=') {
156 date1 = new Date(date.valueOf());
157 filt['>='] = date1.toISOString();
158 if (col.name === 'dob') { filt['>='] = dateStr; } // special case
159 filt2[col.name] = filt;
162 date2 = new Date(date.valueOf());
164 date2.setMinutes(59);
165 date2.setSeconds(59);
166 const filt_a: Object = {};
167 const filt2_a: Object = {};
168 filt_a['<='] = date2.toISOString();
169 if (col.name === 'dob') { filt_a['<='] = dateStr; } // special case
170 filt2_a[col.name] = filt_a;
171 filters.push(filt2_a);
172 } else if (col.filterOperator === '!=') {
173 date1 = new Date(date.valueOf());
174 filt['<'] = date1.toISOString();
175 if (col.name === 'dob') { filt['<'] = dateStr; } // special case
176 filt2[col.name] = filt;
178 date2 = new Date(date.valueOf());
180 date2.setMinutes(59);
181 date2.setSeconds(59);
182 const filt_a: Object = {};
183 const filt2_a: Object = {};
184 filt_a['>'] = date2.toISOString();
185 if (col.name === 'dob') { filt_a['>'] = dateStr; } // special case
186 filt2_a[col.name] = filt_a;
188 const date_filt: any = { '-or': [] };
189 date_filt['-or'].push(filt2);
190 date_filt['-or'].push(filt2_a);
191 filters.push(date_filt);
192 } else if (col.filterOperator === 'between') {
195 // User has not applied the second date yet.
200 date2 = this.localDateFromYmd(endDateStr);
205 // don't make user care about the order
206 // they enter the dates in
210 filt[date1op] = date1.toISOString();
211 if (col.name === 'dob') { filt['>='] = dateStr; } // special case
212 filt2[col.name] = filt;
216 date2.setMinutes(59);
217 date2.setSeconds(59);
218 const filt_a: Object = {};
219 const filt2_a: Object = {};
220 filt_a[date2op] = date2.toISOString();
221 if (col.name === 'dob') { filt_a['<='] = endDateStr; } // special case
222 filt2_a[col.name] = filt_a;
223 filters.push(filt2_a);
225 this.context.dataSource.filters[col.name] = filters;
226 col.isFiltered = true;
227 this.context.reload();
228 this.closeDropdown();
232 clearDateFilter(col: GridColumn) {
233 delete this.context.dataSource.filters[col.name];
234 col.isFiltered = false;
235 this.context.reload();
236 this.closeDropdown();
238 applyBooleanFilter(col: GridColumn) {
239 if (!col.filterValue || col.filterValue === '') {
240 delete this.context.dataSource.filters[col.name];
241 col.isFiltered = false;
242 this.context.reload();
244 const val: string = col.filterValue;
246 const filt: Object = {};
248 const filt2: Object = {};
249 filt2[col.name] = filt;
250 this.context.dataSource.filters[col.name] = [ filt2 ];
251 col.isFiltered = true;
252 this.context.reload();
255 this.closeDropdown();
258 applyFilterCommon(col: GridColumn) {
260 switch (col.datatype) {
263 this.linkFilterEntry ? this.linkFilterEntry.id : null;
264 return this.applyLinkFilter(col);
266 return this.applyBooleanFilter(col);
268 return this.applyDateFilter(col);
270 return this.applyOrgFilter(col);
272 return this.applyFilter(col);
276 applyFilter(col: GridColumn) {
277 // fallback if the operator somehow was not set yet
278 if (col.filterOperator === undefined) { col.filterOperator = '='; }
280 if ( (col.filterOperator !== 'null') && (col.filterOperator !== 'not null') &&
281 (!col.filterValue || col.filterValue === '') &&
282 (col.filterValue !== '0') ) {
283 // if value is empty and we're _not_ checking for null/not null, clear
285 delete this.context.dataSource.filters[col.name];
286 col.isFiltered = false;
288 let op: string = col.filterOperator;
289 let val: string = col.filterValue;
290 if (col.filterOperator === 'null') {
293 } else if (col.filterOperator === 'not null') {
296 } else if (col.filterOperator === 'like' || col.filterOperator === 'not like') {
297 val = '%' + val + '%';
298 } else if (col.filterOperator === 'startswith') {
301 } else if (col.filterOperator === 'endswith') {
305 const filt: any = {};
306 if (col.filterOperator === 'not like') {
308 filt['-not'][col.name] = {};
309 filt['-not'][col.name]['like'] = val;
310 this.context.dataSource.filters[col.name] = [ filt ];
311 col.isFiltered = true;
314 filt[col.name][op] = val;
315 this.context.dataSource.filters[col.name] = [ filt ];
316 col.isFiltered = true;
319 this.context.reload();
320 this.closeDropdown();
322 clearFilter(col: GridColumn) {
323 // clear filter values...
325 // ... and inform the data source
326 delete this.context.dataSource.filters[col.name];
327 col.isFiltered = false;
329 this.context.reload();
330 this.closeDropdown();
334 // Timeout allows actions to occur before closing (some) dropdows
335 // clears the values (e.g. link selector)
336 setTimeout(() => this.dropdowns.forEach(drp => { drp.close(); }));
340 this.filterComboboxes.forEach(ctl => { ctl.applyEntryId(null); });
341 this.orgSelects.forEach(ctl => { ctl.reset(); });
342 if (this.dateSelectOne) { this.dateSelectOne.reset(); }
343 if (this.dateSelectTwo) { this.dateSelectTwo.reset(); }