How to suppress range selection for some columns in ag-grid - javascript

ag-grid has a number of properties: enable*. Columns have a number of properties: suppress*. Setting a suppress* for a column has the effect of disabling the effects of some enable* property on the grid, for that column.
For example:
Docs
Interactive Code Demo on Plunker
var columnDefs = [
{field: 'athlete', suppressMovable: true, width: 150, cellClass: 'suppress-movable-col'},
{field: 'age', lockPosition: true, cellClass: 'locked-col'},
{field: 'country', width: 150}
];
var gridOptions = {
suppressDragLeaveHidesColumns: true,
columnDefs: columnDefs,
defaultColDef: {
width: 100
}
};
In the above example, the 'athlete' column is not movable due to suppressMovable:true. All of the other columns are movable.
I have a grid with enableRangeSelection: true
Docs
Interactive Code Demo on Plunker
I would like to prevent the first column from being included in a range selection.
However, no column property exists called suppressRangeSelection.
How can I prevent the user from including the first column in range?

Does not seem like ag-grid allows such behavior, but I managed to do so using Range Selection API:
var gridOptions = {
columnDefs: columnDefs,
enableRangeSelection: true,
rowData: null,
onRangeSelectionChanged: event => {
var cellRanges = event.api.getCellRanges();
if (!cellRanges || cellRanges.length === 0) return;
var excludeColumn = cellRanges[0].columns.find(
el => el.getColId() === 'athlete'
);
if (!excludeColumn) return;
var rangeParams = {
rowStartIndex: cellRanges[0].startRow.rowIndex,
rowStartPinned: cellRanges[0].startRow.rowPinned,
rowEndIndex: cellRanges[0].endRow.rowIndex,
rowEndPinned: cellRanges[0].endRow.rowPinned,
columns: cellRanges[0].columns
.map(el => el.getColId())
.filter(el => el !== 'athlete'),
};
event.api.clearRangeSelection();
event.api.addCellRange(rangeParams);
},
};

Related

How to add prop/value dynamically to kendo columns

