jqgrid select only one row per group - jqgrid

How can we allow the users to only select one row per group?
I have the following code.
var data = [
{ ActionItemId: "AAZ08702-0001104", StrarTime: "2007-10-01", Category: "General", CategoryDetails: "dummy text of industry. a galley of type ", TargetCategory: "200.00",
TargetDateCategory: "10.00", ActualCategory: "210.00"}
];
$("#jqGrid").jqGrid({
data: data,
datatype: "local",
colModel: [
{ label: 'Action Item ID', name: 'ActionItemId', key: true },
{ label: 'Start Time', name: 'StrarTime'},
{ label: 'Category', name: 'Category'},
{ label: 'Details', name: 'CategoryDetails', cellattr: function (rowId, tv, rawObject, cm, rdata) { return 'style="white-space: normal;"' }},
{ label: 'Target <Category>', name: 'TargetCategory' },
{ label: 'Target Date <Category>', name: 'TargetDateCategory'}
],
loadonce: true,
viewrecords: true,
//width: 1000,
height: 400,
rowNum: 20,
rowList: [20, 30, 50],
rownumbers: true,
rownumWidth: 25,
multiselect: true,
shrinkToFit: false,
pager: "#jqGridPager",
grouping: true,
groupingView: {
groupField: ["Category"],
groupColumnShow: [true],
groupText: ["Category: <b>{0}</b>"],
groupOrder: ["asc"],
groupSummary: [false],
groupCollapse: false
}
});
I need to disable the ability to select multiple rows per column. is it possible?
Is there a setting in the grouping function to enable which will work as mu requirement? or should it be custom development?
Note: I have only added one column to avoid a very long code in the question

One of the possible implementation could be adding add callback which return false if another row from the same group is already selected. An example of the implementation is the following:
beforeSelectRow: function (rowid, e) {
var selarrrow = $(this).jqGrid("getGridParam", "selarrrow"),
$tr = $(e.target).closest("tr.jqgrow"),
otherIdsOfTheGroup;
if ($tr.length > 0) {
otherIdsOfTheGroup =
// get all rows of the group before the current
$tr.prevUntil("tr.jqgroup")
// add all rows of the group after the current
.add($tr.nextUntil("tr.jqgroup"))
// enum all the rows of the group without the current
.map(function () {
// test whether the rowid is already selected
if ($.inArray(this.id, selarrrow) >= 0) {
// add the rowid to the array of returned values
return this.id;
}
});
// otherIdsOfTheGroup contains the array of rowids of the rows
// from the same group, which are already selected
if (otherIdsOfTheGroup.length > 0) {
return false; // prevent selection
}
}
return true; // allow selection
}
See the demo
UPDATED: One can easy modify the aboce vode to unselect the previously selected rows from the same group. One need just call resetSelection for every rowid from otherIdsOfTheGroup array and return true from otherIdsOfTheGroup to allow selection:
beforeSelectRow: function (rowid, e) {
var $this = $(this),
selarrrow = $this.jqGrid("getGridParam", "selarrrow"),
$tr = $(e.target).closest("tr.jqgrow"),
otherIdsOfTheGroup;
if ($tr.length > 0) {
otherIdsOfTheGroup =
// get all rows of the group before the current
$tr.prevUntil("tr.jqgroup")
// add all rows of the group after the current
.add($tr.nextUntil("tr.jqgroup"))
// enum all the rows of the group without the current
.map(function () {
// test whether the rowid is already selected
if ($.inArray(this.id, selarrrow) >= 0) {
// add the rowid to the array of returned values
return this.id;
}
});
// otherIdsOfTheGroup contains the array of rowids of the rows
// from the same group, which are already selected
if (otherIdsOfTheGroup.length > 0) {
$.each(otherIdsOfTheGroup, function () {
$this.jqGrid("resetSelection", this);
});
}
}
return true; // allow selection
}
See the next demo. I included hiding of the column header of "Select All" button just to write less code. You can implement onSelectAll callback and allow to select only one (for example the first) row from every group.

