In my KendoUI datasource, I have the following defined:
change: function (e) {
if (e.action === "itemchange") {
// auto-format Display As
var geoDisplay, geoUrl;
if (e.items[0].GeoState.length > 0) {
geoDisplay = e.items[0].GeoCity + ", " + e.items[0].GeoState;
} else {
geoDisplay = e.items[0].GeoCity;
}
//this.dataItem(this.select()).GeoDisplay = geoDisplay;
e.items[0].GeoCity = "updated: " + e.items[0].GeoCity; // visually updates if editing this field
e.items[0].GeoDisplay = geoDisplay; // field is not updated
}
console.log("change: " + e.action);
console.log(e);
// do something else with e
},
Essentially I want to update other fields on a row being edited based on a field's input.
In this example, GeoCity is updated. The itemchange event is fired and only the GeoCity field gets updated with the new value. However I can see from the data that the other fields' data have been updated.
I have tried doing a .sync() and a few other methods to get this to appear, but no luck so far.
Incidentally, my grid is defined within an AngularJS directive and it's onEdit event isn't what I'm looking for, as I want the events that fire when each field is updated, not the whole row.
How can I get the other fields to visually update?
I managed to solve the issue by placing the following code in the data source code:
change: function (e) {
if (e.action === "itemchange") {
// auto-format Display As
var thisRow = $("#accountGeoLocationEditorGrid tbody").find(".k-grid-edit-row");
// update geo display
if (e.field === "GeoCity" || e.field === "GeoState") {
var geoDisplay, geoUrl;
if (e.items[0].GeoState.length > 0) {
geoDisplay = e.items[0].GeoCity + ", " + e.items[0].GeoState;
} else {
geoDisplay = e.items[0].GeoCity;
}
if (e.items[0].GeoDisplay.length == 0) {
e.items[0].GeoDisplay = geoDisplay;
thisRow.find("input[name=GeoDisplay]").val(geoDisplay);
}
}
}
I was really looking for another way to do this, as I don't really want to be doing DOM lookup, etc in a defined datasource.
Other suggestions welcome.
Did you try the grid refresh() method? At the end of your changes in the change event, call this line (with your grid's correct id):
$("#grid").data("kendoGrid").refresh();
I've tested this on my grid and kendo's samples and it works fine like this. You are editing the datasource but the grid is not aware of the extra changes you have done, except the cell that was edited. Calling refresh will update all the cells on the grid to reflect the datasource.
Related
I have a grid setup using form editing. I want the the user to be able to edit only some of the rows. As a start, I figured the easiest way to do this was to have a column (probably hidden) in my server query and XML that denotes the Access or Role the user has. So essentially the grid now has a column "Access Role" with 'Y' or 'N' for each row. (where Y = user can edit, N = View/readonly)
I've tried a couple things to implement this. The best I've come up with is using the rowattr function, but my use is flawed since it hides the row in the grid (I don't want it hidden, just readonly):
function (rd) {
console.log('Row = '+rd.WWBPITM_SURROGATE_ID);
if (rd.ACCROLE === "N") {
console.log('RowAttr '+rd.ACCROLE);
return {"style": "display:none"};
}
This might be a start, but I'm not sure where to go from here and I'm not sure if I'm barking up the wrong tree with using rowattr.
I also tried using setCell in a loadComplete function, like this:
function GridComplete() {
var grid = $('#list1');
var rowids = grid.getDataIDs();
var columnModels = grid.getGridParam().colModel;
console.log('Check ACCROLE');
// check each visible row
for (var i = 0; i < rowids.length; i++) {
var rowid = rowids[i];
var data = grid.getRowData(rowid);
console.log('ACCROLE for '+rowid+' is '+data.ACCROLE);
if (data.ACCROLE == 'N') { // view only
// check each column
//console.log(data);
for (var j = 0; j < columnModels.length; j++) {
var model = columnModels[j];
if (model.editable) {
console.log('Is Editable? '+model.editable);
//grid.setCell(rowid, model.name, '', 'not-editable-cell', {editable: false, edithidden: true});
grid.setCell(rowid, model.name, '', 'not-editable-cell', {editoptions: { readonly: 'readonly', disabled: 'disabled' }});
}
}
}
}
}
But the editoptions don't seem to do anything with this.
Any ideas how to do this?
OK thanks for explaining about Form editing. Here's an example of how to prevent edits on certain records for jqGrid with form editing:
Start with this example of jqGrid form edit: http://www.ok-soft-gmbh.com/jqGrid/MulticolumnEdit.htm
Use the beforeInitData event to check your data before the edit form is displayed. Note that this is bound to the pager object.
Use getGridParam and getCell methods to get the current value you want. In my example I grabbed the client name
Add your own business logic for checking (I don't allow edits on 'test2')
Return false to prevent the edit form from popping up.
This example only handles edit, not insert or delete.
Replace $grid.jqGrid("navGrid", "#pager",...) from the example with this:
$grid.jqGrid("navGrid", "#pager", {view: true},
// Events for edit
{
beforeInitData: function (formid) {
var selectedRow = jQuery("#list").jqGrid('getGridParam','selrow'); //get selected rows
var selectedClient = $("#list").jqGrid('getCell', selectedRow, 'name');
if(selectedClient == 'test2')
{
alert('You are not allowed to edit records for client "' + selectedClient + '"');
return false;
}
}
},
// Events for add
{
beforeShowForm: function (formid) {
}
}
);
You didn't provide much information about how you're updating rows (there are various methods as described in JQGrid web page demos, but I took a guess as to a possible solution. I started with the example on the bottom of this page (trirand's web site wiki for inline_editing) http://www.trirand.com/jqgridwiki/doku.php?id=wiki:inline_editing and made a few changes.
Added a new data column securityGroup, and put in data like 'A', 'B', 'C'
Displayed the new data column in the grid
The example used the onSelectRow event to start editing a row if you clicked on a new row. I updated this callback to check the value of row['securityGroup'], and only start .editRow if it's in securityGroupA
JSFiddle at http://jsfiddle.net/brianwoelfel/52rrunar/
Here's the callback:
onSelectRow: function(id){
var row = $(this).getLocalRow(id);
if(id && id!==lastsel2){
jQuery('#rowed5').restoreRow(lastsel2);
if(row['securityGroup'] == 'A') {
jQuery('#rowed5').editRow(id,true);
}
lastsel2=id;
}
},
If this method won't work for you, please provide more information about how you're currently doing edits with jqGrid. This example obviously is very trivial and doesn't even post to PHP or mysql or anything.
In case it will be helpful for others, here is how I am implementing Read Only rows in Form Editing mode, based on a column which designates what level of access the user has to each row:
In editoptions, use the beforeShowForm event, like so:
beforeShowForm: function (formid) {
console.log('Checking for READONLY '+formid.name);
var selectedRow = jQuery("#list1").jqGrid('getGridParam','selrow'); //get selected rows
var selRole = $("#list1").jqGrid('getCell', selectedRow, 'ACCROLE');
if(selRole == 'N' || selRole == 'S' || selRole == 'R')
{
//$("<div>Sorry, you do not have access to edit this record.</div>").dialog({title: "Access Denied"});
formid.find("input,select,textarea")
.prop("readonly", "readonly")
.addClass("ui-state-disabled")
.closest(".DataTD")
.prev(".CaptionTD")
.prop("disabled", true)
.addClass("ui-state-disabled");
formid.parent().find('#sData').hide();
var title=$(".ui-jqdialog-title","#edithd"+"list1").html();
title+=' - READONLY VIEW';
$(".ui-jqdialog-title","#edithd"+"list1").html(title);
formid.prepend('<span style="color: Red; font-size: 1em; font-weight: bold;">You viewing READONLY data.</span>');
}
I have a SlickGrid with dataview working pretty good, grid and dataview are synced up for modify and delete selections using syncGridSelection, however an interesting problem occurs on the changed CSS styles. The changed rows CSS stlye are being applied to the same "visible" row number in the grid when I choose a filter set that does not include the actual changed rows. The sort works fine, but I noticed that the filter is not working. Does anyone have a fix for this? Can you paste as much info and code for me as possible because I'm new to SlickGrid. I pasted code that loads up the grid.
function LoadGridData() {
$.getJSON('#Url.Action("GetConfigurations")', function (rows) {
if (rows.length > 0) {
if (rows[0].id = 'undefined') {
$(rows).each(function (index) {
rows[index].newAttribute = "id"
rows[index]["id"] = index;
});
}
};
data = rows;
dataView.beginUpdate();
dataView.setItems(data);
dataView.setFilter(filter);
dataView.endUpdate();
// Refresh the data render
grid.invalidate();
grid.render();
grid.setSelectedRows([]);
dataView.syncGridSelection(grid, true);
});
}
After debugging I found that I had used an older example of marking css changed in function getItemMetadata. The correct code is below. Previously I was referencing data[row]. When Syncing DataView to Grid, the getItem() method returns the correct row. In this case my DataState is my own changed indicator on the view model.
dataView.getItemMetadata = function (row) {
var item = this.getItem(row);
if (item && item.DataState == 2) {
return {
"cssClasses":
"changed"
};
}
How can I display any applied filter criteria on the Kendo UI Grid.
I would like to have a readonly display, of the applied criteria.
Current functionality does allow user to apply filter, but that the user has to go to the filter menu to look for the filter details.
The Kendo UI data source doesn't have a filter event, so you'd need to implement that yourself. Then when the event is triggered, you can get the current filter and format it in whatever way you want it displayed.
For example:
var grid = $("#grid").kendoGrid(...);
// override the original filter method in the grid's data source
grid.dataSource.originalFilter = grid.dataSource.filter;
grid.dataSource.filter = function () {
var result = grid.dataSource.originalFilter.apply(this, arguments);
if (arguments.length > 0) {
this.trigger("afterfilter", arguments);
}
return result;
}
// bind to your filter event
grid.dataSource.bind("afterfilter", function () {
var currentFilter = this.filter(); // get current filter
// create HTML representation of the filter (this implementation works only for simple cases)
var filterHtml = "";
currentFilter.filters.forEach(function (filter, index) {
filterHtml += "Field: " + filter.field + "<br/>" +
"Operator: " + filter.operator + "<br/>" +
"Value: " + filter.value + "<br/><br/>";
if (currentFilter.filters.length > 1 && index !== currentFilter.filters.length - 1) {
filterHtml += "Logic: " + currentFilter.logic + "<br/><br/>";
}
});
// display it somewhere
$("#filter").html(filterHtml);
});
See demo here.
Note that filters can be nested, so once that happens, this example code won't be enough - you'll have to make the code that converts the filters to HTML recursive.
In order to augment all data sources with the "afterfilter" event, you have to change the DataSource protototype instead of changing it on your instance:
kendo.data.DataSource.fn.originalFilter = kendo.data.DataSource.fn.filter;
kendo.data.DataSource.fn.filter = function () {
var result = this.originalFilter.apply(this, arguments);
if (arguments.length > 0) {
this.trigger("afterfilter", arguments);
}
return result;
}
If you want to integrate the whole thing into all grid widgets, you could create a new method filtersToHtml which gets you the HTML represenatation and add it to kendo.data.DataSource.fn like demonstrated above (or you could create your own widget derived from Kendo's grid); in the same way you could add a method displayFilters to kendo.ui.Grid.fn (the grid prototype) which displays this HTML representation in a DOM element whose selector you could pass in with the options to your widget (you could ultimately also create this element within the grid widget). Then instead of triggering "afterfilter" in the filter method, you could call displayFilters instead.
Considering the complexity of the complete implementation which always displays filters, I'd suggest extending the Kendo grid instead of simply modifying the original code. This will help keep this more maintainable and gives it a bit of structure.
how about combining two filters of grid.
this way the user can see the selected filter in text box and even remove it by hitting the 'x' button on filtered column text box.
you can do this by setting grid filterable like this
filterable: {
mode: "menu, row"
}
the documentation and example is in here
When I create a checkbox column (through use of formatters/editors) in Slickgrid, I've noticed that it takes two clicks to interact with it (one to focus the cell, and one to interact with the checkbox). (Which makes perfect sense)
However, I've noticed that I am able to interact with the checkbox selectors plugin (for selecting multiple rows) with one click. Is there any way I can make ALL of my checkboxes behave this way?
For futher readers I solved this problem by modifing the grid data itself on click event. Setting boolean value to opposite and then the formatter will display clicked or unclicked checkbox.
grid.onClick.subscribe (function (e, args)
{
if ($(e.target).is(':checkbox') && options['editable'])
{
var column = args.grid.getColumns()[args.cell];
if (column['editable'] == false || column['autoEdit'] == false)
return;
data[args.row][column.field] = !data[args.row][column.field];
}
});
function CheckboxFormatter (row, cell, value, columnDef, dataContext)
{
if (value)
return '<input type="checkbox" name="" value="'+ value +'" checked />';
else
return '<input type="checkbox" name="" value="' + value + '" />';
}
Hope it helps.
The way I have done it is pretty straight forward.
First step is you have to disable the editor handler for your checkbox.
In my project it looks something like this. I have a slickgridhelper.js to register plugins and work with them.
function attachPluginsToColumns(columns) {
$.each(columns, function (index, column) {
if (column.mandatory) {
column.validator = requiredFieldValidator;
}
if (column.editable) {
if (column.type == "text" && column.autocomplete) {
column.editor = Slick.Editors.Auto;
}
else if (column.type == "checkbox") {
//Editor has been diasbled.
//column.editor = Slick.Editors.Checkbox;
column.formatter = Slick.Formatters.Checkmark;
}
}
});
Next step is to register an onClick event handler in your custom js page which you are developing.
grid.onClick.subscribe(function (e, args) {
var row = args.grid.getData().getItems()[args.row];
var column = args.grid.getColumns()[args.cell];
if (column.editable && column.type == "checkbox") {
row[column.field] = !row[column.field];
refreshGrid(grid);
}
});
Now a single click is suffice to change the value of your checkbox and persist it.
Register a handler for the "onClick" event and make the changes to the data there.
See http://mleibman.github.com/SlickGrid/examples/example7-events.html
grid.onClick.subscribe(function(e, args) {
var checkbox = $(e.target);
// do stuff
});
The only way I found solving it is by editing the slick.checkboxselectcolumn.js plugin. I liked the subscribe method, but it haven't attached to me any listener to the radio buttons.
So what I did is to edit the functions handleClick(e, args) & handleHeaderClick(e, args).
I added function calls, and in my js file I just did what I wanted with it.
function handleClick(e, args) {
if (_grid.getColumns()[args.cell].id === _options.columnId && $(e.target).is(":checkbox")) {
......
//my custom line
callCustonCheckboxListener();
......
}
}
function handleHeaderClick(e, args) {
if (args.column.id == _options.columnId && $(e.target).is(":checkbox")) {
...
var isETargetChecked = $(e.target).is(":checked");
if (isETargetChecked) {
...
callCustonHeaderToggler(isETargetChecked);
} else {
...
callCustonHeaderToggler(isETargetChecked);
}
...
}
}
Code
pastebin.com/22snHdrw
Search for my username in the comments
I used the onBeforeEditCell event to achieve this for my boolean field 'can_transmit'
Basically capture an edit cell click on the column you want, make the change yourself, then return false to stop the cell edit event.
grid.onBeforeEditCell.subscribe(function(row, cell) {
if (grid.getColumns()[cell.cell].id == 'can_transmit') {
if (data[cell.row].can_transmit) {
data[cell.row].can_transmit = false;
}
else {
data[cell.row].can_transmit = true;
}
grid.updateRow(cell.row);
grid.invalidate();
return false;
}
This works for me. However, if you're using the DataView feature (e.g. filtering), there's additional work to update the dataview with this change. I haven't figured out how to do that yet...
I managed to get a single click editor working rather hackishly with DataView by calling
setTimeout(function(){ $("theCheckBox").click(); },0);
in my CheckBoxCellEditor function, and calling Slick.GlobalEditorLock.commitCurrentEdit(); when the CheckBoxCellEditor created checkbox is clicked (by that setTimeout).
The problem is that the CheckBoxCellFormatter checkbox is clicked, then that event spawns the CheckBoxCellEditor code, which replaces the checkbox with a new one. If you simply call jquery's .click() on that selector, you'll fire the CheckBoxCellEditor event again due because slickgrid hasn't unbound the handler that got you there in the first place. The setTimeout fires the click after that handler is removed (I was worried about timing issues, but I was unable to produce any in any browser).
Sorry I couldn't provide any example code, the code I have is to implementation specific to be useful as a general solution.
while selecting the checkbox in the jqgrid i need to sum the values of row data in jqgrid and i need to display those data in the footer of the jqgrid.Please help me out how to achieve that.
Thanks in Advance,
Silambarasan,
You can use footerData method. See here and here for details and demo examples.
I got the answer ,i solved that issue.
The Answer is.
footerrow:true,
userDataOnFooter:true,
onSelectRow: function(rowId)
{ handleSelectedRow(rowId); },
function handleSelectedRow(id) {
var jqgcell = jQuery('#list1').getCell(id, 'headerId');
var amount = jQuery('#list1').getCell(id, 'amount');
var cbIsChecked = (jQuery("#jqg_list1_"+jqgcell).attr('checked'));
if(cbIsChecked==true)
{
if(amount!=null)
{
totalAmt = parseInt(totalAmt) + parseInt(amount);
}
}else
{
if(amount!=null)
{
totalAmt = parseInt(totalAmt) - parseInt(amount);
}
}
myGrid.jqGrid('footerData','set',{needbydate:'Total Amount:',amount:totalAmt});
}
The above function is used to get the values of the selected row by clicking the checkbox you will get the value from that by calling the external function like "handleSelectedRow" you pass your row object from that you do your operation and finally update your answer by using the jqGrid function like "myGrid.jqGrid('footerData','set',{needbydate:'Total Amount:',amount:totalAmt}); "
It will update in your footer.