I have a buffered grid on which I've implemented a local sort function (client side). I would like to remove the sort indication (darker background and little arrow) on the column header when the store reloads.
Does anyone know how to accomplish this in 4.1?
To make this clearer:
I want my columns to be sortable. I do not want them to initialize with sorting disabled. Users should be able to click the header and sort it all they want. But, what I want is to be able to turn off the sort programmatically. I.e., remove any sort classes that were applied from user clicks (things like the darker background and the little sort direction arrow).
The reason I would do this is because I am using a modified buffered store which allows me to do local sorting (client-side) with all of the buffered data (not just the chunk that is displayed). Normally, using a buffered store will make local sorting disabled because it would only sort the data that is displayed in the grid - not all of the data in memory, so the guys at Sencha made any grid that has a buffered store automatically disable local sorting - only remote sorting works. Well, as I said, mine is modified so it will work - but then when this buffered store reloads with new data from the database it does not enjoy the handy sortOnLoad feature normal grids get as a matter of course. In my use-case it is more logical to remove the sort state than it is to override the sortOnLoading functionality and make it apply the same sort to the new data, hence, this question.
I do have it worked out now, I'll post an answer shortly along with my implementation of a buffered store with local sorting, in case anyone is interested and for my own future reference.
I should also point out that I am very aware of the client-side performance penalties that come with local sorting on a store that would need to be buffered (as opposed to just using remote sorting of the data on the server). I am aware that this is probably why Sencha does not support local sorting on their buffered stores. I have assessed all of the pros and cons to this and in my specific use-case it is the most sensible thing for me to do.
Buried deep in the dom there is a setSortState function on the Ext.grid.header.Container class and on the Ext.grid.column.Column class. These don't show up anywhere in the 4.1.0 docs but they're in the code nevertheless.
You can look at these functions yourself to get a complete concept of what they do, but the gist of both them is a switch statement that looks for either a 'DESC', 'ASC' or a null in the first argument, e.g.:
setSortState(`DESC`);
setSortState(`ASC`);
setSortState(null);
Calling either the header version or the column version of this function with a single null argument will remove the sort classes on a column. The only real difference is that the header version looks at the grid's store to find the active sorter in the store's sorters property and then uses that data to determine which column to call this function on - the column version simply runs off the column object that it is called from.
In my use-case I don't add a sorter to the store sorters property so I am using the column version (i.e. calling setSortState from an Ext.grid.column.Column object).
First, here is an example of my implementation of a buffered store that allows local (client-side) sorting:
Ext.define('MyApp.store.TheBufferedStoreWithLocalSorting', {
extend: 'Ext.data.Store',
requires: [
'Ext.ux.data.PagingMemoryProxy',
'Ext.util.MixedCollection'
],
model: 'MyApp.model.SomeModel',
buffered: true,
pageSize: 100,
remoteSort: true, // this just keeps sorting from being disabled
proxy: {
type: 'pagingmemory',
reader: 'json'
},
/*
* Custom sort function that overrides the normal store sort function.
* Basically this pulls all the buffered data into a MixedCollection
* and applies the sort to that, then it puts the SORTED data back
* into the buffered store.
*/
sort: function(sorters) {
var collection = new Ext.util.MixedCollection();
collection.addAll(this.proxy.data);
collection.sort(sorters);
this.pageMap.clear();
this.getProxy().data = collection.getRange();
this.load();
}
});
Now, to answer my question, to remove the sorter classes whenever the store reloads I just need to do this:
Ext.each(myGrid.columns, function(column, index) {
if (column.hasCls('x-column-header-sort-ASC') ||
column.hasCls('x-column-header-sort-DESC')) {
myGrid.columns[index].setSortState(null);
return false;
}
});
When the store is reloaded you could add the following to the store's load event handler:
Ext.create("Ext.data.Store", {
listeners: {
load: {
fn: function () {
grid.addCls("no-sort-icon");
}
}
}
};
Then modify your css to hide the icon when element is child of "no-sort-icon" (this would be on the grid)
.no-sort-icon .x-column-header-text {
background-image: none;
}
.no-sort-icon .x-column-header {
background-color: #C5C5C5;
}
If I understand you correctly, setting this config on the specified column should solve your problem
columns: [
{text: 'First Name', dataIndex:'firstname', sortable: false},
{text: 'Last Name', dataIndex:'lastname'},
]
more details here http://docs.sencha.com/ext-js/4-0/#!/api/Ext.grid.column.Column-cfg-sortable
This is a configuration option, so it would disable the sortability of a column when the grid is first rendered.
Here is a jsfiddle demo
* Note that I use Ext. 4.0.7, you can switch to 4.1.0, but for some reason an unrelated display bug with the dropdown pops out
Related
I am trying to clear the sort applied on a table from the code by using the active and direction fields but with no success:
#ViewChild(MatSort) sort: MatSort;
private clearSort() {
// Reset the sort column
this.sort.active = "";
// Reset the sort direction
this.sort.direction = "";
}
I looked into the Sort Header Documentation but I didn't find any native method that helps to clear an applied sort on a given table.
Any advice would be appreciated.
You can set a default sort option and use sort function on the MatSort api to set that default option.
defaultSort: MatSortable = {
id: 'defColumnName',
start: 'asc',
disableClear: true
};
then use sort function on MatSort directive:
this.sort.sort(this.defaultSort);
//default sort direction
this.sort.direction = 'asc';
Beware that the above code will trigger the sortChange event subscription.
In general, the way to clear a sort is to re-apply the original sort order.
If the original sort order does not have a natural key, you may need to introduce an original order field in your data so you can sort by that field to "clear" any sorting that has occurred since the data loaded.
I haven't used MatSort, but in the absence of someone confirming otherwise, I would assume that to clear the sorting, you need to apply sorting as described above.
If the library supplied a reset, it would have to maintain the data in the original order in memory somewhere, which is why most libraries don't do this.
The manualRowMove works fine visually but the underlying dom is not getting updated. After manualRowMove the HOT.getData() is showing the old sequence of data and not the latest sequence after row swap.
Please advise what I am doing wrong or is it a bug.
Turning on manualRowMove and moving rows around does not affect the original data source. HOT.getData() returns the original data source, which is not what the current state of the table is.
Plugins like manualRowMove, manualColMove, columnSorting are just abstractions that sit on top of the data layer. They maintain state on the handsontable object (ie the sortIndex attribute) which basically act as lookup tables from what the authors call "logical index" to "physical index". Read more about it here: https://github.com/handsontable/handsontable/wiki/Understanding-column-sorting-plugin
It seems like you are trying to allow users to sort the order of the table then save that order. You'll have to roll your own func to walk the table and use a method like getDataAtRow() to get data in table order.
the way I fixed this was to use a PHP script to push the new order to the database on the server (there needs to be some < / > swap logic, it isn't that easy in PHP land, but it is doable), in theory you could recollect the grid and "refresh" it but I didn't feel it was necessary. Obviously the original json from my database is listed by the order
afterRowMove: function (oldIndex, newIndex){
$.post('../ajax/partsOrder.php?id=212&new_parts_order=' + newIndex + '&old_parts_order=' + oldIndex, function(data) {
var msg = '';
if (data.err!=0 || data.url==0) {
msg = 'Error: ' + data.err;
}
else {
msg = 'Success: ' + data.msg;
}
console.log('partsOrder.php says: '+msg);
},"json");
}
PS: I think this is a major omission from handontable, I was not impressed that the getData() doesn't simply represent the gui. In fact it's borderline criminal that they even worked on the gui at all without this
EDIT: Final solution is below.
Whether I try to implement column re-ordering via header dragging or via the column chooser plugin, after re-ordering the columns, clicking on any column header to sort results in the sorted columns being loaded into their original positions in the table. Using the sortable method:
sortable: {
update: function (perm) {
/*
* code to save the new colmodel goes here
*/
// the following line doesn't seem to do anything... just seems to return an array identical to 'perm'
$("#mainGrid").jqGrid("getGridParam", "remapColumns");
// if included, the next line causes the headers to not move
$("#mainGrid").jqGrid("remapColumns", perm, true);
// this alternate allows them to move, but the newly sorted columns still get remapped to their original position
$("#mainGrid").jqGrid("remapColumns", [0,1,2,3,4,5,6,7,8,9,10,11,12], true);
/* the following allows the headers to move, and allows the sort to occur ONLY
* if the order coming back from the database is unchanged. Note that in my real
* code I create an array of consecutive integers to pass as the first param to
* remapColumns()
*/
$("#mainGrid").jqGrid("remapColumns", [0,1,2,3,4,5,6,7,8,9,10,11,12], true, false);
}
}
When the page is reached for the first time, it creates a default column model from an xml file. When the user re-orders the headers, the new column model and column names are stored in the database as JSON strings. When the user makes another database call, the function reads the new column order from the database and creates the data array with the new ordering.
The problem seems to be that after jqGrid has remapped the columns, it still expects to see the data coming back from the server in the original order. So if the original data was
[ [A1, B1, C1], [A2, B2, C2], [A3, B3, C3] ]
after remapping the columns to the order C | A | B, jqGrid still wants the data to came back in the original order.
My final solution was to remove the code that saves the column model state from the sortable.update() function and to put it into window.onbeforeunload(). This way, the state is only saved when the user exits the page.
Hope this helps someone else.
See edited question. Without a method to update the colModel, the best solution seems to put the state save function into window.onbeforeunload().
I am using the FullCalendar jQuery plugin: http://arshaw.com/fullcalendar/
I am also using the example where you can drag external events onto the calendar: http://arshaw.com/js/fullcalendar-1.5.2/demos/external-dragging.html
Right now, I have an event click function as follows:
eventClick: function(event) {
$.ajax({
type: "POST",
url: "ajax-schedule.php",
data: 'id=' + event.id + '&start=' + event.start + '&end=' + event.end,
success: function(data){
alert('done!');
}
});
}
This posts to a file "ajax-schedule.php" where the data is inserted into the mysql database.
I would like to create a link that when clicked will take all of the new/changed events and post the data as shown above, instead of one-by-one.
Something like:
Update Schedule
The "updateSchedule" function would then post all the data.
Looks like the solution may involve the "clientEvents" method: http://arshaw.com/fullcalendar/docs/event_data/clientEvents/
... but I'm sort of lost here.
Any ideas as to how to do this?
You can create an array to store all the events:
var arrayOfEvents = [];
$('#calendar').fullCalendar({
...
drop: function(date) {
...
// retrieve the dropped element's stored Event Object
var originalEventObject = $(this).data('eventObject');
// we need to copy it, so that multiple events don't have a reference to the same object
var copiedEventObject = $.extend({}, originalEventObject);
// Push the event into the array
arrayOfEvents.push(copiedEventObject);
...
},
...
)};
function updateSchedule()
{
var data = "numOfEvents=" + arrayOfEvents.length;
// You can get all events out of the array here
for (var i = 0 ; i < arrayOfEvents.length ; i++) {
var event = arrayOfEvents[i];
data += "&id" + i + "=" + event.id
+ "&start" + i + "=" + event.start
+ "&end" + i + "=" + event.end;
}
// Make your ajax post here
$.ajax({
type: "POST",
url: "ajax-schedule.php",
data: data,
success: function(response){
alert('done!');
}
});
}
So on server-side, your code can get "numOfEvents" and just run a for loop from 0 to numOfEvents to get all events out.
This is something I am planning to implement in future when I get to tweaking performance of my project. My general idea would be something like this:
create handler that stores every change made in fullcalendar, so new events, events updates (drag&drop, resize, title/description/color/whatever is needed), event deletions. I guess the best way would be to create "class" that will be part of fullcalendar itself, tweakable by options of cource, if it wont be part of fc you would need to call it in every state changing function.
This handler stores array of events as well as it provides some basic methods to add fc changes into queue. My idea is to make it event-based, so in array there is member defined by event id which has information about every update made on this event. The way I imagine this to work is not exactly like every event has its own array of updates which server will run sequentially. I think of making this in way, that handler will be set to be saving data every 15/30... seconds (will be set by user) or on user call (pressing button for example). In time between two saves, queue will be populated in way where all updates will be merged into one global change (for example, if you move one event in calendar 5 times, resize 3 times and change title 5 times but at the end its gonna be the very same as it was at the last save, there will be nothing send to server for saving because in reality, no change was made. Or if you do the same and then delete event, its senseless to save all changes and then delete event, instead of that handler will send only delete command as this command is not affected by any other previously done. But if you move event for example two days in future and then 1 day back, it will calculate it was actually moved only by one day forward so there wont be any unnecessary data posted to server).
Eventhought it would be the best to implement it directly to fullCalendar plugin, it also can be standalone class/plugin which could be associated with any kind of application which makes a lot of changes on some set of datas and requires communication to be highly efficient to maximize speed (so user wont be bothered by slow updates/saves). It can be tweaked even more by recognizing exactly which fields (I use fc basically as google calendar, I can change color, desription, title and many more fields, but It would be useless to send whole event as it is if for example only title is changed, no need to send fields that remained the same) in event had been updated and send only those, so there will absolutely no redundant data sent to server. I guess I would do this as every event would have its member in queue array (as I said before) and when new member for event is added to queue, it will store also current event data (which are for comparison only, wont be send to server) for further comparison with next updates (if there will be any).
Hope you didnt get lost and catch my drift. This is just my idea for usefull feature, but I dont see myself working on it this year, depends on school/job. Its not that hard to make it actually, at least not in way I imagine it to be, so there may be someone else who will do it before I even start :)
I have a paged GridPanel and don't know how to go about adding server side sorting. Do I add a listener for the sortChange() event? Do I override it? Or is there another event that I should work with.
Any help would be appreciated.
No. In the definition for your store just set
remoteSort: true, // to enable sorting
sortInfo: { // the default sort
field: 'someField',
direction: 'ASC' | 'DESC'
}
And on the server side you will now be getting a sort and dir request parameters specifying the sort field and direction. When the columns are clicked the store will update sorted by the column and direction you pick.