jqgrid: several questions - matrix display - jqgrid

I have data of matrix stored in table as below tables:
MatrixDimensions - MatrixId, NoOfRows, NoOfCol
MatrixValues - MatrixId, RowNo, ColNo, Value
How can I make jqGrid to take no. of rows & columns dynamically
and also display the serialized data in matrix? Is there a direct way or will I have to implement for loops to upload the data in matrix?
Can I display rows as columns and columns as rows (so having column headers vertically aligned)?
Can I enable only inline editing and disable form based editing?

I just wrote the answer to another question where I described how to create the grid with dynamic number of columns (number of rows is always dynamic in jqGrid). It seems to me this way you can display any matrix. In you case you can probably make the code simpler because you can use generic column names like "1", "2", etc. (or "Col. 1", "Col. 2", etc.) and so on.
I modified the code so that it uses array of arrays (matrix) instead of the array on objects with named properties. So jqGrid will looks like this:
or this:
depending on the input JSON data.
The full demo can be found here. The full JavaScript code of the demo you can find below:
var mygrid=jQuery("#list"),
cmTxtTemplate = {
width:40,
align:"center",
sortable:false,
hidden:true
}, currentTemplate = cmTxtTemplate, i,
cm = [], maxCol = 30, dummyColumnNamePrefix = "", //"Col. ",
clearShrinkToFit = function() {
// save the original value of shrinkToFit
var orgShrinkToFit = mygrid.jqGrid('getGridParam','shrinkToFit');
// set shrinkToFit:false to prevent shrinking
// the grid columns after its showing or hiding
mygrid.jqGrid('setGridParam',{shrinkToFit:false});
return orgShrinkToFit;
},
setGridWidthAndRestoreShrinkToFit = function(orgShrinkToFit,width) {
// restore the original value of shrinkToFit
mygrid.jqGrid('setGridParam',{shrinkToFit:orgShrinkToFit});
mygrid.jqGrid('setGridWidth',width);
},
dummyTestRegex = new RegExp(dummyColumnNamePrefix+"(\\d)+"),
counter = 1;
// Add dummy hidden columns. All the columns has the same template
for (i=0;i<maxCol;i++) {
cm.push({name:dummyColumnNamePrefix+(i+1),template:currentTemplate});
}
mygrid.jqGrid({
url:'Matrix1.json',
datatype: "json",
// colNames will be set based on the properties for JSON input
colModel:cm,
height:"auto",
rownumbers:true,
loadonce:true,
gridview: true,
rowNum: 1000,
sortname:"",
jsonReader: {
cell: "",
id: function (obj) {
return "id"+counter++;
},
page: function (obj) {
var rows = obj.rows, colModel = mygrid[0].p.colModel,
cmi, width = 0, iFirstDummy, cols, orgShrinkToFit,
showColNames = [], hideColNames = [];
if (typeof(rows) === "undefined" || !(rows.length>0)) {
// something wrong need return
return obj.page;
}
// find the index of the first dummy column
// in the colModel. If we use rownumbers:true,
// multiselect:true or subGrid:true additional
// columns will be inserted at the begining
// of the colModel
iFirstDummy = -1;
for(i=0;i<colModel.length;i++) {
cmi = colModel[i];
if (dummyTestRegex.test(cmi.name)) {
iFirstDummy = i;
break;
}
}
if (iFirstDummy === -1) {
// something wrong need return
return obj.page;
}
orgShrinkToFit = clearShrinkToFit();
// we get the first row of the JSON data
cols = rows[0].length;
// fill the list of unused columns
for(i=0;i<colModel.length;i++) {
cmi = colModel[i];
if (i<iFirstDummy+cols) {
cmi.width = currentTemplate.width;
showColNames.push(cmi.name);
} else {
hideColNames.push(cmi.name);
}
}
mygrid.jqGrid('showCol',showColNames);
mygrid.jqGrid('hideCol',hideColNames);
setGridWidthAndRestoreShrinkToFit(orgShrinkToFit,
cols*currentTemplate.width);
return obj.page;
}
}
});
$("#readJson1").click(function() {
mygrid.jqGrid('setGridParam',
{datatype:'json',page:1,url:'Matrix1.json'})
.trigger('reloadGrid');
});
$("#readJson2").click(function() {
mygrid.jqGrid('setGridParam',
{datatype:'json',page:1,url:'Matrix2.json'})
.trigger('reloadGrid');
});
The simplest way to transpose the matrix (reflect it over its main diagonal) is on the server. If you can't do this, you can create and fill the transposed matrix inside of page function (see my code above) and just replace the row part of the obj with the transposed matrix.

Related