I managed to solve this issue using the following code.
beforeSelectRow: function (id, e) {
var rowdata = $("#jqGrid").getRowData(id);
var category = rowdata.Category;
var selectedRowTR = $("#jqGrid").find("tr[id='" + id + "']");
var groupTRs = $("#jqGrid").find("tbody> tr.jqgrow > td[title='" + category + "']").parents("tr");
var ids = groupTRs.map(function () {
return this.id;
}).get();
var selectedIDs = $("#jqGrid").getGridParam("selarrrow");
var commonValues = [];
var i, j;
var arr1Length = ids.length;
var arr2Length = selectedIDs.length;
for (i = 0; i < arr1Length; i++) {
for (j = 0; j < arr2Length; j++) {
if (ids[i] === selectedIDs[j]) {
commonValues.push(ids[i]);
}
}
}
for (var i = 0; i < commonValues.length; i++) {
$("#jqGrid").jqGrid('setSelection', commonValues[i], false);
}
return true;
},
rowdata.Category; is the variable which the table is grouped. The only issue is that the user cannot untick what he/she has already selected in a group. so it works like a radio button set. But it works for my requirement.
hopefully we can improve this and introduce radio kind of behavior for grouping in jqgrid.
thanks everyone.

Related

How to freeze a Dynamic column in JqGrid?

