Allow for searching on specified fields instead of entire object
Hi,
I have a datagrid bound to a complex business object, and only a few properties are shown in the grid. One of the use-cases is the table-wide search on words and parts of words.
The 'problem' is that koGrid seems to check ALL properties in the associated model, instead of just the ones that are shown. This leads to unexpected results, because some rows don't 'visually' contain the search words specified.
Wouldn't it be a good addition if we could specify which properties that have to be included in the search, instead of scanning the entire object for properties?
if (!condition.column) {
for (var prop in item) {
if (item.hasOwnProperty(prop)) {
var pVal = ko.utils.unwrapObservable(item[prop]);
if (pVal && condition.regex.test(pVal.toString())) {
return true;
}
}
}
return false;
}
Kr Sander
The pre-v2.0 filtering behavior had a search box above each column, which was a lot better IMO. Not only are you now more limited in how to search, but you also get the problem you described. Anyway, with a bit of a hack I managed to get the pre-v2.0 filtering behavior back without modifying the sourcecode of the grid:
First, add a custom ko binding:
ko.bindingHandlers.columHeaderSearch = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
$(element).change(function () {
var property = allBindingsAccessor().searchProperty;
var searchQuery = element.value;
vm.FilterInfo(AddFilter(vm.FilterInfo(), property, searchQuery));
});
}
};
Use the binding in a custom headercelltemplate (it's the default template with an extra div and textbox):
<script type="text/html" id="headerCellSearchTemplate">
<div>
<div data-bind="attr: { 'class': 'colt' + $index() + ' kgHeaderText' }" >
<input style="width:80px;" type="text" data-bind="columHeaderSearch: field , searchProperty : field"></input>
</div>
<div data-bind="style: { cursor : sortable() ? 'pointer' : 'default' }, click: sort, css: {'kgSorted': !noSortVisible }, attr: {'class': 'kgHeaderSortColumn ' + headerClass()}">
<div data-bind="attr: { 'class': 'colt' + $index() + ' kgHeaderText' }, html: displayName"></div>
<div class="kgSortButtonDown" data-bind="visible: showSortButtonDown"></div>
<div class="kgSortButtonUp" data-bind="visible: showSortButtonUp"></div>
<div data-bind="visible: resizable, click: gripClick, mouseEvents: { mouseDown: gripOnMouseDown }" class="kgHeaderGrip" ></div>
</div>
</div>
</script>
Next, apply the template to the columns you want to search on. my Addfilter function does some string concatenation to send something like "filter=FirstName:'Sander';LastName:'D'" to the server, depending on your backend this will be different. I'm not really proud of this approach though.. Because you need a hard link to your viewmodel's FilterInfo property, so re-usability sucks. I'm sure it can be made generic, but I'm coping with serious time constraints, as always :)
I like your approach with the column headers having a searchfield with custom binding (I also remember this from pre 2.0).
I have implemented a search expression parser, with the kogrid search string being bound to a computed that executes this parser on a separate inputfields content.
this.availableOptions = new ko.observableArray(client.be.product.models.EnergyTypes);
this.selectedOption = new ko.observable(this.availableOptions()[0]);
this.searchString = new ko.observable('');
this.gridFilter = new ko.computed(function() {
var filter =
client.be.product.helpers.searchStringBuilder(
me.searchString(),
{ 'energyType': me.selectedOption() ? me.selectedOption().valuesMatching : [] });
return filter;
});
I had to change the koGrid implementation to do AND instead of OR searches. Might have overlooked that functionality though.
Would do a pull request if only I knew what I was doing - can't get the build running for KoGrid.