After initialization
<div kendo-grid="grid" k-options="vm.gridOptions" k-columns="vm.columns" ng-show="vm.filtered"></div>
JS:
vm.columns = [
{
field: 'client',
title: 'Client',
attributes: { 'class': 'k-gridRow' }
},
{
field: 'total',
title: 'Total',
attributes: { 'class': 'text-right' },
format: '{0:n0}',
width: 110
............
I need to add/change to column total footerTemplate with new values after some events on page
I tried vm.columns[3]['footerTemplate'] = '2333'; but it is not working.
Help please , maybe reinit for this column only at least, this bad variant though
You have to use setOptions() method to change any widget options after initialization. Try this:
var grid = $("#grid").data("kendoGrid");
var columns = grid.getOptions().columns; // Get the columns from getOptions()
// Change whatever you like
columns[1].footerTemplate = "Min: (#: min #) - Max: (#: max #)";
// Set only columns options again
grid.setOptions({
columns: columns
});
Demo

Deleting jqGrid rows with local data results in unclickable cells

I'm having a problem with my jqGrid.
It is using local data
It is setup to allow inline (cell editing)
When I delete a row (locally only) using the delRowData method, the row is deleted as expected. However when I delete a couple of rows, the inline cell editing seems to stop working.
When this happens I don't see any errors in the chrome debug window. I've setup a fiddle here
Here is the grid definition
$("#grid").jqGrid({
datatype: "local",
height: 250,
colNames: ['Inv No', 'Thingy', 'Blank', 'Number', 'Status'],
colModel: [{
name: 'id',
index: 'id',
width: 60,
sorttype: "int",
editable : false
}, {
name: 'thingy',
index: 'thingy',
width: 90,
sorttype: "date",
editable : true,
edittype : 'text'
}, {
name: 'blank',
index: 'blank',
width: 30,
editable : true,
edittype : 'text'
}, {
name: 'number',
index: 'number',
width: 80,
sorttype: "float",
editable : true,
edittype : 'text'
}, {
name: 'status',
index: 'status',
width: 80,
sorttype: "float",
editable : true,
edittype : 'text'
}],
caption: "Stack Overflow Example",
cellEdit : true,
cellsubmit : 'clientArray'
});
Here is my delete method. I've made it as simple as I can. It just always deletes the first row.
function deleteRow() {
var ids = $("#grid").jqGrid('getDataIDs');
$("#grid").jqGrid('delRowData', ids[0]);
}
The problem is the following: cell editing of jqGrid are written so that it works mostly with row indexes (see here) instead of rowids. Some internal structures of jqGrid (savedRows and iRow options) and the id of editable cell will be assigned with the value which constructed based on the index of editable row. So after deleting of a row in the grid the information need be updated.
I modified your jsfiddle demo to the following: http://jsfiddle.net/OlegKi/wdwdxLuk/14/. It uses the following code of deleteRow called if one clicks on the button "Delete the first Row of the grid":
function deleteRow() {
var $grid = $("#grid"), editingTr, rows, iRow, editingColumnName,
p = $grid.jqGrid("getGridParam"), // get reference to object with jqGrid options
savedRows = p.savedRow, //{id:iRow, ic:iCol, name:nm, v:cellData};
colModel = p.colModel;
if ($grid.length > 0 && $grid[0].rows.length > 1) {
rows = $grid[0].rows;
editingTr = savedRows.length > 0 ? rows[savedRows[0].id] : undefined;
// delete the row
$("#grid").jqGrid('delRowData', rows[1].id); // delete the first row (rows[0] don't contains any data)
if (editingTr !== undefined) {
// update the index of the editing row
iRow = editingTr.rowIndex;
if (iRow < 0) {
// editing row way deleted from the grid
p.savedRow = [];
delete p.iRow;
delete p.iCol;
} else {
// update the row index in savedRows
editingColumnName = colModel[savedRows[0].ic].name;
$(editingTr).find("#" + savedRows[0].id + "_" + $.jgrid.jqID(editingColumnName))
.attr("id", iRow + "_" + editingColumnName);
savedRows[0].id = iRow;
// update row index of selected row
p.iRow = iRow;
}
}
}
}

Render dynamic components in ExtJS 4 GridPanel Column with Ext.create

I've got an ExtJS (4.0.7) GridPanel that I'm populating from a store. The values that I display in the GridPanel's column need to have a different view depending on the type of data that's in the record.
The ultimate goal is that records with "double" or "integer" value for the record's type property present a slider to the user that they can adjust, and a type of "string" just renders some read-only text.
I've created a custom Column to do this. It inspects the type in the renderer and determines what to render.
I've got the "string" working fine with the code below, but struggling with how I can dynamically create and render the more complicated slider control in the column.
This simplified example is just trying to render a Panel with a date control in it as if I can get that going, I can figure out the rest of the slider stuff.
Ext.define('MyApp.view.MyColumn', {
extend: 'Ext.grid.column.Column',
alias: ['widget.mycolumn'],
stringTemplate: new Ext.XTemplate('code to render {name} for string items'),
constructor: function(cfg){
var me = this;
me.callParent(arguments);
me.renderer = function(value, p, record) {
var data = Ext.apply({}, record.data, record.getAssociatedData());
if (data.type == "string") {
return me.renderStringFilter(data);
} else if (data.type == "double" || data.type == "integer") {
return me.renderNumericFilter(data);
} else {
log("Unknown data.type", data);
};
},
renderStringFilter: function(data) {
// this works great and does what I want
return this.stringTemplate.apply(data);
},
renderNumericFilter: function(data) {
// ***** How do I get a component I "create" to render
// ***** in it's appropriate position in the gridpanel?
// what I really want here is a slider with full behavior
// this is a placeholder for just trying to "create" something to render
var filterPanel = Ext.create('Ext.panel.Panel', {
title: 'Filters',
items: [{
xtype: 'datefield',
fieldLabel: 'date'
}],
renderTo: Ext.getBody() // this doesn't work
});
return filterPanel.html; // this doesn't work
}
});
My problem really is, how can I Ext.create a component, and have it render into a column in the gridpanel?
There are a few ways that I have seen this accomplished. Since the grid column is not an Ext container it can not have Ext components as children as part of any configuration the way other container components can. Post grid-rendering logic is required to add Ext components to cells.
This solution modifies your custom column render so that it puts a special css class on the rendered TD tag. After the grid view is ready, the records are traversed and the custom class is found for appropriate special columns. A slider is rendered to each column found.
The code below is a modified version of the ext js array grid example provided in the Sencha examples. The modification mixes in the custom column renderer and the post grid rendering of sliders to TD elements.
This example only includes enough modification of the Sencha example to show the implementation ideas. It lacks separated view and controller logic.
This is modified from here
Ext.require([
'Ext.grid.*',
'Ext.data.*',
'Ext.util.*',
'Ext.data.Model'
]);
Ext.onReady(function() {
// sample static data for the store
Ext.define('Company', {
extend: 'Ext.data.Model',
fields: ['name', 'price', 'change', 'pctChange', 'lastUpdated', 'type']
});
var myData = [
['3m Co', 71.72, 2, 0.03, '9/1/2011', 'integer'],
['Alcoa Inc', 29.01, 4, 1.47, '9/1/2011', 'string'],
['Altria Group Inc', 83.81, 6, 0.34, '9/1/2011', 'string'],
['American Express Company', 52.55, 8, 0.02, '9/1/2011', 'string'],
['American International Group, Inc.', 64.13, 2, 0.49, '9/1/2011', 'integer'],
['AT&T Inc.', 31.61, 4, -1.54, '9/1/2011', 'integer'],
['Boeing Co.', 75.43, 6, 0.71, '9/1/2011', 'string'],
['Caterpillar Inc.', 67.27, 8, 1.39, '9/1/2011', 'integer'],
['Citigroup, Inc.', 49.37, 1, 0.04, '9/1/2011', 'integer'],
['E.I. du Pont de Nemours and Company', 40.48, 3, 1.28, '9/1/2011', 'integer'],
['Exxon Mobil Corp', 68.1, 0, -0.64, '9/1/2011', 'integer'],
['General Electric Company', 34.14, 7, -0.23, '9/1/2011', 'integer']
];
// create the data store
var store = Ext.create('Ext.data.ArrayStore', {
model: 'Company',
data: myData
});
// existing template
stringTemplate = new Ext.XTemplate('code to render {name} for string items');
// custom column renderer
specialRender = function(value, metadata, record) {
var data;
data = Ext.apply({}, record.data, record.getAssociatedData());
if (data.type == "string") {
return stringTemplate.apply(data);;
} else if (data.type == "double" || data.type == "integer") {
// add a css selector to the td html class attribute we can use it after grid is ready to render the slider
metadata.tdCls = metadata.tdCls + 'slider-target';
return '';
} else {
return ("Unknown data.type");
}
};
// create the Grid
grid = Ext.create('Ext.grid.Panel', {
rowsWithSliders: {},
store: store,
stateful: true,
stateId: 'stateGrid',
columns: [{
text: 'Company',
flex: 1,
sortable: false,
dataIndex: 'name'
}, {
text: 'Price',
width: 75,
sortable: true,
renderer: 'usMoney',
dataIndex: 'price'
}, {
text: 'Change',
width: 75,
sortable: true,
dataIndex: 'change',
renderer: specialRender,
width: 200
}, {
text: '% Change',
width: 75,
sortable: true,
dataIndex: 'pctChange'
}, {
text: 'Last Updated',
width: 85,
sortable: true,
renderer: Ext.util.Format.dateRenderer('m/d/Y'),
dataIndex: 'lastUpdated'
}],
height: 350,
width: 600,
title: 'Irm Grid Example',
renderTo: 'grid-example',
viewConfig: {
stripeRows: true
}
});
/**
* when the grid view is ready this method will find slider columns and render the slider to them
*/
onGridViewReady = function() {
var recordIdx,
colVal,
colEl;
for (recordIdx = 0; recordIdx < grid.store.getCount(); recordIdx++) {
record = grid.store.getAt(recordIdx);
sliderHolder = Ext.DomQuery.select('.slider-target', grid.view.getNode(recordIdx));
if (sliderHolder.length) {
colEl = sliderHolder[0];
// remove div generated by grid template - alternative is to use a new template in the col
colEl.innerHTML = '';
// get the value to be used in the slider from the record and column
colVal = record.get('change');
// render the slider - pass in the full record in case record data may be needed by change handlers
renderNumericFilter(colEl, colVal, record)
}
}
}
// when the grids view is ready, render sliders to it
grid.on('viewready', onGridViewReady, this);
// modification of existing method but removed from custom column
renderNumericFilter = function(el, val, record) {
var filterPanel = Ext.widget('slider', {
width: 200,
value: val,
record: record,
minValue: 0,
maxValue: 10,
renderTo: el
});
}
});
I did something like this when I needed to render a small chart (essentially a spark chart) in a grid column. This solution is similar to sha's, but it's more robust and delegates the rendering to the component being rendered rather than the Column, which doesn't really have a render chain.
First, the column class:
Ext.define("MyApp.view.Column", {
extend: "Ext.grid.column.Column",
// ...
renderer: function (value, p, record) {
var container_id = Ext.id(),
container = '<div id="' + container_id + '"></div>';
Ext.create("MyApp.view.Chart", {
type: "column",
// ...
delayedRenderTo: container_id
});
return container;
}
});
Note the delayedRenderTo config option. Just like renderTo, this will be the DOM ID of the element that the chart component will render to, except that it doesn't need to be present in the DOM at the time of creation.
Then the component class:
Ext.define("MyApp.view.Chart", {
extend: "Ext.chart.Chart",
// ...
initComponent: function () {
if (this.delayedRenderTo) {
this.delayRender();
}
this.callParent();
},
delayRender: function () {
Ext.TaskManager.start({
scope: this,
interval: 100,
run: function () {
var container = Ext.fly(this.delayedRenderTo);
if (container) {
this.render(container);
return false;
} else {
return true;
}
}
});
}
});
So during initComponent(), we check for delayed render and prepare that if necessary. Otherwise, it renders as normal.
The delayRender() function itself schedules a task to check every so often (100ms in this case) for the existence of an element with the given ID — i.e., to check whether the column has rendered. If not, returns true to reschedule the task. If so, renders the component and returns false to cancel the task.
We've had good luck with this in the field, so I hope it works for you too.
By the way, I was developing this as a part of answering my own question about ExtJS charting. That thread has the results of my performance testing. I was rendering 168 chart components in grid columns in 3-4s across most browsers and OSes. I imagine your sliders would render much faster than that.
Try something like this:
renderNumericFilter: function () {
var id = Ext.id();
Ext.defer(function () {
Ext.widget('slider', {
renderTo: id,
width: 200,
value: 50,
increment: 10,
minValue: 0,
maxValue: 100,
});
}, 50);
return Ext.String.format('<div id="{0}"></div>', id);
}
But I must say whatever you're trying to do - it doesn't sound right :) I don't think a bunch of sliders inside the grid will look good to the user.

custom formatter for grouptext in jqgrid

Is there a way I can use custom formatter to format the grouptext value in jqgrid.
Current Output for var grouping=new Array("Environment", "System", "IIR","Userlimit","Kernel Parameters");
Suppose I have these groups.
var grouping=new Array("3Environment", "0System", "4IIR","2Userlimit","1Kernel Parameters");
If I sort it in ascending order it should display System, Kernel, Userlimit, Environment, IIR i.e., using some kind of custom formatter remove 01234 from 1st character or similar to sort my groups in some already decided order.
jqGrid Code
$('#compareContent').empty();
$('<div id="compareParentDiv" width="100%">'+
'<table id="list2" cellspacing="0" cellpadding="0"></table>'+
'<div id="gridpager3"></div></div>')
.appendTo('#compareContent');
$("#compareParentDiv").hide();
var gridDiff = $("#list2");
gridDiff.jqGrid({
datastr: compareData,
datatype: "jsonstring",
colNames: ['KeyName', 'SubCategory', starheader, header1,'isEqual'],
colModel: [
{ name: 'elementName', index: 'elementName', key: true, width: 120 },
{ name: 'subCategory', index: 'subCategory', width: 1 },
{ name: 'firstValue', index: 'firstValue', width: 310, jsonmap:'attribute.0.firstValue' },
{ name: 'secondValue', index: 'secondValue', width: 310,jsonmap:'attribute.0.secondValue' },
{ name: 'isEqual', index: 'isEqual', width: 1,hidden:true}
],
pager: '#gridpager3',
rowNum:60,
scrollOffset:1,
//viewrecords: true,
jsonReader: {
repeatitems: false,
page: function(){return 1;},
root: "response"
},
//rownumbers: true,
height: '320',
autowidth:true,
grouping: true,
groupingView: {
groupField: ['subCategory'],
groupOrder: ['desc'],
groupDataSorted : false,
groupColumnShow: [false],
//groupCollapse: true,
groupText: ['<b>{0}</b>']
},
loadComplete: function() {
if (this.p.datatype !== 'local') {
setTimeout(function () {
gridDiff.trigger('reloadGrid');
}, 0);
} else {
$("#compareParentDiv").show();
}
var i, names=this.p.groupingView.sortnames[0], l = names.length;
data = this.p.data, rows = this.rows, item;
for (i=0;i<l;i++) {
if ($.inArray(names[i],grouping) >= 0) {
$(this).jqGrid('groupingToggle',this.id+"ghead_"+i);
$(rows.namedItem(this.id+"ghead_"+i)).find("span.ui-icon").click(function(){
var len = data.length, iRow;
for (iRow=0;iRow<len;iRow++) {
item = data[iRow];
if (item.isEqual) {
$(rows.namedItem(item._id_)).hide();
}
}
});
} else {
// hide the grouping row
$('#'+this.id+"ghead_"+i).hide();
}
//console.info($('#'+this.id+"ghead_"+i));
}
var i, names=this.p.groupingView.sortnames[0], l = names.length,
data = this.p.data, rows = this.rows, item;
l = data.length;
for (i=0;i<l;i++) {
item = data[i];
if (!item.isEqual) {
$(rows.namedItem(item._id_))
.css({
"background-color": "#FFE3EA",
"background-image": "none"
});
} else {
$(rows.namedItem(item._id_)).hide();
}
}
}
});
gridDiff.jqGrid('navGrid', '#gridpager3', { add: false, edit: false, del: false, search: false, refresh: false });
gridDiff.jqGrid('navButtonAdd',"#gridpager3",{caption:"Toggle",title:"Toggle Search Toolbar", buttonicon :'ui-icon-pin-s',
onClickButton:function(){
gridDiff[0].toggleToolbar();
}
});
gridDiff.jqGrid('navButtonAdd',"#gridpager3",{caption:"Clear",title:"Clear Search",buttonicon :'ui-icon-refresh',
onClickButton:function(){
gridDiff[0].clearToolbar();
}
});
gridDiff.jqGrid('filterToolbar',
{stringResult: true, searchOnEnter: false, defaultSearch: 'cn'});
Update
Or is there a way to use positioning to place each grouped item, if sorting is not an option
Updated after accepting answer by Oleg
Page1
Page3
It seems to me that you have not the problem with the custom formatter of the grouptext
You use groupingView having groupDataSorted: false so the sorting of the group (in your case groupField: ['subCategory'] with groupOrder: ['asc']) do jqGrid for you. So the standard sorting behavior will be used.
jqGrid supports sorttype property of the colModel which define how the column should be sorted. If you need custom sorting order you should define sorttype property as a function which returns integer or string used instead of the cell value from the column. the prototype of the sorttype function could be function(cellValue,rowData) so it is possible to define the order which not only the cell value of the sorted column will be used but the whole contain of the row inclusive _id_ property. In you case the usage of the first cellValue parameter would be enough.
So to solve you problem you can for example define an array with the order of 'subCategory' values which you need:
var orderOfSubCategory = [
"System", "system", "Kernel", "Kernel Parameters", "Userlimit",
"Environment", "IIR", "Product"
];
and then define 'subCategory' column as following:
{
name: 'subCategory',
index: 'subCategory',
width: 1,
sorttype: function (value) {
return $.inArray(value, orderOfSubCategory);
}
}
You should don't forget, that jQuery.inArray return 0-based index of the item or -1 if the item will be not found in the array. So the values of 'subCategory' which could be not found in the orderOfSubCategory array will be placed the first. See here the corresponding demo.
Some other small remarks about your code. You use new Array(...) which is bad practice in JavaScript instead of that you should use always [...] construct which do the same is shorter and work quickly. Example: var grouping = ["Environment", "System", "IIR","Userlimit","Kernel Parameters"];
Another place which is difficult for to read for me is:
$('#compareContent').empty();
$('<div id="compareParentDiv" width="100%">'+
'<table id="list2" cellspacing="0" cellpadding="0"></table>'+
'<div id="gridpager3"></div></div>')
.appendTo('#compareContent');
$("#compareParentDiv").hide();
Here you first use cellspacing="0" cellpadding="0" attributes of the table which are unneeded and seconds use empty(), append() combination instead of one html() call. The following code do the same, but it seems me better:
$('#compareContent').html(
'<div id="compareParentDiv" style="display: none" width="100%">' +
'<table id="list2"></table><div id="gridpager3"></div></div>');

Using jQuery, how to check whether a table cell is empty or not?

Using jQuery, how to check whether a table cell is empty or not?
Please help me.
What do you mean by empty.
//cell maycontain new lines,spaces,&npsp;,... but no real text or other element
$("cellselector").text().trim()=="";
or
//cell has no child elements at all not even text e.g. <td></td>
$("cellselector:empty")
You can use CSS selectors with the $ function to get a reference to the cell's element, and then use the html function to see whether it has any contents. For instance, if the cell has an ID "foo":
if ($("#foo").html()) {
// The cell has stuff in it
}
else {
// The cell is empty
}
Try this:
$content = $('#your_cell_id_here').html();
if($content == '')
{
// yes it is empty
}
else
{
// no it is not empty
}
You can use the trechnique I posted here
http://www.keithrull.com/2010/06/09/HowToChangeTableCellColorDependingOnItsValueUsingJQuery.aspx
You can use this if you already know the id of the cell you want to check
$valueOfCell = $('#yourcellid').html();
or you can iterate on the cells of the table
$("#yourtablename tr:not(:first)").each(function() {
//get the value of the table cell located
//in the third column of the current row
var valueOfCell = $(this).find("td:nth-child(3)").html();
//check if its greater than zero
if (valueOfCell == ''){
//place action here
}
else{
//place action here
}
});
You could add css classes to your table and its rows and columns, then use jquery selectors to get the table item:
if ( !($('#mytable tr.row-i td.column-j').html()) ) { alert("empty"); }
for datatables, you may use the following
var missedClockoutDataTable = $("#missedClockoutsDatatable").DataTable({
"order": [[0, "asc"]],
"serverSide": true,
"processing": true,
responsive: true,
destroy: true,
select: true,
fixedHeader: {
header: true,
headerOffset: $('#fixed').height()
},
buttons: [
{extend: 'copy', className: 'ui button'},
{extend: 'csv', className: 'ui button'},
{extend: 'excel', className: 'ui button'},
{extend: 'pdf', className: 'ui button'},
],
dom:
"<'row'<'col-sm-3'l><'col-sm-6 text-center'B><'col-sm-3'f>>" +
"<'row'<'col-sm-12'tr>>" +
"<'row'<'col-sm-5'i><'col-sm-7'p>>",
"ajax": {
"url": "{{url('workplace/dashboard/employees/missed-clockouts')}}",
data: function (d) {
d.gender = $("#gender").val();
d.roles = $("#roles").val();
},
},
"columns": [
{data: 'full_name', name: 'full_name'},
{data: 'clock', name: 'clock'},
{data: 'in', name: 'in'},
{data: 'out', name: 'out'},
{data: 'numberofHours', name: 'numberofHours'},
{data: 'clockoutReason', name: 'clockoutReason'},
{data: 'action', name: 'action'},
],
//when the table has fully loaded, proceed with looping through each row except the first one and then delete the rows if the IN column cell is empty
"initComplete": function(settings, json) {
// if the in type is empty, then hide that row
$("#missedClockoutsDatatable tr:not(:first)").each(function() {
//get the value of the table cell located
//in the third column of the current row
var valueOfCell = $(this).find("td:nth-child(3)").html();
//check if its greater than zero
if (valueOfCell == ''){
console.log(222);
this.remove();
}
});
}
});

Categories