Two related questions on jqGrid column header filters and the advanced filtering dialog - asp.net-mvc

In developing my first ASP.NET MVC 3 app using the jqGrid to display some data, I'm using the column header filters and also allowing for the advanced filter toolbar filtering to be done. Independently these things work pretty well.
First question - Has anyone a solution for communicating the current column header filter settings to the advanced filters?
As an example, a user can filter on the "Ice Cream Name" column, entering a partial name, e.g., "Chocolate", and it'll filter down to "Chocolate Explosion", "Dark Chocolate", etc. - great. What would be nice would be to open the advanced filter and have that "contains 'Chocolate'" column filter automatically populated in the advanced filter. I recognize that the other direction (where someone could AND or OR two values for the same column, e.g. 'Chocolate' OR 'Caramel') becomes problematic but in the other direction, it seems like it might be possible. Perhaps this is just a setting of the grid I'm missing. Anyone solved this?
Second question - I currently can do some filtering with the column header filters, show some result set in the grid and then go into the advanced filter dialog and set up a different filter. That will display the correct results but the column header filters are not cleared, giving the impression that the filtering is not working. How can I reset those column header filters after the use clicks the "Find" button on the dialog?

I find your question very interesting, so I prepared the demo which demonstrate how one can combine Advanced Searching dialog and Toolbar Searching in one grid.
One important, but simple trick is the usage of recreateFilter: true. Per default the searching dialog will be created once and then will be only hide or show. As the result the postData.filters parameter will be not refreshed. After setting recreateFilter: true the problem with filling of the advanced searching dialog with the values from the searching toolbar will be solved. I personally set the default searching options as the following
$.extend(
$.jgrid.search,
{
multipleSearch: true,
multipleGroup: true,
recreateFilter: true,
overlay: 0
}
);
Now to more complex part of the solution is the function refreshSerchingToolbar which I wrote. The function is not so simple, but it's simply in use:
loadComplete: function () {
refreshSerchingToolbar($(this), 'cn');
}
The last parameter is the same parameter which you used as defaultSearch property of the searching toolbar method filterToolbar (the default value is 'bw', but I personally prefer to use 'cn' and set jqGrid parameter ignoreCase: true).
If you fill the advanced searching dialog of the demo with the following field
and click the "Find" button, you will have the following grid:
(I marked the 'Total' column as non-searchable with respect of search: false to show only that all works correctly in the case also)
One can see that all fields of the searching toolbar excepting "Amount" are filled with the values from the searching dialog. The field are not filled because we used "grater or equal" operation instead of "equal". The function refreshSerchingToolbar fills only the elements of the searching toolbar which can be produced by the
Just as a reminder I should mention that in case of the usage of Filter Toolbar it is very important to define searchoptions.sopt options of the colModel. For all non-string column contains (dates, numbers, selects, int, currency) it is extremely important to have 'eq' as the first element of the sopt array. See here and here for details.
If you change the filter of the Advanced Dialog to the following
you will have as expected
At the end I include the code of the refreshSerchingToolbar function:
var getColumnIndex = function (grid, columnIndex) {
var cm = grid.jqGrid('getGridParam', 'colModel'), i = 0, l = cm.length;
for (; i < l; i += 1) {
if ((cm[i].index || cm[i].name) === columnIndex) {
return i; // return the colModel index
}
}
return -1;
},
refreshSerchingToolbar = function ($grid, myDefaultSearch) {
var postData = $grid.jqGrid('getGridParam', 'postData'), filters, i, l,
rules, rule, iCol, cm = $grid.jqGrid('getGridParam', 'colModel'),
cmi, control, tagName;
for (i = 0, l = cm.length; i < l; i += 1) {
control = $("#gs_" + $.jgrid.jqID(cm[i].name));
if (control.length > 0) {
tagName = control[0].tagName.toUpperCase();
if (tagName === "SELECT") { // && cmi.stype === "select"
control.find("option[value='']")
.attr('selected', 'selected');
} else if (tagName === "INPUT") {
control.val('');
}
}
}
if (typeof (postData.filters) === "string" &&
typeof ($grid[0].ftoolbar) === "boolean" && $grid[0].ftoolbar) {
filters = $.parseJSON(postData.filters);
if (filters && filters.groupOp === "AND" && typeof (filters.groups) === "undefined") {
// only in case of advance searching without grouping we import filters in the
// searching toolbar
rules = filters.rules;
for (i = 0, l = rules.length; i < l; i += 1) {
rule = rules[i];
iCol = getColumnIndex($grid, rule.field);
cmi = cm[iCol];
control = $("#gs_" + $.jgrid.jqID(cmi.name));
if (iCol >= 0 && control.length > 0) {
tagName = control[0].tagName.toUpperCase();
if (((typeof (cmi.searchoptions) === "undefined" ||
typeof (cmi.searchoptions.sopt) === "undefined")
&& rule.op === myDefaultSearch) ||
(typeof (cmi.searchoptions) === "object" &&
$.isArray(cmi.searchoptions.sopt) &&
cmi.searchoptions.sopt[0] === rule.op)) {
if (tagName === "SELECT") { // && cmi.stype === "select"
control.find("option[value='" + $.jgrid.jqID(rule.data) + "']")
.attr('selected', 'selected');
} else if (tagName === "INPUT") {
control.val(rule.data);
}
}
}
}
}
}
};
UPDATED: The above code is no more needed in case of usage free jqGrid 4.13.1 or higher. It contains the new default option loadFilterDefaults: true of the filterToolbar, which refreshes the values of the filter toolbar and the filter operations (if searchOperators: true option of filterToolbar is ised) if postData.filters and search: true are set (the filter is applied). Free jqGrid refreshes the filter toolbar on jqGridAfterLoadComplete (if loadFilterDefaults: true are set) or if the event jqGridRefreshFilterValues are explicitly triggered.

