jqGrid Make a Row readonly - jqgrid

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>');
}

Related

Delete Button jqgrid in just few rows

How do I set the delete button in just a few lines?
My list is returned by json and some records do not need the delete button.
How do I remove it from PHP?
Thks.
EDITED
I SOLVED
loadComplete: function () {
var rowIds = jQuery(grid_selector).jqGrid('getDataIDs');
for (i = 0; i <= rowIds.length; i++) {//iterate over each row
rowData = jQuery(grid_selector).jqGrid('getRowData', rowIds[i]);
if (rowData['id'] == true) {
$("#jDeleteButton_"+rowIds[i]).hide();
}
}
}
You can use free jqGrid, it's the fork of jqGrid, which I develop. I supports isDisplayButtons callback of formatter: "actions", which you can use to inform jqGrid about the rows, which can be deleted. See the demo created for the answer.

Kendo Data Grid "Add new Record" ignores validation rules

I have a DataGrid component with remote CRUD operations.
I have a problem with the validation of its grid fields.
When I click "Add new Record", a new empty line is added to the datagrid.
Without filling in any values, and clicking on "Save Changes" button, the create operation is processed even if no required values are filled.
For example name:
name: {
editable: true,
nullable: false,
validation: {
required: {
message: "Please enter a First name"
}
}
},
Validation Message is displayed only when I click into the name cell.
Is there any way to solve this issue?
I tried enable/ disable batch editing, etc... but without luck.
I recently bumped into this issue as well, it gave me plenty of head scratching, but there are two options.
Use inline editing
Force validation on new cells
I did not go with option 1 for numerous reasons including the fact that you now need to have an edit, update, and cancel button per row before it would do any validation.
Option 2
Got the idea from Brian Roth # the Telerik forums, see here. I used his solution but optimized it a little bit.
In the saveChanges event, implement this:
saveChanges: function (e) {
if (!checkCells(this)) {
e.preventDefault(); //prevents save if validation fails
}
}
checkForInvalidCells will basically go through every new row's cells, open for edit and attempt to close (and in the process, firing the validation). If validation was not successful, then prevent the save.
function checkCells(grid)
{
var rows = grid.tbody.find("tr"); //get rows
for (var i = 0; i < rows.length; i++) {
var rowModel = grid.dataItem(rows[i]); //get row data
if (rowModel && rowModel.isNew()) {
var colCells = $(rows[i]).find("td"); //get cells
for (var j = 0; j < colCells.length; j++) {
if ($(colCells[j]).hasClass('k-group-cell'))
continue; //grouping enabled will add extra td columns that aren't actual columns
grid.editCell($(colCells[j])); //open for edit
if (!grid.editable.end()) { //trigger validation
return false; //if fail, return false
}
else {
grid.closeCell(); //if success, keep checking
}
}
}
}
return true; //all cells are valid
}
In case you are comparing my implementation and his, the difference is:
Made it cleaner without the need for break statement and bool flag
Kept save event variable e logic in the event itself
Utilized isNew() method to check for a new row, a new row's ID/Key may not always be 0, especially if it is an editable field and you have already put info in that field
I account for grouping enabled, grouping will introduce extra td columns that shouldn't be checked for saving
Overall, I hate doing hacks like this, but sometimes it's what you gotta do. Be aware that if you are adding a LOT of new rows at a time, the check will take substantially longer.
Also, each check will move the grid's scroll view to the cell that was last checked (since you are opening the cells for editing, programmatically). If all your new rows are visible at the top of the grid, this is no problem for you, but if you are appending new rows at the bottom, the user may find it strange that the grid has scrolled after saving.
This was a huge help. Thank you very much, #gitsitgo.
I needed to make minor changes to get this to work in my grid, so I thought I'd post it here.
var checkCells = (grid: any): boolean => {
var rows = grid.tbody.find("tr"); //get rows
for (var i = 0; i < rows.length; i++) {
var rowModel = grid.dataItem(rows[i]); //get row data
if (rowModel && rowModel.isNew() || rowModel.IsUpdated) {
var colCells = $(rows[i]).find("td"); //get cells
for (var j = 0; j < colCells.length; j++) {
if ($(colCells[j]).hasClass('k-group-cell'))
continue; //grouping enabled will add extra td columns that aren't actual columns
if ($(colCells[j]).hasClass('k-hierarchy-cell'))
continue; //hierarchy cell is not actual column
grid.editCell($(colCells[j])); //open for edit
if (grid.editable) { //check if editable
if (!grid.editable.end()) { //trigger validation
return false; //if fail, return false
}
else {
grid.closeCell(); //if success, keep checking
}
}
}
}
}
return true; //all cells are valid
}
The differences are:
Checks grid.editable is not null before calling grid.editable.end(). This was the main change that I needed as I have cells that are not editable.
Runs validation for both new and updated rows. Note that IsNew() is a function whereas IsUpdated is a boolean.
Skips cells with class k-hierarchy-cell. This is only needed in hierarchical grids (which is what I'm using this for).

Kendo AutoComplete - force users to make valid selection

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;
}
}

