reset filter on tablesorter when going back to page and filtered select drop-down returns no records - tablesorter

I am using tablesorter on a page where I display products. One of the column is the status displayed in a drop-down. When I click a product I can edit it and change its status. When I go back to the product list, the list is still filtered which is OK but if the product was the last with this status, the product list is, of course, empty and the drop-down shows no selection because the status does not exist anymore. I want to clear the filter on the drop-down when I get back on the product list page and there is no more product with this status. How can I achieve this?
My code:
<table cellspacing="1" cellpadding="1" id="ProductTable" class="tablesorter">
<thead>
<tr>
<th class="reorder-false reorder-block-left">Product Name</th>
<th class="">Description</th>
<th class="filter-select filter-onlyAvail">Status</th>
<th class="">Status Change Date</th>
</tr>
</thead>
<tbody id="ProductList">
<tr class="ProductRows" id="P_1906">
<td ><a style="text-decoration:none" href="/Product/Index/1906">Product #1</a></td>
<td><span class="">Test #1</span></td>
<td><span class="">Active</span></td>
<td><span class="time">2015/07/13 16:41:03</span></td>
</tr>
<tr class="ProductRows" id="P_1993">
<td ><a style="text-decoration:none" href="/Product/Index/1993">Test #2</a></td>
<td><span class="">Test #2</span></td>
<td><span class="">Backorder</span></td>
<td><span class="time">2015/08/25 10:39:23</span></td>
</tr>
</tbody>
</table>
$("#ProductTable").tablesorter({
theme: 'blue',
widthFixed: false,
widgets: [ "zebra", "filter", "resizable" ],
widgetOptions: {
filter_childRows: false,
filter_columnFilters: true,
filter_filteredRow: 'filtered',
filter_hideFilters: true,
filter_ignoreCase: true,
filter_liveSearch: true,
filter_onlyAvail: 'filter-onlyAvail',
filter_reset: 'button.reset',
filter_saveFilters: true,
filter_searchDelay: 300,
filter_serversideFiltering: false,
filter_startsWith: false,
filter_useParsedData: false,
filter_defaultAttrib: 'data-value'
}
});
Thank you.

I made it using the following code:
var FilterCallOnce = false;
$("#ProductTable").tablesorter({
theme: 'blue',
widthFixed: false,
widgets: [ "zebra", "filter", "resizable" ],
widgetOptions: {
filter_childRows: false,
filter_columnFilters: true,
filter_filteredRow: 'filtered',
filter_hideFilters: true,
filter_ignoreCase: true,
filter_liveSearch: true,
filter_onlyAvail: 'filter-onlyAvail',
filter_reset: 'button.reset',
filter_saveFilters: true,
filter_searchDelay: 300,
filter_serversideFiltering: false,
filter_startsWith: false,
filter_useParsedData: false,
filter_defaultAttrib: 'data-value'
}
}).bind('filterEnd', function(e, filter){
var table = document.getElementById("ProductList");
var nrow = 0;
var nfiltered = 0;
if (FilterCallOnce == false)
{
FilterCallOnce = true;
if (table != null)
{
for (var i = 0, row; row = table.rows[i]; i++)
{
nrow++;
if (!hasClass(row, 'filtered'))
nfiltered++;
}
if ((nrow > 0) && (nfiltered == 0))
$('#ProductTable').trigger('filterReset');
}
}
});
function hasClass(element, cls)
{
return (' ' + element.className + ' ').indexOf(' ' + cls + ' ') > -1;
}

Related

jQueryUI resizable - when you change the width of a column - all the other speakers twitch

