I'm trying to recreate the following pivot table with ag-Grid:
Using floatingBottomRowData I can't find a way to pass the values for all generated columns. so one pair of values is getting duplicated:
My goal is to access the column group value for each cell of the floating row.
Right now I can't tell the difference between column groups (resulting in duplicated values).
Is there any way to differentiate the different (col1, col2) pairs?
The answer that was given in the ag-grid forum is:
to get the pivot key, do this:
floatingCellRenderer: function(params) {
if (gridOptions.columnApi.isPivotMode()) {
console.log(params.column.getId());
var parent = params.column.getParent();
if (parent) {
var pivotKey = console.log(parent.getOriginalColumnGroup().getColGroupDef().pivotKeys[0]);
}
}
}
to get the columns in the active pivot, use the column API, eg columnApi.getPivotColumns(), which will return a list of the pivot columns. this will be in order, so if you have many columns, there will be one for each parent you traverse.
What I did for the pivot_ type of pinned row: I added a custom pinned row renderer to my column, where I expect the aggregated function to show (for me it was for sum and avg). If I change the pivot function I called a refresh on the pinned row to set the new values.
pinnedRowCellRenderer: function (render)
{
for (var obj_id in render.data)
{
if (obj_id == render.column.colId)
{
return '<div>' + render.data[obj_id] + '</div>';
}
}
return '<div></div>';
}
And the result looks like this:
Result image
Hope it helps!
Related
I have an ag-grid table with 2 columns - one text and one number.
I need to add a custom row to the top of my ag-grid table with the total value of the number column and the word "ALL" in the text column.
This needs to be on the top. And should not change its position even if the table is sorted.
Also, the row should be selectable.
Any help in this regard will be highly appreciated!
Sounds like you are describing Row Pinning which is already a feature in AG Grid. However, since you've also stated that row selection is a requirement, this will not be possible with Row Pinning as it's not supported.
Instead what I'd recommend is adding an extra row object inside the rowData for the custom row, and handling the update of the number column and the custom row position yourself when necessary:
If you want to handle the total value of the number column then you can use the following logic:
function updateTotalRowNode() {
let totalRowNode;
let sum = 0;
gridOptions.api.forEachNode((node) => {
if (node.data.totalRow) {
totalRowNode = node;
} else {
sum += node.data.gold;
}
});
totalRowNode.setDataValue('gold', sum);
}
If you want to keep the custom row always on the top row then you can implement the postSort callback:
postSort: (rowNodes) => {
// here we put All row on top while preserving the sort order
let nextInsertPos = 0;
for (let i = 0; i < rowNodes.length; i++) {
const athlete = rowNodes[i].data.athlete;
if (athlete === 'All') {
rowNodes.splice(nextInsertPos, 0, rowNodes.splice(i, 1)[0]);
nextInsertPos++;
}
}
},
See the following plunker for the implementation.
I have many sheets in my spreadsheet (sheet1,sheet2,sheet3...) and I want to add them all to array, maybe based on any call range? Now I add them manually as below:
=query(
{
INDIRECT("sheet1!$A$3:$V");
INDIRECT("sheet2!$A$3:$V");
INDIRECT("sheet3!$A$3:$V") };
"SELECT Col2, Col3, Col4, ...[etc]")
I want to create any "Settings" sheet and put here all sheets that should be in array, like this:
=query(
{
get_all_sheets_names_from('settings!A1:A100'); // something like this
};
"SELECT Col2, Col3, Col4, ...[etc]")
Is it possible?
My attempts:
https://docs.google.com/spreadsheets/d/1ZMzu6FuVyAJiWfNIHW87OW1Vpg_mM_QdtGs9nq9UXCU/edit#gid=0
I would like the array with data sources to be taken from the G2:G column.
The example in column C shows how this can be done manually. However, I am looking for a solution so that in the query nothing has to be done so that the query can drag an array with the names of the data source from G2:G
1) What i Think
I think it is not possible to use "INDIRECT" in the query parameters, because "INDIRECT" returns a cell reference and the parameters {(); ()} in a query are fixed objects.
An "INDIRECT" on a complete query is not possible either, for the same reason: a query does not return a reference on a cell.
2) Limited soluce
the principle: case1: look in column G the 3rd line (3rd source), if empty then test case 2, otherwise apply the formula with 3 sources.
case 2: if 2nd source is empty then go to case 1, otherwise apply the formula with 2 sources
case 1: if empty then display "no sources" otherwise apply formula with 1 source
Formula
note 1 replace ESTVIDE (fr) by ISBLANK (eng) !!
note 2 : you can test with (G2="source1" and G3="source2),
but it works with G2="source3" and G3="source1"
=SI(ESTVIDE($G$4); SI(ESTVIDE($G$3); SI(ESTVIDE($G$1); "no source(s)";query({((INDIRECT("'"&G2&"'!A1:A5")))};"SELECT Col1")) ;query({(INDIRECT("'"&G2&"'!A1:A5"));(INDIRECT("'"&G3&"'!A1:A5"))};"SELECT Col1")) ;query({(INDIRECT("'"&G2&"'!A1:A5"));(INDIRECT("'"&G3&"'!A1:A5"));(INDIRECT("'"&G4&"'!A1:A5"))};"SELECT Col1"))
Online sheet
https://docs.google.com/spreadsheets/d/1sCwwFjpYKKzzAvVwmbMUWcmHSc1wY52XnHlFdT00A3U/edit?usp=sharing
Limitations
Off course, this is a formula with only 3 sources max !
It will be verry big and uggly with more sources...
Script
macro is the only solution ?
soluce with Macro
append this script,
it gets value sources values from G2:G30 (you need more...put G100..)
it create the formula and put it on H2
it read max 50 value in each source (see A1:A50 in source code)
it's not so hard to understand,
note : managing macro with GSheet is a another problem, if you needs advices, please post a comment.
link to live sheet :
https://docs.google.com/spreadsheets/d/14XaR-UsADUpCUCVWqeg0zCbfGy3CCvnwVxUhozjYocc/edit?usp=sharing
function formula6() {
var spreadsheet = SpreadsheetApp.getActive();
var values=spreadsheet.getRange('G2:G30').getValues();
var acSources="{";
for (var i = 0; (i < values.length) && (values[i]!=""); i++) {
if (i>0) { acSources+=";" }
acSources=acSources+'INDIRECT("'+values[i]+'!A1:A50")';
}
acSources=acSources+"}";
var formula='query('+acSources+';"SELECT Col1")';
spreadsheet.getRange('H2').activate();
spreadsheet.getCurrentCell().setFormula('='+formula);
};
dudes who copy-pasted INDIRECT function into Google Sheets completely failed to understand the potential of it and therefore they made zero effort to improve upon it and cover the obvious logic which is crucial in this age of arrays.
in other words, INDIRECT can't intake more than one array:
=INDIRECT("Sheet1!A:B"; "Sheet2!A:B")
nor convert an arrayed string into active reference, which means that any attempt of concatenation is also futile:
=INDIRECT(MasterSheet!A1:A10)
————————————————————————————————————————————————————————————————————————————————————
=INDIRECT("{Sheet1!A:B; Sheet2!A:B}")
————————————————————————————————————————————————————————————————————————————————————
={INDIRECT("Sheet1!A:B"; "Sheet2!A:B")}
————————————————————————————————————————————————————————————————————————————————————
=INDIRECT("{INDIRECT("Sheet1!A:B"); INDIRECT("Sheet2!A:B")}")
the only possible way is to use INDIRECT for each end every range like:
={INDIRECT("Sheet1!A:B"); INDIRECT("Sheet2!A:B")}
which means that the best you can do is to pre-program your array like this if only part of the sheets/tabs is existant (let's have a scenario where only 2 sheets are created from a total of 4):
=QUERY(
{IFERROR(INDIRECT("Sheet1!A1:B5"), {"",""});
IFERROR(INDIRECT("Sheet2!A1:B5"), {"",""});
IFERROR(INDIRECT("Sheet3!A1:B5"), {"",""});
IFERROR(INDIRECT("Sheet4!A1:B5"), {"",""})},
"where Col1 is not null", 0)
so, even if sheet names are predictable (which not always are) to pre-program 100+ sheets like this would be painful (even if there are various sneaky ways how to write such formula under 30 seconds)
an alternative would be to use a script to convert string and inject it as the formula
A1 would be formula that creates a string that looks like real formula:
=ARRAYFORMULA("=QUERY({"&TEXTJOIN("; "; 1;
FILTER(SNAME(1); SNAME(1)<>SNAME(0))&"!A1:A20")&"}; ""where Col1 is not null""; 0)")
then this script will take the string from A1 cell and it will paste it as valid formula into A2 cell:
function onEdit() {
var sheet = SpreadsheetApp.getActive().getSheetByName('query'); // MASTER SHEET NAME
var src = sheet.getRange("A1"); // COPY STRING FROM
var str = src.getValue();
var cell = sheet.getRange("A2"); // PASTE AS FORMULA INTO
cell.setFormula(str);
}
function SNAME(option) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet()
var thisSheet = sheet.getName();
if(option === 0){ // ACTIVE SHEET NAME =SNAME(0)
return thisSheet;
}else if(option === 1){ // ALL SHEET NAMES =SNAME(1)
var sheetList = [];
ss.getSheets().forEach(function(val){
sheetList.push(val.getName())
});
return sheetList;
}else if(option === 2){ // SPREADSHEET NAME =SNAME(2)
return ss.getName();
}else{
return "#N/A"; // ERROR MESSAGE
};
}
of course, the script can be changed to onOpen trigger or with custom name triggered from the custom menu or via button (however it's not possible to use the custom function as formula directly)
this will cover all your needs to not edit the formula by adding references if new sheets are added. the only drawback is a recalculation of sheet name script... to do so you need to dismantle A1 formula for example by adding ' before the leading = pressing enter and then removing it to fix the formula
spreadsheet demo
I have a Table with 3 columns, the left 2 are fixed-width. I am trying to make the third column fill the whole remaining table.
I have tried:
logTable.getTableColumnModel().set(2, {flex: 1});
But this did nothing to the column width.
Thanks in advance!
The default table column model does not allow that, but the Resize table column model, `qx.ui.table.columnmodel.Resize, does. You can do something like this:
var table = new qx.ui.table.Table(
tableModel, // previously defined, with 3 columns
{
// We want to handle the behavior of table column resizing
tableColumnModel : function(obj)
{
return new qx.ui.table.columnmodel.Resize(obj);
}
});
// Use the Resize table column model to allow final column to consume
// remainder of space
var tcm = table.getTableColumnModel();
var resizeBehavior = tcm.getBehavior();
resizeBehavior.setWidth(0, 50);
resizeBehavior.setWidth(1, 140);
resizeBehavior.setWidth(2, "1*");
Enjoy.
Derrell
I want to sort the columns in my grid, just like the rows. I have made a simple sort function that is called from an actioncolumn handler:
sortColumns:function(record) { // The record after which's values the columns are ordered
var columns = this.columns;
Ext.Array.sort(columns,function(col1,col2) {
if(record.get(col1.dataIndex) > record.get(col2.dataIndex)) return 1;
if(record.get(col1.dataIndex) < record.get(col2.dataIndex)) return -1;
if(col1.dataIndex > col2.dataIndex) return 1;
if(col1.dataIndex < col2.dataIndex) return 1;
throw new Error("Comparing column with itself shouldn't happen.");
});
this.setColumns(columns);
});
The setColumns line now throws the error
Cannot add destroyed item 'gridcolumn-1595' to Container 'headercontainer-1598'
which is because the "old" columns are destroyed first, and then the "new" columns, which are the same and thus destroyed, are applied.
I only want to change the order, but I didn't find any function to do it. Do you know how to do it?
Drag-drop ordering of the columns works, so it is doable; but I don't find the source code where sencha did implement that drag-drop thingy. Do you know where to look for that code?
Reconfigure method needs two arguments
grid.reconfigure(store, columns)
Here is the fiddle that changes the columns programatically https://fiddle.sencha.com/#fiddle/17bk
I have found that columns are items of the grid's headerCt, so the following works well, and unlike the other answers, it does not create new column components, keeping the column state and everything:
var headerCt = normalGrid.headerCt,
columns = headerCt.items.getRange();
Ext.Array.sort(columns,function(col1,col2) {
if(record.get(col1.dataIndex) < record.get(col2.dataIndex)) return -1;
if(record.get(col1.dataIndex) > record.get(col2.dataIndex)) return 1;
if(col1.dataIndex < col2.dataIndex) return -1;
if(col1.dataIndex > col2.dataIndex) return 1;
return 0;
});
headerCt.suspendLayouts();
for(var i=0;i<columns.length;i++)
{
headerCt.moveAfter(columns[i],(columns[i-1] || null));
}
headerCt.resumeLayouts(true);
There is a reconfigure method which can be used to achieve reordering, e.g:
grid.reconfigure(columns);
Check the this.
I couldn't manage to do it without storing columns in a custom field and using reconfigure, maybe someone can suggest something better (reconfigure doesn't work well with just regular columns field it seems):
Ext.define('MyGrid', {
extend: 'Ext.grid.Panel',
//just renamed "columns"
myColumnConfigs: [
//all your column configs
]
});
//to rearrange inside controller, also need to call it on grid render
var grid = this.getView();
var columns = grid.myColumnConfigs;
//...do your sorting on columns array
grid.reconfigure(columns);
I have a grid within a form as in the attached image. When the the customer name is changed, then the grid store is loaded with records corresponding to the customer. I want the balance textfield to be populated with the sum of Amount due column.
The image is here.
Use like this:
store.load({
scope : this,
callback: function(records, operation, success) {
//here the store has been loaded so you can use what functions you like.
//This code sum numbers in certain column
sum = 0;
store.each(function (rec) { sum += rec.get('NameColumn'); });
}
});
Use the store summary function:
var sum = grid.getStore().sum('NameColumn');
sencha api: sum store