I know it's an old post - but if you have multiple grids on the same page the above code can add the filter text to the wrong grid.
Changing this in the first loop in refreshSearchingToolbar, from
control = $("#gs_" + $.jgrid.jqID(cm[i].name));
to
control = $("#gview_"+$grid.attr('id')+" #gs_" + $.jgrid.jqID(cm[i].name));
and this in the second loop from
control = $("#gs_" + $.jgrid.jqID(cmi.name));
to
control = $("#gview_"+$grid.attr('id')+" #gs_" + $.jgrid.jqID(cmi.name));
should do the trick.
Kudos to Oleg

Related

Google Sheets: Automate Hidden Rows For Empty Cells

I'm importing information into a Google Sheet that I'm hoping to print as a delivery list invoice.
The code below allows items marked "0" in Column H to disappear off the invoice, allowing the final product to appear cleaner.
I'm trying to figure out how to make the "Hide Row" function trigger for empty cells, so I don't have to manually enter in 0's.
As far as I can tell, the Filter function won't work because it won't update "OnEdit", only manually. I plan to repeat this for many sheets (one per customer) and require it to be automatic.
This is the code I have so far.
function onEdit() {
var s = SpreadsheetApp.getActive()
.getSheetByName('BELLTOWN');
s.showRows(1, s.getMaxRows());
s.getRange('H:H')
.getValues()
.forEach(function (r, i) {
if (r[0] !== '' && r[0].toString()
.charAt(0) == 0) s.hideRows(i + 1)
});
}
function getNote(cell) {
return SpreadsheetApp.getActiveSheet().getRange(cell).getComment();
}
Change
if (r[0] !== '' && r[0].toString()
to
if (r[0] == '' && r[0].toString()

Free Text Entry in Angular Material mdAutoComplete

I want my angular material autocomplete to be a list of suggestions but not requirements. However I'm not sure how to implement as their is no clear example from the Angular Material docs.
In the example below my model is $ctrl.item.category
Clearly the example below is wrong, as my model is linked to md-selected-item, but this only works if I select an item. I want the user to be able to free enter the text if the item is not in the list. Basically how autocomplete already works in most browsers.
I see plenty of questions on how to disable this, but they are not trying to disable so much as clean up the left over text when an item is not selected. In these cases when an item is not selected then the model value is null, but text is left in the input.
I want the text left int he input to be the model value if the person does not select (or a match is not made).
md-autocomplete(
md-floating-label="Category Name"
flex="50"
md-input-name="category"
md-selected-item="$ctrl.item.category"
md-search-text="catSearch"
md-items="category in $ctrl.categories"
md-item-text="category"
md-min-length="0"
md-select-on-match=""
md-match-case-insensitive=""
required=""
)
md-item-template
span(md-highlight-text="catSearch" md-highlight-flags="^i") {{category}}
My options ($ctrl.categories) is an array of strings ['Food','Liqour'] and I wan the user to be able to use one of those or free enter Tables as their choice.
In this case you should link md-search-text to your model.
If you want to implement fuzzy search you have to write the filter method yourself. Look at this example:
template:
<md-autocomplete
md-items="item in $ctrl.itemsFilter()"
md-item-text="item.label"
md-search-text="$ctrl.query"
md-selected-item="$ctrl.selected"
>
<md-item-template>
<span md-highlight-text="$ctrl.query">{{item.label}}</span>
</md-item-template>
<md-not-found>
No item matching "{{$ctrl.query}}" were found.
</md-not-found>
<div ng-messages="$ctrl.myValidator($ctrl.query)">
<div ng-message="short">Min 2 characters</div>
<div ng-message="required">Required value</div>
</div>
</md-autocomplete>
controller:
var items = [ ... ];
ctrl.itemsFilter = function itemsFilter() {
return ctrl.query ? filterMyItems(ctrl.query) : items;
};
ctrl.myValidator = function (value) {
return {
short: value && value.length < 2,
required : value && value.length < 1,
};
};
then you just need to add filterMyItems method to filter your items
To improve the answer of #masitko, I have implemented the filter in a way, that it adds the query to the filtered list. So it becomes selectable and a valid option. So it's possible to make the autocomplete a suggestion box.
I'm using ES6 in my projects. But it should be easily adaptable to ES5 code.
myFilter() {
if (!this.query) return this.items;
const
query = this.query.toLowerCase(),
// filter items where the query is a substing
filtered = this.items.filter(item => {
if (!item) return false;
return item.toLowerCase().includes(query);
});
// add search query to filtered list, to make it selectable
// (only if no exact match).
if (filtered.length !== 1 || filtered[0].toLowerCase() !== query) {
filtered.push(this.query);
}
return filtered;
}

Perform wildcard search of all (displayed) model fields in MVC?

I have an MVC5 View where I am using the Grid.MVC component (https://gridmvc.codeplex.com/). This allows me to easily display data from my Oracle DB and has out of the box functionality for Sorting/Filtering each Data Column. What I am trying to implement now is a Wildcard Search across all fields in my grid. For example, if I search the number "2" I'd like to return all records that contain a "2" be they string, decimal, or DateTime.
The filter capability on this grid performs filtering (for individual columns) partially by modifying the URL (http://homeURL/?grid-filter=Location.DEPT__1__accounting) such as 1 being Equals, 2 being Contains, 3 being StartsWith, and 4 being EndsWith and then after the next 2 underscores being the search criteria.
I first thought I was going down the right path by using JavaScript to modify to the desired URL via daisy-chaining all fields with the search criteria using a CONTAINS. I then noticed that decimal fields like [Cost] and DateTime (Oracle DB) fields like [Acquired_Date] have criteria settings of Equals, Greater Than, and Less Than, so I tried:
$('#SearchIcon').on("click", function (e) {
window.location = window.location.href.substr(0, window.location.href.indexOf('?'));
window.location = "?grid-filter=FIELD1__2__" + document.getElementById("Search").value +
"&grid-filter=FIELD2__2__" + document.getElementById("Search").value +
"&grid-filter=COST__1__" + document.getElementById("Search").value +
// etc. -- ALL FIELDS
"&grid-filter=NOTE__2__" + document.getElementById("Search").value;
});
This technically functions, but with the [&] is searching for a record(s) that have the corresponding search criteria in EVERY field. What I need is something similar, but with an OR [||] conditioning ---- unfortunately the grid component does not contain this form of functionality.
I then thought to pass the search criteria to a controller action and use it via a multi-WHERE clause and return only the records fitting the filter to my View:
public ActionResult SearchAssets(string searchCriteria)
{
fillPagingIntervalList();
var assetSearchResults = db.ENTITY_COLLECTION.Where(m => m.ID.ToString() == searchCriteria ||
m.Model.ToString() == searchCriteria ||
m.COST.ToString() == searchCriteria ||
// etc. -- ALL FIELDS
).FirstOrDefault();
var assetCount = db.ENTITY_COLLECTION.ToList().Count();
return View(assetSearchResults);
}
This resulted in an error with the WHERE cluase, stating to view the Inner Exception for details -- ORA-12704: character set mismatch MVC. I then reduced my multiple conditions down to just 2 fields to be searched for debugging:
var assetSearchResults = db.ENTITY_COLLECTION.Where(m => m.ID.ToString() == searchCriteria ||
m.Model.ToString() == searchCriteria).FirstOrDefault();
Resulting in:
EntityCommandExecutionException was unhandled by user code.
An exception of type 'System.Data.Entity.Core.EntityCommandExecutionException' occurred in EntityFramework.dll but was not handled in user code
Additional information: An error occurred while executing the command definition. See the inner exception for details.
Inner Exception: ORA-00932: inconsistent datatypes: expected - got NCLOB
Anyone have an idea on how to get what I want working? I also tried .Where(...con1...).Where(...con2...).Where(...etc...) with the same error resulting. I figured a wildcard search across all fields would be difficult to implement, but this is proving to be a whole bigger animal than I anticipated.
This will be very slow, but try this, which will load the entire collection into objects and let LINQ do the filtering on the client side:
public ActionResult SearchAssets(string searchCriteria)
{
fillPagingIntervalList();
var assetSearchResults = db.ENTITY_COLLECTION.ToList().Where(m => m.ID.ToString() == searchCriteria ||
m.Model.ToString() == searchCriteria ||
m.COST.ToString() == searchCriteria ||
// etc. -- ALL FIELDS
).FirstOrDefault();
var assetCount = db.ENTITY_COLLECTION.ToList().Count();
return View(assetSearchResults);
}
You could try something like this:
public ActionResult SearchAssets(string searchCriteria)
{
fillPagingIntervalList();
var assetSearchResults = db.ENTITY_COLLECTION.Where(m => m.ID.ToString() == searchCriteria)
.Union(db.ENTITY_COLLECTION.Where(m =>m.Model.ToString()==searchCriteria))
.Union(db.ENTITY_COLLECTION.Where(m =>m.COST.ToString() == searchCriteria))
// etc. -- ALL FIELDS
var assetCount = db.ENTITY_COLLECTION.ToList().Count();
return View(assetSearchResults);
}
Although, ultimately I would suggest looking into something like a predicate builder. Seems to be what you are doing anyhow.

extjs4 grid - changing column editor per row basis

ExtJS4 grid anticipates appropriate editor (cellEditor or rowEditor) per column.
If a column's header field is dateField - date selector will be applied on every row in that column.
What I need is an editor with different field editors per row, not per column.
The Extjs3 solution is provided here - unfortunately doesn't fit in Extjs4 case.
(please check that link to see explanatory images, cause I can't post images yet)
There's also a single column solution called property grid, but again - it supports only one column and is very deviated from the standard Ext.grid component
I have tried manually changing grid editor by customizing column.field and reloading grid.editingPlugin.editor, but always get a blank rowEditor panel with no fields.
//by default rowEditor applies textField to all cells - I'm trying to force custom numberFiled on apropriate row
var numberField=Ext.form.field.Number();
grid.columns[0].field=numberField;
//destroy current rowEditor's instance
delete grid.editingPlugin.editor;
//now, upon doubleClick on apropriate cell it should reinitialize itself (initEditor()) - and it does, but is an empty panel
what am I missing here? once I delete editingPlugin.editor everything should start from the beginning like during the first time rowEditor is called, but it looses all the fields
Solution for Ext4:
I was looking for a solution for this and this guy said the property grid has this behavior.
I have adapted it to work in a clean way for me
on initComponent I declared:
this.editors = {
'date' : Ext.create('Ext.grid.CellEditor', { field: Ext.create('Ext.form.field.Date', {selectOnFocus: true})}),
'string' : Ext.create('Ext.grid.CellEditor', { field: Ext.create('Ext.form.field.Text', {selectOnFocus: true})}),
'number' : Ext.create('Ext.grid.CellEditor', { field: Ext.create('Ext.form.field.Number', {selectOnFocus: true})}),
'int' : Ext.create('Ext.grid.CellEditor', { field: Ext.create('Ext.form.field.Number', {selectOnFocus: true})}),
'boolean' : Ext.create('Ext.grid.CellEditor', { field: Ext.create('Ext.form.field.ComboBox', {
editable: false,
store: [[ true, 'Sim' ], [false, 'Não' ]]
})})
};
I used these functions to help me (copied):
this.renderCell = function(val, meta, rec) {
var result = val;
if (Ext.isDate(val)) {
result = me.renderDate(val);
} else if (Ext.isBoolean(val)) {
result = me.renderBool(val);
}
return Ext.util.Format.htmlEncode(result);
};
this.getCellEditor = function(record, column) {
return this.editors[record.get('type')];
};
And finally, associate these functions to the column:
{text: "Valor", name : 'colunaValor', width: 75, sortable: true, dataIndex: 'valor', width:200,
renderer: Ext.Function.bind(this.renderCell, this),
getEditor: Ext.Function.bind(this.getCellEditor, this)
}

jQuery UI Datepicker - How can i, on select, highlight a particular range of dates?

I want to, when the user selects a date, highlight the following 11 days. The intention is to show a 12-day range selected depending on whatever day was chosen.
I've seen the "date range picker" from http://www.filamentgroup.com/ suggested, but this doesnt really give me the visualization i want, it just lets a user pick a range (from/to) as i understand it.
Any suggestion on how i can realize this?
Cheers
You can use the beforeShowDay event to give a CSS style for the dates you need to highlight.
Code:
$(document).ready(function(){
var selected = null;
$('#datePicker').datepicker({beforeShowDay: function(date) {
if (selected != null && date.getTime() > selected.getTime() &&
(date.getTime() - selected.getTime()) < 12*24*3600*1000) {
return [true, "highlighted"];
}
return [true, ""];
}});
$("#datePicker").datepicker( 'option' , 'onSelect', function() {
str = $(this).val().toString();
selected=$.datepicker.parseDate('mm/dd/yy', str);
console.log(selected);
});
});

Resources