I use jQueryUI resizable for resizing columns in the table.
When you change the width of one column, the other columns must remain unchanged. Should only varies the width of the table
Why do you change the width of one column, while others twitch? If all columns to make the minimum width, and then begin to increase the width of the last column (right column), all the others begin to twitch.
<div class="table-wrapper">
<table id="table">
<tbody>
<tr>
<td class="gray">Zagreb</td>
<td class="gray">Kinshasa</td>
<td class="gray">Kishinev</td>
<td class="gray">Krakow</td>
<td class="gray">Lima</td>
<td class="gray">Lisbon</td>
</tr>
<tr>
<td>Zagreb</td>
<td>Kinshasa</td>
<td>Kishinev</td>
<td>Krakow</td>
<td>Lima</td>
<td>Lisbon</td>
</tr>
<tr>
<td>Zagreb</td>
<td>Kinshasa</td>
<td>Kishinev</td>
<td>Krakow</td>
<td>Lima</td>
<td>Lisbon</td>
</tr>
<tr>
<td>Zagreb</td>
<td>Kinshasa</td>
<td>Kishinev</td>
<td>Krakow</td>
<td>Lima</td>
<td>Lisbon</td>
</tr>
<tr>
<td>London</td>
<td>Los Angeles</td>
<td>Luxembourg</td>
<td>Madrid</td>
<td>Manila</td>
<td>Mexico</td>
</tr>
<tr>
<td>Milan</td>
<td>Montreal</td>
<td>Mumbai</td>
<td>Nairobi</td>
<td>Nicosia</td>
<td>New York</td>
</tr>
<tr>
<td>Osaka</td>
<td>Oslo</td>
<td>Ottawa</td>
<td>Paris</td>
<td>Prague</td>
<td>Riga</td>
</tr>
<tr>
<td>Rome</td>
<td>Rotterdam</td>
<td>Salvador</td>
<td>Samarkand</td>
<td>Sydney</td>
<td>Singapore</td>
</tr>
<tr>
<td>Sofia</td>
<td>Istanbul</td>
<td>Taipei</td>
<td>Tbilisi</td>
<td>Zurich</td>
<td>Chicago</td>
</tr>
</tbody>
</table>
</div>
JS code:
$(document).ready(function(e)
{
var $table = $("#table");
var startW = 0;
var startW_neighbor = 0;
var td_index_neighbor = 0;
var ui_minColWidth = 0;
$table.find("tr:first th, tr:first td").resizable(
{
handles: 'e',
minWidth: 30,
start : function(event,ui)
{
ui_minColWidth = $(this).resizable( "option", "minWidth");
startW = $(this).width();
startW_neighbor = ui.element.parents('.table-wrapper').find('table#table').width();
},
stop: function (event, ui)
{
},
resize: function (event, ui)
{
var td_width = ui.element.width();
var table = ui.element.parents('.table-wrapper').find('table#table');
var d = Number(td_width) - Number(ui.originalSize.width);
td_width = Number(startW_neighbor) + Number(d) - 2;
table.width(td_width);
}
});
});
In example: http://jsfiddle.net/djmartini/08p3Lqcm/
You're at the same time interfering with normal behavior of resizable and relying on it to calculate some values, so at some point it breaks. In that case you're calculating the width difference based on resizable behavior and applying it to change the behavior. But when resizable doesn't have any more place to resize, the difference is 0, hence nothing moves anymore.
If you want to change the behavior of resizable, you could work with mouse position and micro manage the change in width. In that case you'll need to handle cases where your mouse moves, but nothing gets resized. This happens when columns cannot reduce size anymore.
Something like this seems to work:
$table.find("tr:first th, tr:first td").resizable({
handles: 'e',
minWidth: 30,
start: function (event, ui) {
ui_minColWidth = $(this).resizable("option", "minWidth");
startW = $(this).width();
td_width = startW;//this to check if width can change
startW_neighbor = ui.element.parents('.table-wrapper').find('table#table').width();
startMouseX = event.pageX;//to calculate mouse diffrence
},
stop: function (event, ui) {
},
resize: function (event, ui) {;
var mouseDiff = event.pageX - startMouseX;//mouse mouvement
//If the mouse is negative, it can resize only if column
//isn't at min width. When mouse positive resize always.
if (ui.element.width() != td_width || mouseDiff > 0) {
td_width = ui.element.width();
var table = ui.element.parents('.table-wrapper').find('table#table');
var d = Number(td_width) - Number(ui.originalSize.width);
table_width = Number(startW_neighbor) + mouseDiff;
table.width(table_width);
}
}
});
fiddle: http://jsfiddle.net/wLbn5qjk/1/
EDIT:
You can push this logic and work with alsoResize, the result is a bit better:
$table.find("tr:first th, tr:first td").resizable({
handles: 'e',
minWidth: 30,
alsoResize: 'table',
start: function (event, ui) {
ui_minColWidth = $(this).resizable("option", "minWidth");
startW = $(this).width();
td_width = ui.element.width();
startW_neighbor = ui.element.parents('.table-wrapper').find('table#table').width();
startMouseX = event.pageX;
},
stop: function (event, ui) {
},
resize: function (event, ui) {;
var mouseDiff = event.pageX - startMouseX;
if (ui.element.width() == td_width && mouseDiff < 0) {;
$('table').width(table_width);
}
td_width = ui.element.width();
table_width = $('table').width()
}
});
http://jsfiddle.net/hrzj68wp/1/

