Skip to content

Commit 331accf

Browse files
committed
feat: convert jQuery to native element on few more filters
1 parent 7e9e40b commit 331accf

File tree

6 files changed

+226
-221
lines changed

6 files changed

+226
-221
lines changed

src/app/modules/angular-slickgrid/filters/inputFilter.ts

+58-63
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,25 @@ import {
99
OperatorString,
1010
SearchTerm,
1111
} from './../models/index';
12-
13-
// using external non-typed js libraries
14-
declare const $: any;
12+
import { BindingEventService } from '../services/bindingEvent.service';
13+
import { emptyElement } from '../services/utilities';
1514

1615
export class InputFilter implements Filter {
16+
protected _bindEventService: BindingEventService;
1717
protected _clearFilterTriggered = false;
1818
protected _debounceTypingDelay = 0;
1919
protected _shouldTriggerQuery = true;
2020
protected _inputType = 'text';
2121
protected _timer?: any;
22-
protected $filterElm: any;
23-
grid: any;
22+
protected _filterElm!: HTMLInputElement;
23+
grid!: any;
2424
searchTerms: SearchTerm[] = [];
2525
columnDef!: Column;
2626
callback!: FilterCallback;
2727

28-
constructor() { }
28+
constructor() {
29+
this._bindEventService = new BindingEventService();
30+
}
2931

3032
/** Getter for the Column Filter */
3133
get columnFilter(): ColumnFilter {
@@ -49,7 +51,7 @@ export class InputFilter implements Filter {
4951

5052
/** Getter of the Operator to use when doing the filter comparing */
5153
get operator(): OperatorType | OperatorString {
52-
return this.columnFilter && this.columnFilter.operator || this.defaultOperator;
54+
return this.columnFilter?.operator ?? this.defaultOperator;
5355
}
5456

5557
/** Setter for the filter operator */
@@ -84,43 +86,38 @@ export class InputFilter implements Filter {
8486
// filter input can only have 1 search term, so we will use the 1st array index if it exist
8587
const searchTerm = (Array.isArray(this.searchTerms) && this.searchTerms.length >= 0) ? this.searchTerms[0] : '';
8688

87-
// step 1, create HTML string template
88-
const filterTemplate = this.buildTemplateHtmlString();
89+
// step 1, create the DOM Element of the filter & initialize it if searchTerm is filled
90+
this._filterElm = this.createDomElement(searchTerm);
8991

90-
// step 2, create the DOM Element of the filter & initialize it if searchTerm is filled
91-
this.$filterElm = this.createDomElement(filterTemplate, searchTerm);
92-
93-
// step 3, subscribe to the input change event and run the callback when that happens
92+
// step 2, subscribe to the input event and run the callback when that happens
9493
// also add/remove "filled" class for styling purposes
9594
// we'll use all necessary events to cover the following (keyup, change, mousewheel & spinner)
96-
this.$filterElm.on('keyup blur change wheel', this.handleInputChange.bind(this));
95+
this._bindEventService.bind(this._filterElm, ['keyup', 'blur', 'change', 'wheel'], this.handleInputChange.bind(this));
9796
}
9897

9998
/**
10099
* Clear the filter value
101100
*/
102101
clear(shouldTriggerQuery = true) {
103-
if (this.$filterElm) {
102+
if (this._filterElm) {
104103
this._clearFilterTriggered = true;
105104
this._shouldTriggerQuery = shouldTriggerQuery;
106105
this.searchTerms = [];
107-
this.$filterElm.val('');
108-
this.$filterElm.trigger('change');
106+
this._filterElm.value = '';
107+
this._filterElm.dispatchEvent(new Event('change'));
109108
}
110109
}
111110

112111
/**
113112
* destroy the filter
114113
*/
115114
destroy() {
116-
if (this.$filterElm) {
117-
this.$filterElm.off('keyup blur change wheel').remove();
118-
}
119-
this.$filterElm = null;
115+
this._bindEventService.unbindAll();
116+
this._filterElm?.remove?.();
120117
}
121118

122-
getValue() {
123-
return this.$filterElm.val();
119+
getValue(): string {
120+
return this._filterElm.value;
124121
}
125122

126123
/** Set value(s) on the DOM element */
@@ -129,7 +126,7 @@ export class InputFilter implements Filter {
129126
let searchValue: SearchTerm = '';
130127
for (const value of searchValues) {
131128
searchValue = operator ? this.addOptionalOperatorIntoSearchString(value, operator) : value;
132-
this.$filterElm.val(searchValue);
129+
this._filterElm.value = `${searchValue ?? ''}`;
133130
}
134131

135132
// set the operator when defined
@@ -141,14 +138,14 @@ export class InputFilter implements Filter {
141138
// ------------------
142139

143140
/**
144-
* When loading the search string from the outside into the input text field, we should also add the prefix/suffix of the operator.
145-
* We do this so that if it was loaded by a Grid Presets then we should also add the operator into the search string
146-
* Let's take these 3 examples:
147-
* 1. (operator: '>=', searchTerms:[55]) should display as ">=55"
148-
* 2. (operator: 'StartsWith', searchTerms:['John']) should display as "John*"
149-
* 3. (operator: 'EndsWith', searchTerms:['John']) should display as "*John"
150-
* @param operator - operator string
151-
*/
141+
* When loading the search string from the outside into the input text field, we should also add the prefix/suffix of the operator.
142+
* We do this so that if it was loaded by a Grid Presets then we should also add the operator into the search string
143+
* Let's take these 3 examples:
144+
* 1. (operator: '>=', searchTerms:[55]) should display as ">=55"
145+
* 2. (operator: 'StartsWith', searchTerms:['John']) should display as "John*"
146+
* 3. (operator: 'EndsWith', searchTerms:['John']) should display as "*John"
147+
* @param operator - operator string
148+
*/
152149
protected addOptionalOperatorIntoSearchString(inputValue: SearchTerm, operator: OperatorType | OperatorString): string {
153150
let searchTermPrefix = '';
154151
let searchTermSuffix = '';
@@ -181,64 +178,62 @@ export class InputFilter implements Filter {
181178
return outputValue;
182179
}
183180

184-
/**
185-
* Create the HTML template as a string
186-
*/
187-
protected buildTemplateHtmlString() {
188-
const fieldId = this.columnDef && this.columnDef.id;
189-
let placeholder = (this.gridOptions) ? (this.gridOptions.defaultFilterPlaceholder || '') : '';
190-
if (this.columnFilter && this.columnFilter.placeholder) {
191-
placeholder = this.columnFilter.placeholder;
192-
}
193-
return `<input type="${this._inputType || 'text'}" role="presentation" autocomplete="off" class="form-control search-filter filter-${fieldId}" placeholder="${placeholder}"><span></span>`;
194-
}
195-
196181
/**
197182
* From the html template string, create a DOM element
198-
* @param filterTemplate
183+
* @param {Object} searchTerm - filter search term
184+
* @returns {Object} DOM element filter
199185
*/
200-
protected createDomElement(filterTemplate: string, searchTerm?: SearchTerm) {
201-
const fieldId = this.columnDef && this.columnDef.id;
202-
const $headerElm = this.grid.getHeaderRowColumn(fieldId);
203-
$($headerElm).empty();
186+
protected createDomElement(searchTerm?: SearchTerm): HTMLInputElement {
187+
const columnId = this.columnDef?.id ?? '';
188+
const headerElm = this.grid.getHeaderRowColumn(columnId);
189+
emptyElement(headerElm);
204190

205191
// create the DOM element & add an ID and filter class
206-
const $filterElm = $(filterTemplate);
192+
let placeholder = this.gridOptions?.defaultFilterPlaceholder ?? '';
193+
if (this.columnFilter?.placeholder) {
194+
placeholder = this.columnFilter.placeholder;
195+
}
196+
197+
const inputElm = document.createElement('input');
198+
inputElm.type = this._inputType || 'text';
199+
inputElm.className = `form-control search-filter filter-${columnId}`;
200+
inputElm.autocomplete = 'off';
201+
inputElm.placeholder = placeholder;
202+
inputElm.setAttribute('role', 'presentation');
207203

208-
$filterElm.val(searchTerm);
209-
$filterElm.data('columnId', fieldId);
204+
inputElm.value = (searchTerm ?? '') as string;
205+
inputElm.dataset.columnid = `${columnId}`;
210206

211207
// if there's a search term, we will add the "filled" class for styling purposes
212208
if (searchTerm) {
213-
$filterElm.addClass('filled');
209+
inputElm.classList.add('filled');
214210
}
215211

216-
// append the new DOM element to the header row
217-
if ($filterElm && typeof $filterElm.appendTo === 'function') {
218-
$filterElm.appendTo($headerElm);
219-
}
212+
// append the new DOM element to the header row & an empty span
213+
headerElm.appendChild(inputElm);
214+
headerElm.appendChild(document.createElement('span'));
220215

221-
return $filterElm;
216+
return inputElm;
222217
}
223218

224219
/**
225220
* Event handler to cover the following (keyup, change, mousewheel & spinner)
226221
* We will trigger the Filter Service callback from this handler
227222
*/
228-
protected handleInputChange(event: KeyboardEvent & { target: any; }) {
223+
protected handleInputChange(event: Event) {
229224
if (this._clearFilterTriggered) {
230225
this.callback(event, { columnDef: this.columnDef, clearFilterTriggered: this._clearFilterTriggered, shouldTriggerQuery: this._shouldTriggerQuery });
231-
this.$filterElm.removeClass('filled');
226+
this._filterElm.classList.remove('filled');
232227
} else {
233228
const eventType = event?.type ?? '';
234-
let value = event?.target?.value ?? '';
229+
let value = (event?.target as HTMLInputElement)?.value ?? '';
235230
const enableWhiteSpaceTrim = this.gridOptions.enableFilterTrimWhiteSpace || this.columnFilter.enableTrimWhiteSpace;
236231
if (typeof value === 'string' && enableWhiteSpaceTrim) {
237232
value = value.trim();
238233
}
239-
value === '' ? this.$filterElm.removeClass('filled') : this.$filterElm.addClass('filled');
234+
value === '' ? this._filterElm.classList.remove('filled') : this._filterElm.classList.add('filled');
240235
const callbackArgs = { columnDef: this.columnDef, operator: this.operator, searchTerms: [value], shouldTriggerQuery: this._shouldTriggerQuery };
241-
const typingDelay = (eventType === 'keyup' && event?.key !== 'Enter') ? this._debounceTypingDelay : 0;
236+
const typingDelay = (eventType === 'keyup' && (event as KeyboardEvent)?.key !== 'Enter') ? this._debounceTypingDelay : 0;
242237

243238
if (typingDelay > 0) {
244239
clearTimeout(this._timer);

src/app/modules/angular-slickgrid/filters/inputMaskFilter.ts

+39-36
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ export class InputMaskFilter extends InputFilter {
2828
this.searchTerms = (args.hasOwnProperty('searchTerms') ? args.searchTerms : []) || [];
2929

3030
// get input mask from params (can be in columnDef or columnFilter params)
31-
if (this.columnDef && this.columnDef.params && this.columnDef.params.mask) {
31+
if (this.columnDef?.params?.mask) {
3232
this._inputMask = this.columnDef.params.mask;
33-
} else if (this.columnFilter && this.columnFilter.params && this.columnFilter.params.mask) {
33+
} else if (this.columnFilter?.params?.mask) {
3434
this._inputMask = this.columnFilter.params.mask;
3535
}
3636

@@ -42,47 +42,50 @@ export class InputMaskFilter extends InputFilter {
4242
// filter input can only have 1 search term, so we will use the 1st array index if it exist
4343
const searchTerm = (Array.isArray(this.searchTerms) && this.searchTerms.length >= 0) ? this.searchTerms[0] : '';
4444

45-
// step 1, create HTML string template
46-
const filterTemplate = this.buildTemplateHtmlString();
45+
// step 1, create the DOM Element of the filter & initialize it if searchTerm is filled
46+
this._filterElm = this.createDomElement(searchTerm);
4747

48-
// step 2, create the DOM Element of the filter & initialize it if searchTerm is filled
49-
this.$filterElm = this.createDomElement(filterTemplate, searchTerm);
50-
51-
// step 3, subscribe to the input change event and run the callback when that happens
48+
// step 2, subscribe to the input event and run the callback when that happens
5249
// also add/remove "filled" class for styling purposes
50+
// we'll use all necessary events to cover the following (keyup, change, mousewheel & spinner)
51+
this._bindEventService.bind(this._filterElm, ['keyup', 'blur', 'change'], this.handleInputChange.bind(this));
52+
}
5353

54-
this.$filterElm.on('keyup blur change', (e: any) => {
55-
let value = '';
56-
if (e && e.target && e.target.value) {
57-
let targetValue = e.target.value;
58-
const enableWhiteSpaceTrim = this.gridOptions.enableFilterTrimWhiteSpace || this.columnFilter.enableTrimWhiteSpace;
59-
if (typeof targetValue === 'string' && enableWhiteSpaceTrim) {
60-
targetValue = targetValue.trim();
61-
}
54+
/**
55+
* Event handler to cover the following (keyup, change, mousewheel & spinner)
56+
* We will trigger the Filter Service callback from this handler
57+
*/
58+
protected handleInputChange(event: Event) {
59+
let value = '';
60+
if ((event?.target as HTMLInputElement)?.value) {
61+
let targetValue = (event?.target as HTMLInputElement)?.value ?? '';
62+
const enableWhiteSpaceTrim = this.gridOptions.enableFilterTrimWhiteSpace || this.columnFilter.enableTrimWhiteSpace;
63+
if (typeof targetValue === 'string' && enableWhiteSpaceTrim) {
64+
targetValue = targetValue.trim();
65+
}
6266

63-
// if it has a mask, we need to do a bit more work
64-
// and replace the filter string by the masked output without triggering an event
65-
const unmaskedValue = this.unmaskValue(targetValue);
66-
const maskedValue = this.maskValue(unmaskedValue);
67-
value = unmaskedValue;
67+
// if it has a mask, we need to do a bit more work
68+
// and replace the filter string by the masked output without triggering an event
69+
const unmaskedValue = this.unmaskValue(targetValue);
70+
const maskedValue = this.maskValue(unmaskedValue);
71+
value = unmaskedValue;
6872

69-
if (e.keyCode >= 48) {
70-
this.$filterElm.val(maskedValue); // replace filter string with masked string
71-
e.preventDefault();
72-
}
73+
if ((event as KeyboardEvent)?.keyCode >= 48) {
74+
this._filterElm.value = maskedValue; // replace filter string with masked string
75+
event.preventDefault();
7376
}
77+
}
7478

75-
if (this._clearFilterTriggered) {
76-
this.callback(e, { columnDef: this.columnDef, clearFilterTriggered: this._clearFilterTriggered, shouldTriggerQuery: this._shouldTriggerQuery });
77-
this.$filterElm.removeClass('filled');
78-
} else {
79-
this.$filterElm.addClass('filled');
80-
this.callback(e, { columnDef: this.columnDef, operator: this.operator, searchTerms: [value], shouldTriggerQuery: this._shouldTriggerQuery });
81-
}
82-
// reset both flags for next use
83-
this._clearFilterTriggered = false;
84-
this._shouldTriggerQuery = true;
85-
});
79+
if (this._clearFilterTriggered) {
80+
this.callback(event, { columnDef: this.columnDef, clearFilterTriggered: this._clearFilterTriggered, shouldTriggerQuery: this._shouldTriggerQuery });
81+
this._filterElm.classList.remove('filled');
82+
} else {
83+
this._filterElm.classList.add('filled');
84+
this.callback(event, { columnDef: this.columnDef, operator: this.operator, searchTerms: [value], shouldTriggerQuery: this._shouldTriggerQuery });
85+
}
86+
// reset both flags for next use
87+
this._clearFilterTriggered = false;
88+
this._shouldTriggerQuery = true;
8689
}
8790

8891
/** From a regular string, we will use the mask to output a new string */

0 commit comments

Comments
 (0)