I use dgrid (http://dgrid.io/index.php) in my application.
It has sorting implemented by default, but I would like to sort special characters such as Ą, Ę in oreder they are supposed to be in. By that i mean: A, Ą, B, C, Ć, ...
Right now I have rows starting with special characters at the end of the grid.
Any idea how to repair it?
With help from link provided by GibboK i managed to write custom sorting in dgrid.
It works like that:
var grid = new (declare([Grid]))({
store: new Memory({ data: data, idProperty: 'id' }),
columns: columns,
}, 'domID');
grid.on('dgrid-sort', function (event) {
event.preventDefault();
grid.set('sort', function (a, b) {
*sorting logic*
grid.updateSortArrow(event.sort, true);
})
Related
Our tables pagination and sorting are all done on the server, but I still want to allow the user to click column headers to sort. I need to access the tables sort options when the sorting changes. It seems the events are split
update:sort-by
update:sort-desc
I could create two methods, have the first event set the column (sort-by) value, and then the second method actually trigger the sort. But that sounds horrible and prone to race conditions or future bugs.
It would be better if the sort-by included the desc/asc data as well. I tried creating a ref to the table, but for some reason the properties containing the sort information are empty.
The event update:options contains all the necessary information, but that could fire for reasons other than sorting which isn't ideal either.
So I'm not sure if I'm missing something here. Is there a better way to accomplish this?
<v-data-table
ref="contractItemTable"
:headers="headers"
:items="contracts"
:disable-sort="isLoadingPage"
:server-items-length="tableTotal"
disable-pagination
hide-default-footer
#click:row="navigateToContract"
single-select
#update:sort-by="sortTable"
#update:sort-desc="sortTable"
item-key="id.id">
And the JS
public sortTable(event) {
console.debug(this.$refs.contractItemTable.sortBy);
console.debug(this.$refs.contractItemTable.sortDesc);
}
No matter what I click, the sortBy and sortDesc properties are empty. If I use the options event, the event contains the correct sortBy and sortDesc. But as stated above, not exactly the event I want to use. I have it working, but I have to "ignore" the initial load options event since it's not a valid time for this method to fire.
In data table add attribute:
:sort-desc.sync="sort_desc"
In data:
sort_desc:true,//or what you need by default
In observable:
sort_desc:function(val,_prev){
//do what you need to change sort and refresh
},
You're mostly there (and found your question while trying to sort out the best way to apply sorting to derived/computed column-items myself) ... just need to bind the v-data-table sort-by and sort-desc attributes to data elements so that you can refer to them in your sortTable function:
<v-data-table
...
:items="contracts"
:headers="headers"
:sort-by.sync="sortBy"
:sort-desc.sync="sortDesc"
#update:sort-by="sortTable"
#update:sort-desc="sortTable"
...
>
export default {
data() {
contracts: [],
sortBy: 'contractDate', // make sure this matches headers[].value
sortDesc: false, // and both match your initial from-server sort
...
headers: [
{ text: 'Contract Date', sortable: true, value: 'contractDate' },
{ text: 'Contract Value', sortable: true, value: 'contractValue' }
...
]
},
methods: {
sortTable() {
if(this.sortBy === 'contractDate') {
this.contracts.sort( (a,b) => {
return ( a.contractDate > b.contractDate ? 1 : -1 ) * (this.sortDesc ? -1 : 1 );
});
}
if(this.sortBy === 'contractValue') {
this.contracts.sort( (a,b) => {
return ( a.contractValue > b.contractValue ? 1 : -1 ) * (this.sortDesc ? -1 : 1 );
});
}
}
}
}
Your sort logic obviously will vary, but included the Array.sort() callback example there to show where this.sortDesc is used to reverse the evaluation.
A big caveat to be aware of is that if you do not specify
<v-data-table :must-sort="true" >
Then the bound #update:sort-by and #update:sort-desc events will BOTH fire when sort is deactivated which happens when user clicks the same column header a third time (it cycles through sorted to no-sort states.)
To get around this, add something like
sortPending: false
to your data state, and when the event fires, return immediately out of the event if this.sortPending === true, and move your sort logic into a Vue.nextTick() callback:
methods: {
sortTable() {
if(this.sortPending) return;
this.sortPending = true;
this.$nextTick( () => {
this.sortPending = false;
/* your sort logic here */
});
I have implemented a grid with server side paging, sorting, filtering. Now my user has saved that grid with all the sorting, filtering information. I have generated a query with all these information. That means, when I load that grid again, I am giving the filtered and sorted data to the grid. Even though I have given the sorted, filtered data, now the events hits the DB since I am applying the filtering and sorting properties as below. In this situation, If I have sorted 7 fields in the grid, it hits DB 8 times (7 for applying filter and one for loading the grid for the first time). This makes so many performance issues.
$(openFrom + "#jqxgrid").jqxGrid('sortby', sessionStorage.getItem("GridSortColumn"), sortdirectionvalue);
$(openFrom + "#jqxgrid").jqxGrid('addfilter', currentColumnFilterValue['columnname'], filterGroup);
$(openFrom + "#jqxgrid").jqxGrid('applyfilters');
Here is my source object.
var source =
{
datafields: DataFields,
datatype: "json",
async: false,//If it is true, server side filtering and sorting won't work together
url: '../Widget/GetDataForGrid/',
type: 'POST',
sort: function () {
},
filter: function () {
},
beforeprocessing: function (data) {
}
};
So my requirement is, I just need to show the filter, sort selection in the grid without going to DB. This is applicable only for the first time(When the grid with sorted, filtered information loads first). And when the user click the filter again or user tries to sort another field, it should work as in server side filtering and sorting.
Any help is much appreciated.
I have solved it myself.
I created a variable and initiated it in the document ready as follows.
var isFilterSortGrid = false;
Change sort filter functions in source object as follows.
sort: function () {
if (isFilterSortGrid) {
$("#jqxgrid").jqxGrid('updatebounddata', 'sort');
}
},
filter: function () {
if (isFilterSortGrid) {
$("#jqxgrid").jqxGrid('updatebounddata', 'filter');
}
}
And finally in filter and clear button click I made that variable true again.
$(document).on('click', '#filterbuttonjqxgrid', function () {
isFilterSortGrid = true;
});
$(document).on('click', '#filterclearbuttonjqxgrid', function () {
isFilterSortGrid = true;
});
I am applying the filter and sorting as normal, so that the filter selection will be there.
I've got a few Kendo AutoComplete fields linked to remote data (hundreds of possibilities, so DropDownList is not an option).
How can I force users to make a selection from the displayed list?
I'm also retrieving the additional data that's returned from the data source, e.g.
$("#station").kendoAutoComplete({
dataSource: stationData,
minLength: 2,
dataTextField: 'name',
select: function(e){
var dataItem = this.dataItem(e.item.index());
console.dir(dataItem);
}
});
I'm doing additional stuff with the data in dataItem and it needs to be a valid selection.
Thanks
SOLVED:
I think I was possibly over-complicating things. The answer is pretty simple and is posted below.
var valid;
$("#staton").kendoAutoComplete({
minLength: 2,
dataTextField: "name",
open: function(e) {
valid = false;
},
select: function(e){
valid = true;
},
close: function(e){
// if no valid selection - clear input
if (!valid) this.value('');
},
dataSource: datasource
});
This method allows users to type whatever they like into the AutoComplete if list not opens. There are two corrections to fix this:
initialize variable valid as false:
var valid = false;
Check if no valid selection in change event, but not in close:
...
change: function(e){ if (!valid) this.value(''); }
The answer the OP posted, in addition to the answer suggested by #Rock'n'muse are definitely good propositions, but both miss an important and desired functional aspect.
When utilizing the solution given by #Mat and implementing the change-vice-close suggestion from #Rock'n'muse, the typed-in value indeed clears from the widget if no selection is made from the filtered data source. This is great; however, if the user types in something valid and selects a value from the filtered list, then places the cursor at the end of the value and types something which now invalidates the value (doesn't return any valid selections from the data source), the typed-in value is not cleared from the widget.
What's happening is that the isValid value remains true if the previously typed-in (and valid) value should be altered. The solution to this is to set isValid to false as soon as the filtering event is triggered. When the user changes the typed-in value, the widget attempts to filter the data source in search of the typed-in value. Setting isValid to false as soon as the filter event is triggered ensures a "clean slate" for the change event as suggested by the solution from #Rock'n'muse.
Because we're setting isValid to false as soon as the filtering event is triggered, we do not need to do so in the open event (as data source filtering must occur before the user will ever see an option to select). Because of this, the open event binding was removed from #Mat's solution. This also means the initial assignment of false upon declaration of isValid is superfluous, but variable assignment upon declaration is always a good idea.
Below is the solution from #Mat along with the suggestions from #Rock'n'muse and with the filtering implementation applied:
var isValid = false;
$("#staton").kendoAutoComplete({
minLength: 2,
dataTextField: "name",
select: function () {
valid = true;
},
change: function (e) {
// if no valid selection - clear input
if (!valid) {
e.sender.value("");
}
},
filtering: function () {
valid = false;
},
dataSource: datasource
});
As an addendum, using the select event binding to set and evaluate a simple boolean value as #Mat proposes is much cleaner, simpler and faster than using a jQuery $.each(...) on the data source in order to make sure the typed-in value matches an actual item of the data source within the change event. This was my first thought at working a solution before I found the solution from #Mat (this page) and such is my reasoning for up-voting his solution and his question.
Perhaps, can you make your own validation by using the blur event :
$("#station").blur(function() {
var data = stationData,
nbData = data.length,
found = false;
for(var iData = 0; iData < nbData; iData++) {
if(this.value === data[iData].yourfieldname) // replace "yourfieldname" by the corresponding one if needed
found = true;
}
console.log(found);
});
You can check this fiddle.
You most likely need some custom logic which to intercept each key stroke after the min length of two symbols is surpassed, and prevent the option to enter a character which makes the user string not match any of the items from the autocomplete list.
For this purpose intercept the change event of the Kendo autocomplete and compare the current input value from the user against the items from the filtered list.
Hope this helps,
$("#autocomplete_id").val("");
$("#autocomplete").kendoAutoComplete({
dataSource: datasource,
minLength: 1,
dataTextField: "catname",
dataValueField:"id",
select: function(e) {
var dataItem = this.dataItem(e.item.index());
$("#autocomplete_id").val(dataItem.id);
},
dataBound: function(e){
$("#autocomplete_id").val("");
}
});
FYI, autocomplete_id is a hidden field to store the value of the autocomplete. - Sometimes, we would like to have the dataValueField other than dataTextField. So, it serves its purpose.
In this you can get the value of the autocomplete "id" from the element autocomplete_id - which is dataValueField from serverside.
In databound, its values is set to null, and on select, it is assigned the "id" value.
Although the accepted answer works, it is not the best solution.
The solution provided doesn't take into account if the user enters a value before even the kendo AutoComplete widget triggers the open event. In result, the value entered is not forced and therefore, the entry/selection is invalid.
My approach assumes that the application is running in MVC and the array is passed in ViewData. But this can be changed to suit your environment.
My approach:
var validSelect, isSelected;
$("#staton").kendoAutoComplete({
minLength: 2,
filter: "startswith",
dataTextField: "name",
filtering: function(e) {
validSelect = false;
dataArr = #Html.Raw(Json.Encode(ViewData["allStatons"]));
// for loop within the ViewData array to find for matching ID
for (var i = 0; i < dataArr .length; i++){
if (dataArr[i].ID.toString().match("^" + $("#staton").val())) {
validSelect = true;
break;
}
}
// if value entered was not found in array - clear input
if (!validSelect) $("#staton").val("");
},
select: function(e){
isSelected = true;
},
close: function(e){
// if selection is invalid or not selected from the list - clear input
if (!validSelect || !isSelected) $("#staton").val("");
},
dataSource: datasource
});
As you can see, this approach requires the array from the server side on load so that it can match during the filtering event of the widget.
I found this on Telerik's site by just using the change event. For me this works best. I added the "value === ''" check. This will catch when the user "clears" the selection.
Here's the link to the full article.
$("#countries").kendoAutoComplete({
dataSource: data,
filter: "startswith",
placeholder: "Select country...",
change: function() {
var value = this.value();
if (value === '') return;
var found = false;
var data = this.dataSource.view();
for(var idx = 0, length = data.length; idx < length; idx++) {
if (data[idx] === value) {
found = true;
break;
}
}
if (!found) {
this.value("");
alert("Custom values are not allowed");
}
}
});
In my AutoComplete Change event I check for a selected item and clear the cell if not selected.
function myAutoComplete_OnChange(e)
{
if (this.dataItem())
{
// Don't use filtered value as display, instead use this value.
e.sender.element[0].value = this.dataItem().WidgetNumber;
}
else
{
var grid = $("#grid").data("kendoGrid");
grid.dataItems()[grid.select().index()].WidgetKey = null;
grid.dataItems()[grid.select().index()].WidgetNumber = null;
}
}
The Story So far....
Trying to learn JS and JQuery and i thought i would start with the basics and try alittle AJAX "search as you type" magic. Firstly i just wanted to get the AJAX part right and iterating through the return JSON object and appending it to a unordered list. Im doing no validation on the inputted value vs. the returned JSON results at this time, i just want a controlled way of when to do the AJAX getJSON call. Later i will do the validation once i get this right.
Anyways im having trouble displaying the Account Numbers in in the ul. At the moment the only thing that is being displayed is AccountNumber in the li and not my ACCOUNT NUMBERS
My JS Code is here:
http://jsfiddle.net/garfbradaz/HBYvq/54/
but for ease its here as well:
$(document).ready(function() {
$("#livesearchinput").keydown(function(key) {
$.ajaxSetup({
cache: false
});
$.getJSON(" /gh/get/response.json//garfbradaz/MvcLiveSearch/tree/master/JSFiddleAjaxReponses/", function(JSONData) {
$('<ul>').attr({
id: "live-list"
}).appendTo('div#livesearchesults');
$.each(JSONData, function(i, item) {
var li = $('<li>').append(i).appendTo('ul#live-list');
//debugger;
});
});
});
});
My JSON file is hosted on github, but again for ease, here it is:
https://github.com/garfbradaz/MvcLiveSearch/blob/master/JSFiddleAjaxReponses/demo.response.json
{
"AccountNumber": [
1000014,
1015454,
1000013,
1000012,
12
]
}
Also here is my Fiddler results proving my JSON object is being returned.
EDIT:
There were so queries about what i was trying to achieve, so here it goes:
Learn JQuery
To build a "Search as you Type" input box. Firstly i wanted to get the AJAX part right first, then i was going to build an MVC3 (ASP.NET) Application that utilises this functionality, plus tidy up the JQuery code which includes validation for the input vs. the returned JSON.
Cheesos answer below worked for me and the JSFiddle can be found here:
http://jsfiddle.net/garfbradaz/JYdTU/
First, I think keydown is probably the wrong time to do the json call, or at least... it's wrong to do a json call with every keydown. That's too many calls. If I type "hello" in the input box, within about .8 seconds, then there are 5 json requests and responses.
But you could make it so that it retrieves the json only the first time through, using a flag.
Something like this:
$(document).ready(function() {
var $input = $("#livesearchinput"), filled = false;
$.ajaxSetup({ cache: false });
$input.keydown(function(key) {
if (!filled) {
filled = true;
$.getJSON("json101.js", function(JSONData) {
var $ul =
$('<ul>')
.attr({id: "live-list"})
.appendTo('div#livesearchesults');
$.each(JSONData, function(i, item) {
$.each(item, function(j, val) {
$('<li>').append(val).appendTo($ul);
});
});
});
}
});
});
The key thing there is I've used an inner $.each().
The outer $.each() is probably unnecessary. The JSON you receive has exactly one element in the object - "AccountNumber", which is an array. So you don't need to iterate over all the items in the object.
That might look like this:
$.each(JSONData.AccountNumber, function(i, item) {
$('<li>').append(item).appendTo($ul);
});
What you probably want is this:
$.each(JSONData.AccountNumber, function(i, item) {
var li = $('<li>').append(item).appendTo('ul#live-list');
});
Your code:
$.each(JSONData, function(i, item) {
var li = $('<li>').append(i).appendTo('ul#live-list');
});
Says "iterate over the keys and values of the outer JSON structure, and print the keys". The first key is "AccountNumber", so you end up printing that.
What you want to do is iterate over the array stored at JSONData.AccountNumber, and print the values:
$.each(JSONData.AccountNumber, function() {
var li = $('<li>').append(this).appendTo('ul#live-list');
});
I have 3 different tabs where i am displaying data using jQGrids(each tab contain one grid).
But i just thought that my grids are completely the same, only the difference is that they using different url to get data.
So I have three similar girds on each tab only with different urls:
First: url: '/Home/GetData?id=1' Second: url: '/Home/GetData?id=2' and Third: url: '/Home/GetData?id=3'
So i was thinking that may be i may declare grid only once and than on each tab click a can pass the url to load data? So on each tab click jQGrid will be populating from the new url.
May be some one may have any ideas about that?
Or may be some one may have better ideas how to reduce "jQGrid copy-paste" in that case?
UPDATE 0:
Nearly get it work i mean it is working but there is one small problem,
When i am switching tabs the header of the grid getting lost...and some jqgrid formatting as well.
here is my code:
$("#tabs").tabs({
show: function (event, ui) {
if (ui.index == 0) {
//$("#Grid1").appendTo("#tab1");
//$("#Grid1Pager").appendTo("#tab1");
//When Appending only pager and grid div, header getting lost so i've append the whole grid html instead
$("#gbox_Grid1").appendTo("#tab1");
changeGrid("#Grid1", 1);
}
else if (ui.index == 1) {
//$("#Grid1").appendTo("#tab2");
//$("#Grid1Pager").appendTo("#tab2");
$("#gbox_Grid1").appendTo("#tab2");
changeGrid("#Grid1", 2);
}
else if (ui.index == 2) {
//$("#Grid1").appendTo("#tab3");
//$("#Grid1Pager").appendTo("#tab3");
$("#gbox_Grid1").appendTo("#tab3");
changeGrid("#Grid1", 3);
}
}
});
function changeGrid(grid, id) {
$(grid).jqGrid('setGridParam', {
url: '/Home/GetData?id=' + id
});
$(grid).trigger('reloadGrid');
}
UPDATE 1
All right, i've changed the code to append the whole grid instead of appending grid div and pager only. So it is working like that.
You can basically make the tabs as regular buttons that will call some function which sets new URL parameter to the grid and reloads it.
The function should be something like this:
function changeGrid(grid, id) {
$(grid).jqGrid('setGridParam', {
url: '/Home/GetData?id=' + id, page: 1
});
$(grid).trigger('reloadGrid');
}
Note that I set the page to 1. Keep in mind that you might need to set the default sorting column or something similar depending on your solution.
UPDATE
If you really want to go with the tabs, the 'show' event handler can be simplified.
Try this:
$("#tabs").tabs({
show: function (event, ui) {
var id = ui.index + 1;
$("#gbox_Grid1").appendTo("#tab" + id);
$("#Grid1").jqGrid('setGridParam', {
url: '/Home/GetData?id=' + id
});
$("#Grid1").trigger('reloadGrid');
}
});