Incorrect sorting in TableSorter

Strange behavoir in tablesorter:
$(document).ready(function() {
var timeInSec = function(node) { return $(node).attr("time") };
var sortDigit = { sorter : "digit" };
$("#agentRapport").tablesorter({
headers: {
1: sortDigit,
2: sortDigit
},
textExtraction: {
1: timeInSec,
2: timeInSec
},
sortList: [[1,1]]
});
});
Resulting sorted table looks like this (sorted the following myself like you would see it in the browser):
...
<tr>
<td>Banana</td>
<td time="1411252">23 min 31 sec</td>
<td time="352813">5 min 52 sec</td>
<td>4</td>
<td>225</td>
</tr>
<tr>
<td>Apple</td>
<td time="1391952">23 min 11 sec</td>
<td time="347988">5 min 47 sec</td>
<td>4</td>
<td>86</td>
</tr>
<tr>
<td>Plum</td>
<td time="1427192">23 min 47 sec</td>
<td time="356798">5 min 56 sec</td>
<td>4</td>
<td>119</td>
</tr>
<tr>
<td>Pear</td>
<td time="1381072">23 min 1 sec</td>
<td time="345268">5 min 45 sec</td>
<td>4</td>
<td>108</td>
</tr>
...
Is this a bug?
If you are using the original tablesorter (v2.0.5b) from tablesorter.com, the above code will not work since the textExtraction function will not allow the targetting of specific columns.
However, you can use my fork of tablesorter which does allow this option (demo)
The fork should work without any extra functions since it also has an alpha-numeric sort (demo):
$(function () {
$("#agentRapport").tablesorter({
sortList: [
[1, 1]
]
});
});
But if you want to continue using the original version of tablesorter, you can use a parser, and save yourself from adding a time="#" attribute to every cell (demo):
$.tablesorter.addParser({
id: "min&sec",
is: function (s) {
return false;
},
format: function (s, table) {
var min = (parseInt(s.match(/(\d+)(?:\s+min)/), 10) || 0) * 60,
sec = (parseInt(s.match(/(\d+)(?:\s+sec)/), 10) || 0);
return min + sec;
},
type: "numeric"
});
$(function () {
$("#agentRapport").tablesorter({
headers: {
1: { sorter: "min&sec" },
2: { sorter: "min&sec" }
},
sortList: [
[1, 1]
]
});
});

How to get the selected Table cell value in datatable