JQGrid setCell customFormatter

I'm using setCell to set the value of a cell. The problem is it is still calling the customFormatter specified for the column. Is there anyway I can set the value of this cell without it having to go through the customFormatter?
First of all the custom formatter will be used on every grid refresh, so to set the cell value you have to do this after the custom formatter. The best place to do this is inside of loadComplete or gridComplete event handler.
To set the cell value you can use jQuery.text for example. So you should get jQuery object which represent the cell (<td> element) and then use jQuery.text or jQuery.html to change the cell contain. How I understand you, you knows the rowid of the cell and the column name which you want to change. The following code could be:
loadComplete: function() {
var rowid = '2', colName = 'ship_via', tr,
cm = this.p.colModel, iCol = 0, cCol = cm.length;
for (; iCol<cCol; iCol++) {
if (cm[iCol].name === colName) {
// the column found
tr = this.rows.namedItem(rowid);
if (tr) {
// if the row with the rowid there are on the page
$(tr.cells[iCol]).text('Bla Bla');
}
break;
}
}
}
See the corresponding demo here.

jqGrid - inline edit and trapping edit rules

In my situation, I need to allow users to edit various cells in the grid and then save the whole grid to the server later. I have pretty much solved this issue with inline editing and saving to ‘clientArray’. However, I am trying to use the editRules and have run into some issues.
If I make a column editable, and use edit rules to require it to be a number
{ name: 'Value', index: 'Value', width: 50, sortable: true,edittype: 'text',
editable: true, editoptions: { maxlength: 10 },
editrules:{number: true},
formatter:currencyFmatter, unformat:unformatCurrency },
and I control editing and saving in the onSelectRow event:
onSelectRow: function(id){
if(id && id!==lastSel){
jQuery("#Groups").saveRow(lastSel,true,'clientArray');
jQuery("#Groups").editRow(id,true);
}
lastSel=id
},
I then use a button click event to save the grid. Everything works great untill I put a non-numeric value into the Value cell then click on the row below it. It throw as warning box up and stop the save, but it does not stop me from changing rows. So I now have two rows open for editing. Is there a way to trap the editrule error so I can handle it before moving to the next row.
I have tried to use the functions with saveRow (succesfunc, aftersavefunc, errorfunc, afterrestorefunc), of which all say fire after data is saved to server, which does not appear to work for ‘clientArray’.
Basically, I need to find a way to validate the data in inline editing when saved to ‘clientArray’ and information, suggestions, and particularly, examples would be greatly appreciated.
Thanks.
After playing for awhile, I decided edit rules dont work so well with inLine Editing. So, as you suggested, I made my own validation routine. The trick was figuring out how to get the value of the edited row.
The one thing I am trying to figure out now is how to get the focus to go back to the Value column. Back to the documentation!
if(id && id!==lastSel){
//dont save if first click
if (lastSel != -1) {
//get val of Value to check
var chkval = jQuery("#"+lastSel+"_Value").val() ;
// verify it is a number
if (isNaN(chkval)) {//If not a number
//Send Validation message here
//Restore saved row
jQuery("#Grid").restoreRow(lastSel);
//Return to failed save row
jQuery("#Grid ").setSelection(lastSel,false);
//reopen for editing
jQuery("#Grid ").editRow(lastSel,true);
//Note - dont reset lastSel as you want to stay here }
else {
// If number is good, proceed to save and edit next
jQuery("#Grid ").jqGrid('saveRow',lastSel, checksave, 'clientArray', {}, null, myerrorfunc);
jQuery("#Grid ").editRow(id,true);
lastSel=id;
};
isDirty = true;
};
else {
//first click - open row for editing
alert("new Edit")
jQuery("#Grid ").editRow(id,true);
lastSel=id;}
}
in order to resolve this, I used the plugin jquery.limitkeypress.min.js.
onSelectRow: function(id){
if(id && id!==lastsel){
jQuery('#treegrid').jqGrid('restoreRow',lastsel);
jQuery('#treegrid').jqGrid('editRow',id, true);
$("input[name=Presupuesto]").limitkeypress({ rexp: /^[+]?\d*\.?\d*$/ });
lastsel=id;
}
}
where, "Presupuesto" is the name of the column where I let input data to the user.
It works very good...
To take care of this you can code up your own validation function, myRowIsValid in the example below. Then, just call this function as part of your onSelectRow event handler:
onSelectRow: function(id){
if(id && id!==lastSel){
if (lastSel != null && !myRowIsValid(lastSel) ) {
// Display validation error somehow
return;
}
jQuery("#Groups").saveRow(lastSel,true,'clientArray');
jQuery("#Groups").editRow(id,true);
}
lastSel=id
},
Basically, if the validation fails, then let the user know and do not edit the next row.
I made an array called edit_list, and in the callback oneditfunc I add the rowid to the list, and on aftersavefunc I remove it from the list.
That way you can examine the edit_list in the callbacks to determine if there is a row being edited.
This solution probably did not exist when the OP first asked, but this is how I solved it using the latest jqGrid (4.4.4).
I take advantage of the fact that the saveRow will return true/false on success.
Hopefully this helps.
function StartEditing($grd, id) {
var editparameters = {
"keys": true,
"url": 'clientArray'
};
$grd.jqGrid('editRow', id, editparameters);
$grd[0].LastSel = id;
}
onSelectRow: function(id) {
var $grd = $(this);
if (id && !isNaN($grd[0].LastSel) && id !== $grd[0].LastSel) {
if ($grd.jqGrid('saveRow', $grd[0].LastSel, { "url": 'clientArray' }))
StartEditing($grd, id);
else
$grd.jqGrid('setSelection', $grd[0].LastSel);
}
else
StartEditing($grd, id);
}
To trap edit rules on saveRow event,you can use the inbuilt return type of 'saveRow' .Please mark as answer if you are looking for this
yourcode:
onSelectRow: function(id){
if(id && id!==lastSel){
jQuery("#Groups").saveRow(lastSel,true,'clientArray');
jQuery("#Groups").editRow(id,true);
}
lastSel=id
}
modify code:
onSelectRow: function(id){
if(id && id!==lastSel){
var bool = jQuery("#Groups").saveRow(lastSel,true,'clientArray');
//bool true -->success,false -->invalid row /req field validations
if(bool)
{
//Returns true on sucessful save of record i.e to grid (when using client array)
//and set current selected row to edit mode
jQuery("#Groups").editRow(id,true);
}
else
{
//Set last selected row in edit mode and add required values
jQuery("#Groups").editRow(lastSel,true);
}
}
lastSel=id
}

Resources