In our application we are using a jqGrid which supports hiding and reordering of columns. When the columns are hidden or reordered we want to store the new settings into our database. But to do this we somehow need to capture the hiding or reordering event. Or possibly to capture when the colModel changes.
Is there any way to capture and handle these events?
Thanks.
You can use 'done' event of the columnChooser. Here is an example:
var grid = $("list");
grid.navButtonAdd(
'#pager',
{caption:"", buttonicon:"ui-icon-calculator", title:"Column choose",
onClickButton: function() {
grid.jqGrid('columnChooser',
{
"done": function(perm) {
if (perm) {
this.jqGrid("remapColumns", perm, true);
}
// here you can do some additional actions
}
});
}
});
UPDATED: If you define sortable option as
sortable: {
update: function (permutation) {
alert("sortable.update");
}
}
and not as sortable:true you will receive the notification about the new order of columns. See the source code of jqGrid for details. The array permutation with integers has the same meaning as in remapColumns functions (see my old answer for details).
You can capture column changes via the sortable parameter as mentinoed in Oleg's "update" above, or as discussed on jqGrid's message board.
However, please note that the array passed to your callback will be relative to the current column order. In other words, saving the array as is after moving multiple columns will not produce the desired results. See my answer on this other stackoverflow question.
Related
I'm currently editing an application which uses YUI 2.5. I haven't used it before and could use some help.
I want to be able to add a dropdown editor for a particular column's rows using datatable, but I only want it to appear if specific values appear in another column in the corresponding row.
Is it possible to add some kind of If statement in the column definitions? Would I have to use a custom formatter?
eg.
var eventColumnDefs = [
{key:"event_id", sortable:false},
{key:"event_name", sortable:true},
{key:"extended", sortable:true, formatter: function (o) {
if (event_name=type1||event_name=type4||event_name=type5) {
editor:"dropdown", editorOptions:{dropdownOptions:eventData.extendedList}
}
}
}];
I know this code wouldn't work, by the way, I would just appreciate a bit of guidance.
You are close. In the column definition you add the information about the dropdown editor as if you always wanted it to show up. Now, going to the code sample here: http://developer.yahoo.com/yui/datatable/#cellediting
See the last line that attaches onEventShowCellEditor to whatever event you want to make the editor pop up? That is where you put the conditional. Instead of just asking to show the cell editor under any circumstance, you put some code there, like:
myDataTable.subscribe("cellClickEvent", function (ev) {
if ( ** whatever ** ) {
this.myDataTable.onEventShowCellEditor.apply(this, arguments);
}
});
I haven't been doing YUI2 for quite some time now so I don't remember the details on the arguments received by the event listener. I believe that you might also use showCellEditor() instead of onEventShowCellEditor, the later only massages the arguments as received from the event listener and ends up calling showCellEditor so you might as well skip it.
Found a hacky solution as follows
In Column Definitions:
var eventColumnDefs = [
{key:"player_name", sortable:true, editor:"dropdown", editorOptions:{dropdownOptions:currenteam}}
];
Then later on in initialiseTables:
eventDataTable.subscribe("cellClickEvent", function(ev) {
var target, column, field;
target = ev.target;
column = this.getColumn(target);
if (column.key === "player_name") {
field= this.getRecord(target).getData("team_id");
}
if (hometeamlistID == field) {
currenteam=hometeamlist;
} else if (awayteamlistID == field) {
currenteam=awayteamlist;
}
eventDataTable._oColumnSet._aDefinitions[8].editorOptions.dropdownOptions = currenteam;
});
hometeamlist, awayteamlist, hometeamlistID and awayteamlistID are pulled from an XML string. I haven't included that code above, but some of it is included in my question here:
YUI 2.5. Populating a dropdown from XML
I am developing a grid that contains a list of permits per module.
What I want is to validate every 2 events when a change is made in a combobox in a column. I'm using 1 and 0 for activating / deactivating
The first case: If I active "write", "modify", "delete" or "print" means that self-select "read"
The second case is the opposite: If you disable "Read" will turn off automatically "write", "modify", "delete" and "print"
Researching I found the option to use functions of input events:
{"name":"read",
"index":"read",
"width":48,
"resizable":false,
"editable":true,
"edittype":"select",
"editoptions":{
"value":"0:0;1:1",
"dataEvents":[{
"type":"change",
"fn":function(e){
if($(e.target).val() == '0')
{
// actions here...
}
}
}]
}
}
You can change the elements of the other columns ... by row?
EDIT
my solution:
$('tr.jqgrow select[name*="read"]').live("change",function()
{
if($(this).val() === '0') $(this).closest('tr.jqgrow').find('select.editable').not(this).find('option:first-child').attr("selected", "selected");
});
$('tr.jqgrow select[name!="read"]').live("change",function()
{
$(this).closest('tr.jqgrow').find('select[name*="read"]').find('option:last-child').attr("selected", "selected");
});
In the answer you will find an example how to implement dependent selects in jqGrid. By the way you can use the same idea with formatter: "checkbox". In the case the implementation will be much easier. It's important that you have to modify the <select> elements or chachboxes manually.
One more answer can you show another implementation option which you can use.
Is there a way to toggle the multiselect option of a grid?
Changing the multiselect parameter of the grid and calling for a reload has the side-effect of leaving the header behind when disabling or not creating the header column if multiselect was not TRUE upon the grid creation.
The closest I have come is setting multiselect to TRUE upon grid creation and using showCol and hideCol to toggle: $('#grid').showCol("cb").trigger('reloadGrid');
This has a side effect of changing the grid width when toggled. It appears the cb column width is reserved when it is not hidden.
Basically I'm attempting to create a grid with an "edit/cancel" button to toggle the multiselect -- very similar to how the iPhone/iPad handles deleting multiple mail or text messages.
Thank you in advance.
I full agree with Justin that jqGrid don't support toggling of multiselect parameter dynamically. So +1 to his answer in any way. I agree, that the simplest and the only supported way to toggle multiselect parameter will be connected with re-initialize (re-creating) the grid.
So if you need to change the value of multiselect parameter of jqGrid you need first change multiselect parameter with respect of respect setGridParam and then re-creating the grid with respect of GridUnload method for example. See the demo from the answer.
Nevertheless I find your question very interesting (+1 for you too). It's a little sport task at least to try to implement the behavior.
Some remarks for the understanding the complexity of the problem. During filling of the body of the grid jqGrid code calculate positions of the cells based on the value of multiselect parameter (see setting of gi value here and usage it later, here for example). So if you will hide the column 'cb', which hold the checkboxes, the cell position will be calculated wrong. The grid will be filled correctly only if either the column 'cb' not exists at all or if you have multiselect: true. So you have to set multiselect: true before paging or sorting of the grid if the column 'cb' exist in the grid. Even for hidden column 'cb' you have to set multiselect to true. On the other side you have to set multiselect to the value which corresponds the real behavior which you need directly after filling of the grid (for example in the loadComplete).
I hope I express me clear inspite of my bad English. To be sure that all understand me correctly I repeat the same one more time. If you want try to toggle multiselect dynamically you have to do the following steps:
create grid in any way with multiselect: true to have 'cb' column
set multiselect: false and hide 'cb' column in the loadComplete if you want to have single select behavior
set multiselect: true always before refreshing the grid: before paging, sorting, filtering, reloading and so on.
I created the demo which seems to work. It has the button which can be used to toggle multiselect parameter:
In the demo I used the trick with subclassing (overwriting of the original event handle) of reloadGrid event which I described the old answer.
The most important parts of the code from the demo you will find below:
var events, originalReloadGrid, $grid = $("#list"), multiselect = false,
enableMultiselect = function (isEnable) {
$(this).jqGrid('setGridParam', {multiselect: (isEnable ? true : false)});
};
$grid.jqGrid({
// ... some parameters
multiselect: true,
onPaging: function () {
enableMultiselect.call(this, true);
},
onSortCol: function () {
enableMultiselect.call(this, true);
},
loadComplete: function () {
if (!multiselect) {
$(this).jqGrid('hideCol', 'cb');
} else {
$(this).jqGrid('showCol', 'cb');
}
enableMultiselect.call(this, multiselect);
}
});
$grid.jqGrid('navGrid', '#pager', {add: false, edit: false, del: false}, {}, {}, {},
{multipleSearch: true, multipleGroup: true, closeOnEscape: true, showQuery: true, closeAfterSearch: true});
events = $grid.data("events"); // read all events bound to
// Verify that one reloadGrid event hanler is set. It should be set
if (events && events.reloadGrid && events.reloadGrid.length === 1) {
originalReloadGrid = events.reloadGrid[0].handler; // save old
$grid.unbind('reloadGrid');
$grid.bind('reloadGrid', function (e, opts) {
enableMultiselect.call(this, true);
originalReloadGrid.call(this, e, opts);
});
}
$("#multi").button().click(function () {
var $this = $(this);
multiselect = $this.is(":checked");
$this.button("option", "label", multiselect ?
"To use single select click here" :
"To use multiselect click here");
enableMultiselect.call($grid[0], true);
$grid.trigger("reloadGrid");
});
UPDATED: In case of usage jQuery in version 1.8 or higher one have to change the line events = $grid.data("events"); to events = $._data($grid[0], "events");. One can find the modified demo here.
I really like what you are trying to do here, and think it would be a great enhancement for jqGrid, but unfortunately this is not officially supported. In the jqGrid documentation under Documentation | Options | multiselect you can see the Can be changed? column for multiselect reads:
No. see HOWTO
It would be nice if there was a link or more information about that HOWTO. Anyway, this is probably why you are running into all of the weird behavior. You may be able to work around it if you try hard enough - if so, please consider posting your solution here.
Alternatively, perhaps you could re-initialize the grid in place and change it from/to a multi-select grid? Not an ideal solution because the user will have to wait longer for the grid to be set up, but this is probably the quickest solution.
A simpler answer:
<input type="button" value="Multiselect" onclick="toggle_multiselect()">
<script>
function toggle_multiselect()
{
if ($('#list1 .cbox:visible').length > 0)
{
$('#list1').jqGrid('hideCol', 'cb');
jQuery('.jqgrow').click(function(){ jQuery('#list1').jqGrid('resetSelection'); this.checked = true; });
}
else
{
$('#list1').jqGrid('showCol', 'cb');
jQuery('.jqgrow').unbind('click');
}
}
</script>
Where list1 is from <table id="list1"></table>.
While both autocomplete and select in jqgrid editform place the selected label into the cell, select will place the value (id) in the postdata array where autocomplete will place the label into the postdata array.
is there a way to get the editoption's autocomplete to post the item value (id) instead of the label?
here is the jqgrid code segment i'm using autocomplete in...
$('#tab3-grid').jqGrid({
colNames:['Workorder', 'wo.CUID',.....],
colModel:[
.
.
.
{name:'wo.CUID', index:'cu.LastName', width:120, fixed:true, align:'center', sortable:true, editable:true, edittype:'text',
editoptions:{dataInit:function(el){$(el).autocomplete({ source: 'php/customer-ac-script.php'
, minLength: 1
})
}
},
formoptions:{rowpos: 1, label:'Customer', elmprefix:'* '},
editrules:{required:true}
},
.
.
.
$('#tab3-grid').jqGrid('navGrid', '#tab3-pager',
{view:true, closeOnEscape:true, cloneToTop:true}, // general parameters that apply to all navigation options below.
{jqModal:true, navkeys:[true,38,40], savekey:[true,13]}, // edit options.
{jqModal:true, navkeys:[true,38,40], savekey:[true,13], reloadAfterSubmit:false, afterSubmit: addRecordID}, // add options.
{jqModal:true, afterSubmit: serverMessage}, // del options.
{jqModal:true}, // search options.
{jqModal:true, navkeys:[true,38,40]} // view options.
);
The php code segment:
// construct autocomplete select.
$i = 0;
while($row = mysql_fetch_assoc($result)) {
$output[$i][$crudConfig['id']] = $row['CUID'];
$output[$i][$crudConfig['value']] = $row['LastName'];
logMsg(__LINE__,'I','cu.CUID: '.$row['CUID'].', cu.LastName: '.$row['LastName']);
$i++;
}
// encode to json format and send output back to jqGrid table.
echo json_encode($output);
logMsg(__LINE__,'I','Send json output back to jqGrid table: '.json_encode($output));
Would it be as simple as calling a function under the autocomplete select event or the grid before or after editform submit?
Also, i noticed this note in the jqgrid doc's for datainit: that says...
Note: Some plugins require the position of the element in the DOM and
since this event is raised before inserting the element into the DOM
you can use a setTimeout function to accomplish the desired action.
Would the lack of including the settimeout function be causing the problem?
The server code which provide the JSON response on the autocomplete request has id and value properties. On the other side the standard behavior of jQuery UI Autocomplete is to use label and value properties (see "Datamodel" in the documentation). The value of label property (if any exist) will be used to display in the contextmenu. The value of value property will be placed in the <input> field after the user choose the item from the contextmenu. The value of label property can has HTML markup, but the value of value property must be the text.
So I see the problem as pure problem of the usage of jQuery UI Autocomplete independent on jqGrid. If I understand correct your question you can solve your problem by modification your server side code.
Oleg's answer clarifying the data model for jquery UI's autocomplete, has allowed me to move forward and understand that autocomplete has nothing to do with constructing and sending the postdata array to the server, jqgrid's editform handles it. With that knowledge, i was able to answer my original question and successfully integrate autocomplete into jqgrid. So, in the interest of sharing, i'd like to show you all my motivation and solution.
By default, selecting a label from the autocomplete list put's the value of the selected label/value pair into the text box. All the editform cares about when you submit is what's in the edit fields. So when you submit the editform, the cell's postdata element value will again contain the value of the autocomplete text box. But what if while wanting to post the value of the label/value pair, you want the label of the label/value pair displayed in the text box? You have a problem! How do you get the value of the label/value pair posted to the server?
Well, after spending a few days on it, it turns out to be quite simply. While i'm sure there is more than one solution, here is mine:
add a hidden id column in the grid
define the select: and focus: events in the autocomplete function
in the select: function; insert the selected label into the text box (optional), disable the default behavior of autocomplete, then set the cell of the hidden column to the value of the selected label/value pair
in the focus: function; insert the selected label into the text box(optional), disable the default behavior of autocomplete
add an "onclickSubmit:" event to the navgrid edit options with function name something like "fixpostdata"
in the "fixpostdata" function; get the cell value of the hidden column and insert it into the postdata element associated with the cell.
The following are the grid and javascript code segments i used…
grid segments
{name:'wo_CUID', index:'wo_CUID', width: 70, hidden: true},
{name:'wo.CUID', index:'cu.LastName', width:120, sortable:true, editable:true, edittype:'text',
editoptions:{
dataInit:function(el){ // el contains the id of the edit form input text box.
$(el).autocomplete({
source: 'php/customer-ac-script.php',
minLength: 1,
select: function(event, ui){event.preventDefault();
$(el).val(ui.item.label);
var rowid = $('#tab3-grid').getGridParam('selrow');
// set the hidden wo_CUID cell with selected value of the selected label.
$('#tab3-grid').jqGrid('setCell', rowid,'wo_CUID',ui.item.value);},
focus: function(event, ui) {event.preventDefault();
$(el).val(ui.item.label);}
})
}
},
formoptions:{rowpos: 1, label:'Customer', elmprefix:'* '},
editrules:{required:true}
},
.
.
$('#tab3-grid').jqGrid('navGrid', '#tab3-pager',
{view:true, closeOnEscape:true, cloneToTop:true},
{jqModal:true, navkeys:[false,38,40], onclickSubmit: fixpostdata}, // edit options.
.
.
javascript function
// define handler function for 'onclickSubmit' event.
var fixpostdata = function(params, postdata){
var rowid = $('#tab3-grid').getGridParam('selrow');
var value = $('#tab3-grid').jqGrid('getCell', rowid,'wo_CUID');
postdata['wo.CUID'] = value;
return;
}
The fixpostdata function fires when you submit the editform but befor the postdata array is sent to the server. At this point you replace the cell's postdata element value with whatever you want. In this case, the value of the label/value pair stored in the hidden column cell. When the function returns, the modified postdata array is sent to the server.
Done!
The drag/drop column reordering is a great feature, but how do I prevent the users from moving particular (non-data) columns?
For example, I am using the Checkbox Selectors for my mutli-select Grid, but this column should always be locked to the left, while the other columns can be freely reordered.
I looked at the sortable demos on jQuery UI and modified the setupColumnReorder function in slick.grid.js to exclude certain items. By excluding the checkbox column I was able to prevent it from getting reordered, even by dragging other columns before it.
function setupColumnReorder() {
var checkBoxColumn = $headers.children([0]).attr('id');
$headers.sortable({
items: "div:not('.slick-resizable-handle','#"+checkBoxColumn+"')",
...
Since my checkbox column is always first, I just grab the id like so. A bit of a hack, but it worked.
I tried this:
function setupColumnReorder() {
var checkBoxColumn = $headers.children([0]).attr('id');
$headers.sortable({
items: "div:not('.slick-resizable-handle','#"+checkBoxColumn+"')",
...
but i had problems when dragging other columns before it. Then i tried this:
grid.onColumnsReordered.subscribe(function (e, args) {
if (myGridColumns[0].id != grid.getColumns()[0].id)
{
grid.setColumns(myGridColumns);
}
else
{
myGridColumns= grid.getColumns();
}
});
and it was just fine.