I am using jquery-2.0.3.min.js, bootstrap.min.js, jquery-ui-1.10.3.min.js, DataTables-1.9.4 with tabletools, datatables.net/blog/Twitter_Bootstrap_2
My View
<div id="windowDepartment" title="Departments"></div>
<table cellpadding="0" cellspacing="0" border="0" class="table table-striped table-bordered table-hover table-condensed" id="DepartmentTable">
<thead>
<tr>
<th>departmentID</th>
<th>departmentName</th>
<th>description</th>
<th>Action</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
Datatable initialisation Script
$(document).ready(function () {
oDepartmentTable = $('#DepartmentTable').dataTable(
{
"sDom": "T<'clear'>lfrtip",
"bJQueryUI": true,
"sPaginationType": "full_numbers",
"bServerSide": true,
"sAjaxSource": "Department/AjaxList",
"aaSorting": [[2, 'asc'], [3, 'asc']],
"aoColumns": [
{ "mDataProp": "departmentID", "sType": "string", "bVisible": false, "bSearchable": false },
{ "mDataProp": "departmentName", "sType": "string", "bVisible": true, "bSearchable": true },
{ "mDataProp": "description", "sType": "string", "bVisible": true, "bSearchable": true },
{ "mDataProp": null,"bSearchable": false,
"sDefaultContent": '<div class="btn-group"><a class="btn btn-mini dropdown-toggle" data-toggle="dropdown" href="#"><span class="icon-circle-arrow-down"></span></a><ul class="dropdown-menu"><li><a class="editDepartment"> <i class="icon-pencil"></i> Edit</a></li><li><a class="deleteDepartment"> <i class="icon-trash"></i> Delete</a></li></ul></div>'
}
],
"oTableTools": {
"sSwfPath": "/Content/DataTables-1.9.4/extras/TableTools/media/swf/copy_csv_xls_pdf.swf"
}
});
});
EDIT FORM SCRIPT
$(document).ready(function () {
$('#DepartmentTable tbody').on('click', 'a.editDepartment', function (e) {
e.preventDefault();
//1. Dose not work shows "not available"
var aData = oDepartmentTable.fnGetData(this)
//2. Gets the correct ID if "bVisilble=true"
var departmentid = $(this).parent().parent().parent().parent().parent().children()[0].innerHTML ;
//goto Edit Controller. DepartmentID is required here
$.get('Department/Edit/' + departmentid , function (data) {
$('div#windowDepartment').html(data);
//Open Dialog box
$("#windowDepartment").dialog().dialog({
resizable: true,
height: 500,
width: 500,
modal: true,
buttons:
{
Edit: function () {
editDepartment(); //Saves the data. Works fine
}, // end ok button
Cancel: function () {
$(this).dialog("close");
}
}, //end buttons
close: function () {
$(this).dialog("close");
}
}); //end modal edit
});
});
});
My Problem. (in EDIT FORM SCRIPT)
I need the DepartmentID to pass to my controller ('Department/Edit/' + departmentid)
My observations
1) var aData = oDepartmentTable.fnGetData(this) always shows "not available" in chrome console.
2) var departmentid = $(this).parent().parent().parent().parent().parent().children()[0].innerHTML gets the correct departmentID if i use "bVisible": true in datatable initialisation.
(3) I dont want to show departmentID to the end user. if i make "bVisible": false in datatable initialisation then
var departmentid = $(this).parent().parent().parent().parent().parent().children()[0].innerHTML returns the departmentName
Thanks
Look at the discussion in the datatables forum here and fnGetPosition
Try this clickhandler instead :
$(document).ready(function () {
$('#DepartmentTable tbody tr').on('click', function (e) {
// get the position of the current data from the node
var aPos = oDepartmentTable.fnGetPosition( this );
// get the data array
var aData = oDepartmentTable.fnGetData( aPos[0] );
// get departmentID for the row
var departmentID = aData[aPos].departmentID;
console.log(departmentID);
// ... do your stuff here, eg
//goto Edit Controller. DepartmentID is required here
//$.get('Department/Edit/' + departmentid , function (data) {
//..
//..
});
});
It works for me. However, not quite as the docs says. Also, I first tried out with datatables 1.9.1 - it didnt work at all. Guess this area of datatables have had some bugs and have been refactored over the versions.

Kendo UI and knockout.js rowTemplate sorting