I have a something like this:
i want to freeze the Doctor Name column which is Dynamic.
Actually, the whole grid is dynamic.
if (result[7] != null) {
var resu = JSON.parse(result[7]);
if (resu.length > 0) {
{
ColModel = [];
var model = Object.keys(resu[0]);
for (var i = 0; i < model.length; i++) {
var responColNM = "";
if (model[i] == "Doctor Name") {
responColNM = {
name: model[0], index: model[0], label: model[0], width: 140, editable: false, sortable: false, frozen: true,
}
}
else {
responColNM = {
name: model[i], index: model[i], label: model[i], width: 43, editable: false, sortable: false,
}
}
ColModel.push(responColNM);
}
}
}
strNew = resu;
}
else {
//store in arr
//str = { DOCTORNAME: '', CNT: '', DT: '' };
//strNew.push(str);
}
if (str == 1) {
colnames = [];
colmodel = ColModel;
$("#gvtable").jqGrid('setGridParam', { data: response }).trigger("reloadGrid");
$("#gvtable").jqGrid('setFrozenColumns').trigger("reloadGrid");
here, the column Doctor name does not freeze.
any suggestions?
Thanks in advance!
Some notes before to answer.
jqGrid name in colModel can't contain spaces. In your case it contain spaces
After call of setFrozenColumns method you do not need to trigger reloading the grid.
Finally, but not least, colModel can't be a dynamically changed. In order to do this you will need to destroy the grid and recreate it with the new colModel
The frozen column should be the first one in the array of colModel in order to be frozen. In your code it is not clear if it is first. You set the first column, but it is not known if it is first added in the colModel

Custom function only for edit mode, not add mode, in jqGrid

I have a jqGrid custom function as editrules: { custom: true, custom_func: checkforduplicates, required:true }
However, I want this function to be run only in add mode, not in edit mode. Is this possible ?
EDIT:- After answer below from Oleg, I changed code to below. However, the alert does not print. Not sure where I am going wrong.
colModel: [
{ key: true, name: 'id', editable: false, formatter: 'integer', viewable: false, hidden: true },
{
key: false,
name: 'name',
editable: true,
editrules: {
required: true,
custom: function (options) {
// options have the following properties
// cmName
// cm
// iCol
// iRow
// rowid
// mode - "editForm", "addForm", "edit", "add", "cell" and so on
// newValue - the value which need be validated
// oldValue - the old value
// some additional properties depends on the editing mode
alert("mode is " + options.mode);
if (options.mode === "add") { // "add" for inline editing
var grid = $("#grid");
var textsLength = grid.jqGrid("getRowData");
var textsLength2 = JSON.stringify(textsLength);
alert("i am here");
var myAttrib = $.map(textsLength,
function (item) { return item.name });
var count = 0;
for (var k in textsLength) {
if (textsLength.hasOwnProperty(k)) {
++count;
}
}
var text, i;
for (i = 0; i < count; i++) {
text = myAttrib[i];
if (value === text) {
return [false, " - Duplicate category name."];
}
}
return [true, ""];
}
return true;
}
}
},
Free jqGrid supports old style custom_func with the options value, name and iCol and the new style validation. To use new style validation one don't need to specify any custom_func callback, but to define custom as the calback function with one parameter:
editrules: {
required: true,
custom: function (options) {
// options have the following properties
// cmName
// cm
// iCol
// iRow
// rowid
// mode - "editForm", "addForm", "edit", "add", "cell" and so on
// newValue - the value which need be validated
// oldValue - the old value
// some additional properties depends on the editing mode
if (options.mode === "addForm") { // "add" for inline editing
// do the validation
}
return true;
}
}
In case of validation of Add form the mode property is equal to "addForm", options.iRow === -1, options.oldValue === null, options.rowid === "_empty". It's recommended to use options.mode to detect the editing (or searching mode) in free jqGrid because the values of other properties (iRow, oldValue and rowid) depends on the editing mode.
For version 4.7 I use this method. Form for adding data class for the table. After that, special actions verified by the user are performed.
{
name : "LOGIN",
index : "LOGIN", editrules: {
required:true,
custom:true,
custom_func: dublicateUser
}
...
{
closeAfterAdd : true,
width : 500,
recreateForm : true,
afterShowForm : function () {
jQuery("#TblGrid_list_users").addClass('addMode');
}
...
function dublicateUser() {
var a;
var login = jQuery('#LOGIN').val();
var checkMode = jQuery('#TblGrid_list_users').hasClass('addMode');
jQuery.ajax({
type: 'POST',
data: {login:login, mode:checkMode},
url: 'code/validate_user.php',
async: false,
success: function(data) {
if (data == 'err') {
a = 1;
}
else {
a=0;
}
}
});
if (a==1) {
return[false,"error"];
}
else {
return[true];
}
}

Kendo treelist - trying to set a column template

I'm working with a Kendo treelist widget, and disappointed to see there's no rowTemplate option as there is on the Kendo grid.
I see a columnTemplate option (i.e. http://docs.telerik.com/kendo-ui/api/javascript/ui/treelist#configuration-columns.template ), but this will affect the entire column.
However, I need to drill into each cell value and set a css color property based on a ratio ( i.e. If value/benchmark < .2, assign <span style='color:red;'> , but my color value is dynamic.
There's a dataBound: and dataBinding: event on the treelist, but I'm still trying to figure out how to intercept each cell value and set the color once I've done my calculation.
var treeOptions = {
dataSource: ds,
columns: colDefs,
selectable: true,
scrollable: true,
resizable: true,
reorderable: true,
height: 320,
change: function (e) {
// push selected dataItem
var selectedRow = this.select();
var row = this.dataItem(selectedRow);
},
dataBound: function (e) {
console.log("dataBinding");
var ds = e.sender.dataSource.data();
var rows = e.sender.table.find("tr");
}
};
and this is where I'm building out the `colDefs' object (column definitions):
function parseHeatMapColumns(data, dimId) {
// Creates the Column Headers of the heatmap treelist.
// typeId=0 is 1st Dimension; typeId=1 is 2nd Dimension
var column = [];
column.push({
"field": "field0",
"title": "Dimension",
headerAttributes: { style: "font-weight:" + 'bold' + ";" },
attributes : { style: "font-weight: bold;" }
});
var colIdx = 1; // start at column 1 to build col headers for the 2nd dimension grouping
_.each(data, function (item) {
if (item.typeId == dimId) {
// Dimension values are duplicated, so push unique values (i.e. trade types may have dupes, whereas a BkgLocation may not).
var found = _.find(column, { field0: item.field0 });
if (found == undefined) {
column.push({
field: "field2",
title: item.field0,
headerAttributes: {
style: "font-weight:" + 'bold'
}
,template: "<span style='color:red;'>#: field2 #</span>"
});
colIdx++;
}
}
});
return column;
}
**** UPDATE ****
In order to embed some logic within the template :
function configureHeatMapColumnDefs(jsonData, cols, model) {
var colDef = '';
var dimId = 0;
var colorProp;
var columns = kendoGridService.parseHeatMapColumns(jsonData, dimId);
// iterate columns and set color property; NB: columns[0] is the left-most "Dimension" column, so we start from i=1.
for (var i = 1; i <= columns.length-1; i++) {
columns[i]['template'] = function (data) {
var color = 'black';
if (data.field2 < 1000) {
color = 'red';
}
else if (data.field2 < 5000) {
color = 'green';
}
return "<span style='color:" + color + ";'>" + data.field2 + "</span>";
};
}
return columns;
}
Advice is appreciated.
Thanks,
Bob
In the databound event you can iterate through the rows. For each row you can get the dataItem associated with it using the dataitem() method (http://docs.telerik.com/kendo-ui/api/javascript/ui/treelist#methods-dataItem)
Once you have the dataitem, calculate your ration and if the row meets the criteria for color, change the cell DOM element:
dataBound: function (e) {
var that = e.sender;
var rows = e.sender.table.find("tr");
rows.each(function(idx, row){
var dataItem = that.dataItem(row);
var ageCell = $(row).find("td").eq(2);
if (dataItem.Age > 30) {
//mark in red
var ageText = ageCell.text();
ageCell.html('<span style="color:red;">' + ageText + '</span>');
}
}
DEMO
UPDATE: you can also do this with a template:
$("#treelist").kendoTreeList({
dataSource: dataSource,
height: 540,
selectable: true,
columns: [
{ field: "Position"},
{ field: "Name" },
{ field: "Age",
template: "# if ( data.Age > 30 ) { #<span style='color:red;'> #= data.Age # </span> #}else{# #= data.Age # #}#"
}
],
});
DEMO

True scrolling data in JQGrid not working with MVC 3

Can someone help me with this issue?
I have a jqgrid(4.4.0) and I want to implement true scrolling. The problem is that it only loads datarows once, meaning the first page only. So when I scroll, it doesn't fetch the other pages. I have checked this issue and still not working.
Here is the jqgrid:
jQuery("#deptProjectsGrid").jqGrid({
url: '#Url.Action("GetUserProjects", "TimesheetByWeek")',
datatype: 'json',
mtype: 'GET',
colNames: ['Projekt', 'Oprettet af'],
colModel: [
{ name: 'project', index: 'project', sortable: true, align: 'left' },
{ name: 'creator', index: 'creator', align: 'left' }
],
height: 300,
width: 250,
altRows: true,
pager: '#pager1',
scroll: 1,
rowNum: 15,
rownumbers: true,
viewrecords: true,
sortname: 'project',
sortorder: 'desc',
gridview: true,
caption: "Projekter",
onSelectRow: function (ids) {
if (ids == null) {
ids = 0;
if ($taskgrid.jqGrid('getGridParam', 'records') > 0) {
$taskgrid.jqGrid('setGridParam', { url: '#Url.Action("GetProjectTasks", "TimesheetByWeek")/' + ids });
$taskgrid.jqGrid('setCaption', "No Tasks " + ids).trigger('reloadGrid');
}
} else {
var data = $("#deptProjectsGrid").getRowData(ids);
$taskgrid.jqGrid('setGridParam', { url: '#Url.Action("GetProjectTasks", "TimesheetByWeek")/' + ids });
$taskgrid.jqGrid('setCaption', "Tasks for " + data.project).trigger('reloadGrid');
}
}
});
The controller action looks like this:
public JsonResult GetUserProjects(int page, int rows, string search, string sidx, string sord)
{
try
{
using (DanishEntities db = new DanishEntities())
{
int totalRecords = 0;
var allProjectEntities = _projectRepository.GetProjectsPaged(page, rows, out totalRecords, db);
var projects = Mapper.Map<List<Project>, List<ProjectModel>>(allProjectEntities);
var totalPages = (int)Math.Ceiling(totalRecords / (float)rows);
var jsonData = new
{
total = rows,
page = page++,
records = totalRecords,
rows = (
from p in projects
select new
{
id = p.ID,
cell = new object[] { p.Name, "john doe" }
}).ToArray()
};
return Json(jsonData, JsonRequestBehavior.AllowGet);
}
}
catch (Exception)
{
throw;
}
}
and the query is:
public List<Project> GetProjectsPaged(int page, int rows, out int totalCount, DanishEntities dbContext)
{
List<Project> pagedProjectEntities = new List<Project>();
try
{
totalCount = dbContext.Projects.Count();
pagedProjectEntities = dbContext.Projects
.OrderBy(p => p.ID)
.Skip((page-1) * rows)
.Take(rows)
.ToList();
}
catch (Exception)
{
throw;
}
return pagedProjectEntities;
}
I have wasted 4 hours of my life trying to figure this out and I just can't see the problem. I have even set "total = totalPages" in the controller action but nothing is working. Help please!!
Just another quick question on jqGrid: How do I disable "rowNum" in a jqGrid so that the grid shows all available rows returned by the query in the data set?

jqGrid Retain Invalid Cell Value After EditRules Pop Up

#Oleg - I am new to jqGrid.I have three issues. Urgent help required.
I am using jqGrid 3.8, inline edit mode.
I want to retain the invalid cell values after the pop up for invalid cell.
Also I want to set the focus back to the invalid cell.
I have "add row" and filter tool bar feature in my jqGrid. I have used Oleg's solution in creating drop down for filter tool bar (posted in another jQuery thread).
**
- Problem:
** I am calling setSearchSelect from afterSaveCell, because I want to add new values in filter drop down every time I add or delete column.(Note: column is a textbox). But the filter tool bar isn't getting refreshed even if I use
var sgrid = $("#list")[0];
sgrid.triggerToolbar();
See the code below for setting the toolbar.
<script type="text/javascript">
var mydata = [
{id:"1", Name:"Miroslav Klose", Category:"sport", Subcategory:"football"},
{id:"2", Name:"Michael Schumacher", Category:"sport", Subcategory:"formula 1"},
{id:"3", Name:"Albert Einstein", Category:"science", Subcategory:"physics"},
{id:"4", Name:"Blaise Pascal", Category:"science", Subcategory:"mathematics"}
],
grid = $("#list"),
getUniqueNames = function(columnName) {
var texts = grid.jqGrid('getCol',columnName), uniqueTexts = [],
textsLength = texts.length, text, textsMap = {}, i;
for (i=0;i<textsLength;i++) {
text = texts[i];
if (text !== undefined && textsMap[text] === undefined) {
// to test whether the texts is unique we place it in the map.
textsMap[text] = true;
uniqueTexts.push(text);
}
}
return uniqueTexts;
},
buildSearchSelect = function(uniqueNames) {
var values=":All";
$.each (uniqueNames, function() {
values += ";" + this + ":" + this;
});
return values;
},
setSearchSelect = function(columnName) {
grid.jqGrid('setColProp', columnName,
{
stype: 'select',
searchoptions: {
value:buildSearchSelect(getUniqueNames(columnName)),
sopt:['eq']
}
}
);
};
grid.jqGrid({
data: mydata,
datatype: 'local',
colModel: [
{ name:'Name', index:'Name', width:200 ,editable:true},
{ name:'Category', index:'Category', width:200,editable:true },
{ name:'Subcategory', index:'Subcategory', width:200,editable:true }
],
sortname: 'Name',
viewrecords: true,
rownumbers: true,
sortorder: "desc",
editurl: "clientArray",
multiselect: true,
pagination:true,
cellEdit: true,
cellsubmit: 'clientArray',
//ignoreCase: true,
pager: '#pager',
height: "auto",
enableSearch: true,
caption: "How to use filterToolbar better locally",
afterSaveCell: function(rowid,name,val,iRow,iCol) {
setSearchSelect(name);
jQuery("#list").('setColProp', name,
{
width:100
}
);
var sgrid = $("#list")[0];
sgrid.triggerToolbar();
alert(name);
},
loadComplete: function () {
setSearchSelect('Category');
}
}).jqGrid('navGrid','#pager',
{edit:false, add:false, del:false, search:false, refresh:true});
setSearchSelect('Category');
setSearchSelect('Subcategory');
grid.jqGrid('setColProp', 'Name',
{
searchoptions: {
sopt:['cn'],
dataInit: function(elem) {
$(elem).autocomplete({
source:getUniqueNames('Name'),
delay:0,
minLength:0
});
}
}
});
grid.jqGrid('filterToolbar',
{stringResult:true, searchOnEnter:true, defaultSearch:"cn"});
function addRow(tableId){
var loopRow = document.getElementById("addRowsInput").value;
var recordCount = '';
var rwData = '';
//var selRowIds = getRowIDs('list');
var gridProducts = $("#list");
var resetFirstRow = jQuery("#list").getRowData( 1 );
jQuery("#list").setRowData( 1, resetFirstRow );
if(loopRow == null || loopRow == "" || loopRow == "Enter number of units to be added")
{
loopRow = 1;
}
for(i=0; i< loopRow; i++)
{
recordCount = jQuery("#list").getGridParam("records") ;
var emptydata = [
{id:(recordCount+1), Name:"", Category:"", Subcategory:""}]
gridProducts.jqGrid('addRowData', recordCount+1, emptydata[0]);
}
}
</script>
#Oleg - one more question on the solution you suggested. Sorry I tried myself to find it but couldn't.
In the buildSearchSelect: method , how can I include filter for empty string.
As explained above I have a "Add Row" button. So when the user wants to filter rows with empty columns I need a filter value.
The implementation of setSearchSelect function from my old answer work only if the searching toolbar not yet exist. If the toolbar exist one have to modify the options of the select element or autocomplete source of the jQuery UI autocomplete widget.
I extended the code. You can see the new version of the demo here. In the same way one could use inline editing instead of the cell editing.
Here is the modified JavaScript code:
var mydata = [
{id:"1", Name:"Miroslav Klose", Category:"sport", Subcategory:"football"},
{id:"2", Name:"Michael Schumacher", Category:"sport", Subcategory:"formula 1"},
{id:"3", Name:"Albert Einstein", Category:"science", Subcategory:"physics"},
{id:"4", Name:"Blaise Pascal", Category:"science", Subcategory:"mathematics"}
],
grid = $("#list"),
getUniqueNames = function(columnName) {
var texts = grid.jqGrid('getCol',columnName), uniqueTexts = [],
textsLength = texts.length, text, textsMap = {}, i;
for (i=0;i<textsLength;i++) {
text = texts[i];
if (text !== undefined && textsMap[text] === undefined) {
// to test whether the texts is unique we place it in the map.
textsMap[text] = true;
uniqueTexts.push(text);
}
}
uniqueTexts.sort();
return uniqueTexts;
},
buildSearchSelect = function(uniqueNames) {
var values=":All";
$.each (uniqueNames, function() {
values += ";" + this + ":" + this;
});
return values;
},
setSearchSelect = function(columnName) {
var $select = $('select#gs_'+columnName), un = getUniqueNames(columnName),
htmlForSelect = '<option value="">All</option>', i, l=un.length, val;
grid.jqGrid('setColProp', columnName,
{
stype: 'select',
searchoptions: {
value:buildSearchSelect(un),
sopt:['eq']
}
}
);
if ($select.length > 0) {
// The searching toolbar already exist. One have to update it manually
for (i=0;i<l;i++) {
val = un[i];
htmlForSelect += '<option value="'+val+'">'+val+'</option>';
}
$select.html(htmlForSelect);
}
},
setAutocomplete = function(columnName) {
var $input = $('input#gs_'+columnName), un = getUniqueNames(columnName);
grid.jqGrid('setColProp', columnName,
{
searchoptions: {
sopt:['cn'],
dataInit: function(elem) {
$(elem).autocomplete({
source:un,
delay:0,
minLength:0
});
}
}
});
if ($input.length > 0) {
// The searching toolbar already exist. One have to update the source
$input.autocomplete('option', 'source', un);
}
},
selectColumns = ['Category','Subcategory'], autocompleteColumns = ['Name'];
grid.jqGrid({
data: mydata,
datatype: 'local',
colModel: [
{ name:'Name', index:'Name', width:200 ,editable:true},
{ name:'Category', index:'Category', width:200,editable:true },
{ name:'Subcategory', index:'Subcategory', width:200,editable:true }
],
sortname: 'Name',
viewrecords: true,
rownumbers: true,
sortorder: "desc",
cellEdit: true,
cellsubmit: 'clientArray',
ignoreCase: true,
pager: '#pager',
height: "auto",
caption: "How to use filterToolbar better locally including local cell editing",
afterSaveCell: function(rowid,name,val,iRow,iCol) {
if ($.inArray(name,selectColumns) !== -1) {
setSearchSelect(name);
} else if ($.inArray(name,autocompleteColumns) !== -1) {
setAutocomplete(name);
}
}
}).jqGrid('navGrid','#pager',
{edit:false, add:false, del:false, search:false, refresh:true});
$.each(selectColumns,function() {
setSearchSelect(String(this));
});
$.each(autocompleteColumns,function() {
setAutocomplete(String(this));
});
grid.jqGrid('filterToolbar',
{stringResult:true, searchOnEnter:true, defaultSearch:"cn"});

Resources