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>
Related
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
Can you please help me to have table sorter with ajax for individual column.
is there anything inbuilt method to call ajax call for filter column?
My source code is below
theme: 'blue',
widthFixed: true,
widgets: [ 'stickyHeaders', 'columnSelector','filter'],
textExtraction: function(node, table, cellIndex){
return $(node).text().replace(/\s'/g,'');
},
headers: {0: { filter: false},6: { filter: false},7: { filter: false},8: { filter: false}},
widgetOptions : {
// Use the $.tablesorter.storage utility to save the most recent filters
filter_saveFilters : false,
// jQuery selector string of an element used to reset the filters
filter_reset : 'button.reset',
// add custom selector elements to the filter row
filter_formatter : {
/*0 : function($cell, indx){
return $.tablesorter.filterFormatter.uiDatepicker( $cell, indx, {
dateFormat: 'dd/mm/yyyy',
changeMonth: true,
changeYear : true
});
},*/
// Alphanumeric (match)
4 : function($cell, indx){
return $.tablesorter.filterFormatter.select2( $cell, indx, {
match : true, // adds "filter-match" to header
// cellText : 'Select: ', // Cell text
width: '90%', // adjusted width to allow for cell text
value: [<%=createdBy%>], // initial values
placeholder: "Search.."
});
},
5 : function($cell, indx){
return $.tablesorter.filterFormatter.select2( $cell, indx, {
match : true, // adds "filter-match" to header
// cellText : 'Select: ', // Cell text
width: '90%', // adjusted width to allow for cell text
value: [<%=actions%>], // initial values
placeholder: "Search.."
});
},
/*7 : function($cell, indx){
return $.tablesorter.filterFormatter.uiDatepicker( $cell, indx, {
// from : '08/01/2013', // default from date
// to : '1/18/2014', // default to date
dateFormat : 'dd/MM/yyyy',
changeMonth : true,
changeYear : true,
cellText : ""
});
}*/
},
// option added in v2.16.0
filter_selectSource : {
// Alphanumeric match (prefix only)
// added as select2 options (you could also use select2 data option)
4 : function(table, column) {
return [<%=createdBy%>];
},
5 : function(table, column) {
return [<%=actions%>];
},
}
/* filter_placeholder : {
from : 'From...',
to : 'To...'
}*/
},
});
Please help me with above thing
thanks in advance.
If you aren't using the pager widget, then you can bind to the filterStart event. But make sure to add a method to not make another ajax call because the filterStart event will fire again after the table updates. Try something like this (this is untested code):
var $table = $('table');
$table
.on('filterBegin', function( event, filters ){
// only trigger an ajax call if the filters have changed
if ( filters.join(',') !== $table.data('lastSearch').join(',') ) {
// do some ajax stuff here
// then in the success callback, save content to table...
// then update
$table
.find('tbody')
.html( newContent )
.trigger('update');
}
})
.tablesorter({
// add your other options here
widgetOptions: {
// you might need to set this option to prevent
// rows from being hidden... maybe!
filter_serversideFiltering : true
},
initialized: function(){
lastSearch = $table.data('lastSearch');
}
});
The grid columns may be resizable. I want to store user-adjusted columns width and restore them when the next session starts.
The best way to store columns width I've found is the following:
var element = $('#grid').kendoGrid({
...
resizable: true,
columnResize: function(e) {
var state = {};
this.columns.every(function(c,i) {
state[c.field] = c.width;
return true;
});
var state_txt = JSON.stringify(state);
localStorage['profile_userprofile_grid_column_width'] = state_txt;
}
}
Now I want to restore column width saved in the previous user session. I can read columns width from the storage:
var state = JSON.parse(localStorage['profile_userprofile_grid_column_width']);
Does somebody know some elegant way to apply these values back to the grid if it is already created at this time? The resize handle does it internally, so it is possible, but the code doing it in the grid source is ugly.
You can trigger the columnResize event post initilisation as shown below
function grid_columnResize(e) {
// Put your code in here
console.log(e.column.field, e.newWidth, e.oldWidth);
}
$("#grid").kendoGrid({
columns: [
{ field: "name" },
{ field: "age" }
],
dataSource: [
{ name: "Jane Doe", age: 30 },
{ name: "John Doe", age: 33 }
],
resizable: true
});
var grid = $("#grid").data("kendoGrid");
grid.bind("columnResize", grid_columnResize);
Documentation
This is an old question, but here is what we have. Function handles column widths and groups.
var _updateResultsGridColumns = function(columns, groups) {
var kendoGrid = $resultsGrid.data("kendoGrid");
if (kendoGrid) {
kendoGrid.setOptions({
columns: columns,
});
var dataSource = kendoGrid.dataSource;
dataSource.group(groups);
kendoGrid.setDataSource(dataSource);
}
}
how can I make one single cell editable out of a not editable column?
my javaScript looks like this:
$( '#grid' ).jqGrid({
// ...
cellEdit : true,
colModel : [
{ name : "id", index : "id", editable : false },
{ name : "wbs", index : "wbs", editable : false },
{ name : "value", index : "value", editable : false }
],
loadComplete : function(data) {
// ... foreach ( cell in data.rows.columns ) ...
if ( cell.shouldBeEditable ) {
jQuery('#grid').setCell(cell.row, cell.col, '', 'green', { editable : true });
}
}
// ...
}
so, after globally setting columns as not editable, I try to set them as editable locally, based on some criteria (to identify them more easily I also paint them green).
Alas, it's not working: cells become green, but when I try to click them they do not become editable.
Inspecting the selected cell with firebug reveals the edit-cell class to be correctly applied.
As a last note, it's working if I set columns as editable in the first instance.
I suggest you do it in reverse. Make the column editable, but disable the cells that you do not want to be editable. This is a function I wrote to disable cells:
// cellName is the name defined in your colModel
function disableGridCell(cellName) {
var cell = $('[name="' + cellName + '"]');
cell.css('display', 'none');
var div = $("<div>")
.css('width', '100%')
.css('height', '100%')
.css('border', '1px solid #000')
.css('background-color', '#CCC')
.text('xxxxxxxxxxxx');
cell.parent().append(div);
}
I call disableGridCell inside of my onEditFunc of my grid's editRow function:
$('#grid').jqGrid('editRow', id, keys, onEditFunc);
function onEditFunc(id) {
if (condition to disable cell) {
disableGridCell('CellName');
}
}
I found a nice workaround to the problem (thanks to Walter for getting me on the right track).
Instead of locking all the cells and selectively unlocking them in a declarative manner (which may lead to long load times), I'm declaring all the cells as editable.
Then I'm attaching a callback to afterEditCell option:
var $grid = $('#grid');
$grid.jqGrid({
// ...
afterEditCell : function(rowid, cellname, value, iRow, iCol) {
if ( shouldNotBeEditable(iRow, iCol) ) {
$grid.setCell(rowid, cellname, '', 'not-editable-cell');
$grid.restoreCell(iRow, iCol);
}
},
// ...
});
This way the cell is immediately reset to its previous value and I ensure that the callback gets called only once per not-to-be-edited cell, since the cell is made into a not-editable-cell after the first call.
colModel:[
{name:'description',index:'description', width:4, sortable: false, editable: true},
{name:'qty',index:'qty', width:1,sortable: false, editable: true},
{name:'cost',index:'cost', width:1, editable: true, sortable: false},
{name:'totalCost',index:'totalCost', width:1, sortable: false}
onCellSelect: function(rowid, iCol, cellcontent, e) {
//here it will allow editing of either qty or cost col for that rowid
//only if both cols have a numeric value already
if(iCol===1 || iCol===2) {
var rowdata = $("#projectTable").getRowData(rowid);
var itemCost = parseInt(rowdata['cost']);
var qty = parseInt(rowdata['qty']);
if(!$.isNumeric(itemCost) || !$.isNumeric(qty)) {
$("#projectTable").jqGrid('setColProp','qty',{editable: false});
$("#projectTable").jqGrid('setColProp','cost',{editable: false});
} else {
$("#projectTable").jqGrid('setColProp','qty',{editable: true});
$("#projectTable").jqGrid('setColProp','cost',{editable: true});
}
}
},
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.