I have a MVC site where I use Kendo UI and knockout.js to display the pages. One scenario is to get the database information from server through $.getJSON and then display this information on a KendoUI grid.
<div data-bind="kendoGrid:{sortable:true, data:users, rowTemplate:'userRowTemplate'}>
<table>
<thead>
<tr>
<th>Username</th>
<th>First Name</th>
<th>Last Name</th>
</tr>
</thead> </table>
</div>
<script type="text/html">
<tr>
<td data-bind="text: Username"></td>
<td data-bind="text: FirstName"></td>
<td data-bind="text: LastName"></td>
<tr>
</script>
and the javascript :
<script type="text/javascript">
var ViewModel = function () {
var self=this;
self.users=ko.mapping.fromJS([]);
$getJSON("/UserManagementController/GetUsers",function(data){
ko.mapping.fromJS(data,{},self.users);
});
};
$(document).ready(function(){
var newViewModel=new ViewModel();
ko.applyBindings(newViewModel);
});
</script>
I want this data to be sortable on specific columns (the ones specified here are for example's sake), but I haven't been able to achieve this successfully. I have tried the solution from this knockout-kendo plugin issue post, which works well on simple objects, but does not work on observables. So my question is : how to map data from database through MVC controller to observables in knockout and displaying them in a Kendo grid, but still be able to sort them?
Thanks,
Alex Barac
You can do this by creating a JS view model to represent the data coming back from the server, and mapping the data into the view model. Then you can have simple objects that get set by subscribing to the matching observable properties to implement the sort.
Here is an example:
http://jsfiddle.net/R4Jys/1/
HTML:
<div data-bind="kendoGrid: gridOptions(myList)"></div>
<script id="rowTmpl" type="text/html">
<tr>
<td>
<span data-bind="text: firstName" />
</td>
<td>
<span data-bind="text: lastName" />
</td>
<td>
<span data-bind="text: userName" />
</td>
</tr>
</script>
JavaScript:
var gridItem = function () {
var self = this;
self.firstName = ko.observable();
self.firstNameSort;
self.firstName.subscribe(function (value) {
self.firstNameSort = value;
});
self.lastName = ko.observable();
self.lastNameSort;
self.lastName.subscribe(function (value) {
self.lastNameSort = value;
});
self.userName = ko.observable();
self.userNameSort;
self.userName.subscribe(function (value) {
self.userNameSort = value;
});
self.other = ko.observable('test');
return self;
};
var vm = function() {
var self = this;
self.myList = ko.observableArray();
self.test = ko.observable();
self.gridOptions = function (data) {
return {
data: data,
rowTemplate: 'rowTmpl',
useKOTemplates: true,
scrollable: true,
sortable: true,
columns: [
{
field: "firstNameSort",
title: "First Name",
width: 130
},
{
field: "lastNameSort",
title: "Last Name",
filterable: true
},
{
field: "userNameSort",
title: "Username"
}
]
}
};
var data = [{'firstName':'Steve', 'lastName':'Jones', 'userName': 'steve.jones'},
{'firstName':'Janet', 'lastName':'Smith', 'userName': 'janet.smith'},
{'firstName':'April', 'lastName':'Baker', 'userName': 'april.baker'},
{'firstName':'Dave', 'lastName':'Lee', 'userName': 'dave.lee'},
{'firstName':'Jack', 'lastName':'Bennet', 'userName': 'jack.bennet'},
{'firstName':'Chris', 'lastName':'Carter', 'userName': 'chris.carter'}];
self.myList(ko.utils.arrayMap(data, function(item) {
var g = new gridItem();
ko.mapping.fromJS(item, {}, g);
return g;
}));
};
var pageVm = new vm();
ko.applyBindings(pageVm);
As an alternative you can modify the kendo.web.js (version 2013.3.1119) on line 6844 & 6844.
Replace
compare: function(field) {
var selector = this.selector(field);
return function (a, b) {
a = selector(a);
b = selector(b);
With
compare: function(field) {
var selector = this.selector(field);
return function (a, b) {
a = ko.utils.unwrapObservable(selector(a));
b = ko.utils.unwrapObservable(selector(b));
By using the knockout utility "ko.utils.unwrapObservable" you can get the value of the observable for use when kendo compares the values of the column
DEFINE CUSTOM COMPARE FUNCTION will serves solution, where you can override the comparing function in field definition.
So you can do something like this:
$("#grid").kendoGrid({
dataSource: dataSource,
sortable: true,
columns: [{
field: "item",
sortable: {
compare: function(a, b) {
var valueA = a["item"];
var valueB = b["item"];
if (typeof valueA === "function") valueA = valueA();
if (typeof valueB === "function") valueB = valueB();
if (this.isNumeric(valueA) && this.isNumeric(valueB))
{
valueA = parseFloat(valueA);
valueB = parseFloat(valueB);
}
if (valueA && valueA.getTime && valueB && valueB.getTime)
{
valueA = valueA.getTime();
valueB = valueB.getTime();
}
if (valueA === valueB)
{
return a.__position - b.__position;
}
if (valueA == null)
{
return -1;
}
if (valueB == null)
{
return 1;
}
if (valueA.localeCompare)
{
return valueA.localeCompare(valueB);
}
return valueA > valueB ? 1 : -1;
}
}
}]
});
private isNumeric(input)
{
return (input - 0) == input && ('' + input).trim().length > 0;
}
The compare method is stolen from kendo script, but change is where the property is typeof function (what ko.observable is), it unwraps the value. Plus, I added support for numbers.

Knockoutjs Custom bindig with jQueryUI Dialog

I'm trying to make a custom binding based on this code, http://jsfiddle.net/rniemeyer/WpnTU/ , Mine after a field checkbox is selected then open a jQueryUI dialog. Here is the code: http://jsfiddle.net/superjohn_2006/UFEg6/ , another question is it posible to acomplish this without a template.
<table>
<tbody data-bind="foreach: records">
<tr data-bind="foreach: fields">
<th align="left">
<input type="checkbox" data-bind="checked: chkedValue" /><span data-bind=" text: field"></span>
</th>
</tr>
<tr data-bind="foreach: fields">
<th align="left"><a data-bind="click: $root.addFormatting" href="#">Add Formatting</a></th>
</tr>
<tr data-bind="foreach: row">
<td data-bind="text: value"></td>
</tr>
</tbody>
</table>
<div id="details" data-bind="jqDialog: { autoOpen: false, resizable: false, modal: true }, template: { name: 'editTmpl', data: selectedField }, openDialog: selectedField">
</div>
<script id="editTmpl" type="text/html">
<p>
<label>Selected Field: </label>
<span data-bind="value: field" />
</p>
<button data-bind="jqButton: {}, click: $root.accept">Accept</button>
<button data-bind="jqButton: {}, click: $root.cancel">Cancel</button>
</script>
**The model
// custom binding
ko.bindingHandlers.jqDialog = {
init: function(element, valueAccessor) {
var options = ko.utils.unwrapObservable(valueAccessor()) || {}; // initialize a jQuery UI dialog
$(element).dialog(options);
// handle disposal
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
$(element).dialog("destroy");
});
}
};
//custom binding handler that opens/closes the dialog
ko.bindingHandlers.openDialog = {
update: function(element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
if (value) {
$(element).dialog("open");
} else {
$(element).dialog("close");
}
}
};
//custom binding to initialize a jQuery UI button
ko.bindingHandlers.jqButton = {
init: function(element, valueAccessor) {
var options = ko.utils.unwrapObservable(valueAccessor()) || {};
//handle disposal
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
$(element).button("destroy");
});
$(element).button(options);
}
};
var resultsData = [
{ fields: [{ field: "Field1", chkedValue: false }, { field: "Field2", chkedValue: false }] },
{ row: [{ value: "1" }, { value: "True" }] },
{ row: [{ value: "2" }, { value: "False" }] }
];
var TableModel = function (records) {
var self = this;
self.records = ko.observableArray(ko.utils.arrayMap(records, function (record) {
return { fields: ko.observableArray(record.fields), row: ko.observableArray(record.row) };
}));
self.selectedField = ko.observable();
self.addFormatting = function (formatToAdd) {
self.selectedField();
};
};
this.accept = function() {
},
this.cancel = function() {
}
ko.applyBindings(new TableModel(resultsData));
the following couple of lines need to be changed.
span data-bind="value: field"
for:
span data-bind="text: $data.field"
and,
self.selectedField();
for:
self.selectedField(formatToAdd);
modified code is in the same jsFiddle, jus add: /1/
to the end of the url address.

Resources