Kendo UI Grid - Excel Export with hidden columns and custom formatting

I'm attempting to use the Grid component's built-in support for exporting to excel, applying custom cell formatting as shown in these Telerik docs:
http://docs.telerik.com/kendo-ui/controls/data-management/grid/how-to/excel/cell-format
The approach using hard-coded row / cell indexes in the export comes with a rather obvious issue when exporting a grid with a prior hidden column displayed - best way to reproduce is to refer to this jsfiddle:
https://jsfiddle.net/3anqpnqt/1/
Run fiddle
Click export to excel - observe custom number formatting
Unhide subcategory column (using column menu)
Click export to excel - observe custom number formatting on column 2 which is now 'subcategory'
With reference to this code in the fiddle:
$("#grid").kendoGrid({
toolbar: ["excel"],
excel: {
fileName: "Grid.xlsx",
filterable: true
},
columns: [
{ field: "productName" },
{ field: "category" },
{ field: "subcategory", hidden: true },
{ field: "unitPrice"}
],
dataSource: [
{ productName: "Tea", category: "Beverages", subcategory: "Bev1", unitPrice: 1.5 },
{ productName: "Coffee", category: "Beverages", subcategory: "Bev2", unitPrice: 5.332 },
{ productName: "Ham", category: "Food", subcategory: "Food1", unitPrice: -2.3455 },
{ productName: "Bread", category: "Food", subcategory: "Food2", unitPrice: 6 }
],
columnMenu: true,
excelExport: function(e) {
var sheet = e.workbook.sheets[0];
for (var rowIndex = 0; rowIndex < sheet.rows.length; rowIndex++) {
var row = sheet.rows[rowIndex];
var numericFormat = "#,##0.00;[Red](#,##0.00);-";
for (var cellIndex = 0; cellIndex < row.cells.length; cellIndex++) {
var cell = row.cells[cellIndex];
if (row.type === "data") {
if (cellIndex == 2) { // how are we able to identify the column without using indexes?
cell.format = numericFormat;
cell.hAlign = "right";
}
}
}
}
}
});
What I need to be able to do is identify the cell as the 'unitPrice' and apply the format, but inspection of the object model within the excelExport handler doesn't give me any way to make this link. In my real application, I have several custom formats to apply (percentages, n0, n2 etc) so it's not as simple as going $.isNumeric(cell.value) or otherwise.
Update
I also need the solution to work with column / row groups, which generate additional header rows / columns in the Excel model.
It looks like row[0] is the header row, so you could try changing
if (cellIndex == 2) {
to
if (sheet.rows[0].cells[cellIndex].value == "unitPrice") {
EDIT:
Seems to work with column group: https://jsfiddle.net/dwosrs0x/
Update:
The object model for worksheet is not the most clear. The first row does seem to be a "master" header row in the various scenarios that I looked at. Here is something that seems to work if unitPrice is not in a grouping. If unitPrice is in a grouping, then something more complicated involving the group header (row[1]) might be possible. The puzzle is to find out what position the desired column will eventually occupy.
var header = sheet.rows[0];
var upIndex = -1;
var upFound = false;
for (var cellIndex = 0; cellIndex < header.cells.length; cellIndex++) {
if ('colSpan' in header.cells[cellIndex])
upIndex = upIndex + header.cells[cellIndex].colSpan;
else
upIndex = upIndex + 1;
if (header.cells[cellIndex].value == "unitPrice") { // wot we want
upFound = true;
break;
}
}
for (var rowIndex = 0; rowIndex < sheet.rows.length; rowIndex++) {
var row = sheet.rows[rowIndex];
if (row.type === "data" && upFound) {
var cell = row.cells[upIndex];
cell.format = numericFormat;
cell.hAlign = "right";
}
}
fiddle with groups - https://jsfiddle.net/dwosrs0x/4/
fiddle with straightforward grid (to prove it still works) - https://jsfiddle.net/gde4nr0y/1/
This definitely has the whiff of "bodge" about it.

jqgrid select only one row per group

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.

Extjs 4 disable sort if grid is empty

Hello stackoverflow community,
is it somehow possible to disable the sorting mechanism in a grid for every column on a condition? Like...
if the grid hasn't any data loaded,
the sorting should be disabled,
else enabled.
I have the problem that if there is no data and you click on a column to sort, the remote sorting mechanism, will start loading the whole data and sorts it, too ...
This behaviour isn't needed or wished, so I want to disable the sorting possibility.
Your help is appreciated.
Thanks in advance and have a nice day.
This is a bit of a hack, but seems to work OK:
http://jsfiddle.net/q43HC/6/
var data = [{
data1: 'test',
data2: 'test'
}, {
data1: 'test2',
data2: 'test2'
}];
var store = Ext.create('Ext.data.Store', {
fields: ['data1', 'data2'],
data: data
});
Ext.define('SampleGrid', {
extend: 'Ext.grid.Panel',
store: store,
height: 250,
width: 400,
title: 'My Window',
columns: [{
text: 'test',
dataIndex: 'data1'
}, {
text: 'test2',
dataIndex: 'data2'
}],
bbar: [{
text: 'Clear Store',
handler: function() {
store.removeAll();
var grid = this.up('grid'),
cols = grid.query('gridcolumn'),
i = 0,
len = cols.length;
for (; i < len; i++) {
cols[i].doSort(null);
cols[i].__toggleSortState = cols[i].toggleSortState;
cols[i].toggleSortState = function() {};
}
}
}, {
text: 'Reload Store',
handler: function() {
store.loadData(data);
var grid = this.up('grid'),
cols = grid.query('gridcolumn'),
i = 0,
len = cols.length;
for (; i < len; i++) {
if (cols[i].__toggleSortState) {
cols[i].toggleSortState = cols[i].__toggleSortState;
}
}
}
}],
renderTo: Ext.getBody()
});
Ext.onReady(function() {
var grd = new SampleGrid();
});
I am just changing the sort state when the data is removed in order to remove any current sorting then replacing the toggleSortState function with an empty one so clicking the header will not sort the column. When reloading the store I put the function back.. You will have to adapt this to your project, but could create a store aware grid with similar logic.
You can do this globally by overriding the Ext.data.Store.sort method. The system I was working on uses remoteSort and hence hits the server everytime the user clicks the column header to sort, even if the grid itself is empty, pretty much same with your problem.
This is the code that you only need to declare in a single location (ie. your ext-overrides file or commons file), instead of in every single grid:
Ext.define('Ext.overrides.data.Store', {
override: 'Ext.data.Store',
sort: function() {
//prevents the grid from submitting a request if store is empty, for remote sort
if (this.count() > 0) {
this.callParent(arguments);
}
}
});
I have written the following function to achieve the same solution:
function disableColumnSorting(disable){
var grid = Ext.getCmp('billRecordGrid'),
cols = grid.query('gridcolumn'),
colLength = cols.length,
i = 0;
for(i; i < colLength; i++) {
if (disable) {
cols[i].sortable= false;
} else {
cols[i].sortable= true;
}
}
}
Thanks to drew630, you gave me a helpful hint, so I could solve my problem on my own.
If you do not want anything to happen when the grid is empty, you could put this in the store of the grid:
listeners: {
beforeload: function() {
return false when my conditions is met.;
}
}
This way your store will not load and nothing will happen. This is also handy when you have a pager component on the grid and where your server expects e.g. extraParams that are not yet set. If you expect extraParams, and when not yet set, the user clicks the refresh on the pager, you end up with exceptions. But with this check, you can prevent a call to the back-end if conditions are not met.
I sometimes set my api.read URL to undefined, (in cases where the store needs extraParams that are not yet set) and only when those params are available, I also set the URL (with a call in the store itself where the url is already set e.g. activateRead(). In the beforeload, I then test if the api.read is undefined or not.
Hope it helps.
Regards
Lawrende

is there a way to auto adjust widths in ng-grid?

I'm having trouble with widths of my columns. I don't know what width they will be beforehand, nor do I want to specify each column's width. Is there an attribute that I can use that auto adjusts all the column widths based on the content (including the column header)?
Woot! I came up with a pretty cool answer! Setting the width to "auto" was really not working for me. Maybe it is because I'm using an ng-view? Not sure. So I created 2 new functions you can put in your controller.js. Using them like this will give you computed column widths! Read the code comments; there are some caveats.
Example usage, controllers.js:
var colDefs = makeColDefs(rows[0]);
colDefs = autoColWidth(colDefs, rows[0]);
$scope.phones = rows;
$scope.gridOptions = {
data : 'phones',
showFilter : true,
enableColumnResize : true,
columnDefs : colDefs
};
The code, controllers.js:
/**
* Create a colDefs array for use with ng-grid "gridOptions". Pass in an object
* which is a single row of your data!
*/
function makeColDefs(row) {
var colDefs = [];
for ( var colName in row) {
colDefs.push({
'field' : colName
});
}
return colDefs;
}
/**
* Return a colDefs array for use with ng-grid "gridOptions". Work around for
* "auto" width not working in ng-grid. colDefs array will have percentage
* widths added. Pass in an object which is a single row of your data! This
* function does not do typeface width! Use a fixed width font. Pass in an
* existing colDefs array and widths will be added!
*/
function autoColWidth(colDefs, row) {
var totalChars = 0;
for ( var colName in row) {
// Convert numbers to strings here so length will work.
totalChars += (new String(row[colName])).length;
}
colDefs.forEach(function(colDef) {
var numChars = (new String(row[colDef.field])).length;
colDef.width = (numChars / totalChars * 100) + "%";
});
return colDefs;
}
Jasmine test, controllerSpec.js:
'use strict';
var ROW = {
'col1' : 'a',
'col2' : 2,
'col3' : 'cat'
};
var COLUMN_DEF = [ {
field : 'col1'
}, {
field : 'col2'
}, {
field : 'col3'
} ];
var COLUMN_DEF2 = [ {
field : 'col1',
width : '20%'
}, {
field : 'col2',
width : '20%'
}, {
field : 'col3',
width : '60%'
} ];
/* jasmine specs for controllers go here */
describe('controllers', function() {
beforeEach(module('myApp.controllers'));
it('should make an ng-grid columnDef array from a row of data.',
function() {
expect(makeColDefs(ROW)).toEqual(COLUMN_DEF);
});
it('should return an ng-grid columnDef array with widths!', function() {
var colDefs = makeColDefs(ROW);
expect(autoColWidth(colDefs, ROW)).toEqual(COLUMN_DEF2);
});
});
Use ui-grid, is must better: http://ui-grid.info/docs/#/tutorial/204_column_resizing
but https://github.com/angular-ui/ng-grid/wiki/Configuration-Options
This works:
$scope.gridOptions = {
init: function (gridCtrl, gridScope) {
gridScope.$on('ngGridEventData', function () {
$timeout(function () {
angular.forEach(gridScope.columns, function (col) {
gridCtrl.resizeOnData(col);
});
});
});
},
data: 'scopeDataField',
columnDefs: [
{
field: 'property1',
displayName: 'property1title',
width: '100px' // a random fixed size, doesn't work without this
},
{
field: 'property2',
displayName: 'property2title',
width: '100px'
}
],
enableColumnResize : true
};
See answer at https://stackoverflow.com/a/32605748/1688306
Column width calculated by column name or datum, whichever is longer.
Yes! In your columndefs you can set the width to auto and this will automatically size the columns based on the width of the data and/or header.
Reference: Defining Columns
I am using ui-grid. The following code is working for me. You can try this.
First you need to add a module dependency 'ui.grid.autoResize' in your main module.
Then add css class something like this
.grid {
width : 400px !important;
max-width : 400px !important;
height : 600px !important;
}
And finally add the ui-grid directive in your html file like this. Keep in mind don't forgot to add the 'ui-grid-auto-resize' in your grid directive.
<div id="grid1" ui-grid="gridOptions" class="grid" ui-grid-auto-resize></div>

jqgrid get values from combobox inside cell

I'm using select type inside a column , when I'm generating the xml from the grid's data , i can't get the value of the select type cell.
This is my code:
{name:'code',index:'code', width:80, sorttype:"int" , editable:true,edittype:"select",
editoptions:
{
value:"1:11 ;2:22" }
and the xml generating is with:
var dataFromGrid = grid.jqGrid ('getRowData');
var xml = xmlJsonClass.json2xml ({Row: dataFromGrid}, '\t');
I get inside the xml "11" intead of "1".
How can i get the option value?
Thank's In Advance.
I had to do this in local processing mode:
var rows = jQuery(this).getRowData();
var cols = jQuery(this).jqGrid('getGridParam', 'colModel');
for (var col in cols) {
if (cols[col].edittype == 'select') {
var VALs = cols[col].editoptions.value;
if (typeof (VALs) == "object") {
for (var row in rows) {
for (var v in VALs) {
if (rows[row][cols[col].name] == VALs[v]) {
rows[row][cols[col].name] = v;
break;
}
}
}
}
}
}
If you use datatype:"xmlstring" it will be changed to the "local" datatype after the filling of the grid. So like in case of the datatype:"local" the grid has internal data parameter which represent grid data and not the visualization of the current page of data which you receive with
var dataFromGrid = grid.jqGrid ('getRowData');
So I recommend you to use
var dataFromGrid = grid.jqGrid ('getGridParam', 'data');
to get the data from the grid.
if you only need the selected ID in the data, you can specify formatter: 'select'
...
{
name: 'unit', index: 'unit', editable: true, formatter: 'select', edittype: 'select', editrules: { required: true }, editoptions: { value: "1:11 ; 2:22" }
},
...
then retriving the grid data :
var griddata = $('#gridID').getGridParam('data');
alert(JSON.stringify(griddata));

Resources